brintos

brintos / linux-shallow public Read only

0
0
Text · 7.5 KiB · 847e3b6 Raw
327 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *   Generic i2c interface for ALSA4 *5 *   (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>6 *   Modified for the ALSA driver by Jaroslav Kysela <perex@perex.cz>7 */8 9#include <linux/init.h>10#include <linux/slab.h>11#include <linux/module.h>12#include <linux/string.h>13#include <linux/errno.h>14#include <sound/core.h>15#include <sound/i2c.h>16 17MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");18MODULE_DESCRIPTION("Generic i2c interface for ALSA");19MODULE_LICENSE("GPL");20 21static int snd_i2c_bit_sendbytes(struct snd_i2c_device *device,22				 unsigned char *bytes, int count);23static int snd_i2c_bit_readbytes(struct snd_i2c_device *device,24				 unsigned char *bytes, int count);25static int snd_i2c_bit_probeaddr(struct snd_i2c_bus *bus,26				 unsigned short addr);27 28static const struct snd_i2c_ops snd_i2c_bit_ops = {29	.sendbytes = snd_i2c_bit_sendbytes,30	.readbytes = snd_i2c_bit_readbytes,31	.probeaddr = snd_i2c_bit_probeaddr,32};33 34static int snd_i2c_bus_free(struct snd_i2c_bus *bus)35{36	struct snd_i2c_bus *slave;37	struct snd_i2c_device *device;38 39	if (snd_BUG_ON(!bus))40		return -EINVAL;41	while (!list_empty(&bus->devices)) {42		device = snd_i2c_device(bus->devices.next);43		snd_i2c_device_free(device);44	}45	if (bus->master)46		list_del(&bus->buses);47	else {48		while (!list_empty(&bus->buses)) {49			slave = snd_i2c_slave_bus(bus->buses.next);50			snd_device_free(bus->card, slave);51		}52	}53	if (bus->private_free)54		bus->private_free(bus);55	kfree(bus);56	return 0;57}58 59static int snd_i2c_bus_dev_free(struct snd_device *device)60{61	struct snd_i2c_bus *bus = device->device_data;62	return snd_i2c_bus_free(bus);63}64 65int snd_i2c_bus_create(struct snd_card *card, const char *name,66		       struct snd_i2c_bus *master, struct snd_i2c_bus **ri2c)67{68	struct snd_i2c_bus *bus;69	int err;70	static const struct snd_device_ops ops = {71		.dev_free =	snd_i2c_bus_dev_free,72	};73 74	*ri2c = NULL;75	bus = kzalloc(sizeof(*bus), GFP_KERNEL);76	if (bus == NULL)77		return -ENOMEM;78	mutex_init(&bus->lock_mutex);79	INIT_LIST_HEAD(&bus->devices);80	INIT_LIST_HEAD(&bus->buses);81	bus->card = card;82	bus->ops = &snd_i2c_bit_ops;83	if (master) {84		list_add_tail(&bus->buses, &master->buses);85		bus->master = master;86	}87	strscpy(bus->name, name, sizeof(bus->name));88	err = snd_device_new(card, SNDRV_DEV_BUS, bus, &ops);89	if (err < 0) {90		snd_i2c_bus_free(bus);91		return err;92	}93	*ri2c = bus;94	return 0;95}96 97EXPORT_SYMBOL(snd_i2c_bus_create);98 99int snd_i2c_device_create(struct snd_i2c_bus *bus, const char *name,100			  unsigned char addr, struct snd_i2c_device **rdevice)101{102	struct snd_i2c_device *device;103 104	*rdevice = NULL;105	if (snd_BUG_ON(!bus))106		return -EINVAL;107	device = kzalloc(sizeof(*device), GFP_KERNEL);108	if (device == NULL)109		return -ENOMEM;110	device->addr = addr;111	strscpy(device->name, name, sizeof(device->name));112	list_add_tail(&device->list, &bus->devices);113	device->bus = bus;114	*rdevice = device;115	return 0;116}117 118EXPORT_SYMBOL(snd_i2c_device_create);119 120int snd_i2c_device_free(struct snd_i2c_device *device)121{122	if (device->bus)123		list_del(&device->list);124	if (device->private_free)125		device->private_free(device);126	kfree(device);127	return 0;128}129 130EXPORT_SYMBOL(snd_i2c_device_free);131 132int snd_i2c_sendbytes(struct snd_i2c_device *device, unsigned char *bytes, int count)133{134	return device->bus->ops->sendbytes(device, bytes, count);135}136 137EXPORT_SYMBOL(snd_i2c_sendbytes);138 139int snd_i2c_readbytes(struct snd_i2c_device *device, unsigned char *bytes, int count)140{141	return device->bus->ops->readbytes(device, bytes, count);142}143 144EXPORT_SYMBOL(snd_i2c_readbytes);145 146int snd_i2c_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)147{148	return bus->ops->probeaddr(bus, addr);149}150 151EXPORT_SYMBOL(snd_i2c_probeaddr);152 153/*154 *  bit-operations155 */156 157static inline void snd_i2c_bit_hw_start(struct snd_i2c_bus *bus)158{159	if (bus->hw_ops.bit->start)160		bus->hw_ops.bit->start(bus);161}162 163static inline void snd_i2c_bit_hw_stop(struct snd_i2c_bus *bus)164{165	if (bus->hw_ops.bit->stop)166		bus->hw_ops.bit->stop(bus);167}168 169static void snd_i2c_bit_direction(struct snd_i2c_bus *bus, int clock, int data)170{171	if (bus->hw_ops.bit->direction)172		bus->hw_ops.bit->direction(bus, clock, data);173}174 175static void snd_i2c_bit_set(struct snd_i2c_bus *bus, int clock, int data)176{177	bus->hw_ops.bit->setlines(bus, clock, data);178}179 180#if 0181static int snd_i2c_bit_clock(struct snd_i2c_bus *bus)182{183	if (bus->hw_ops.bit->getclock)184		return bus->hw_ops.bit->getclock(bus);185	return -ENXIO;186}187#endif188 189static int snd_i2c_bit_data(struct snd_i2c_bus *bus, int ack)190{191	return bus->hw_ops.bit->getdata(bus, ack);192}193 194static void snd_i2c_bit_start(struct snd_i2c_bus *bus)195{196	snd_i2c_bit_hw_start(bus);197	snd_i2c_bit_direction(bus, 1, 1);	/* SCL - wr, SDA - wr */198	snd_i2c_bit_set(bus, 1, 1);199	snd_i2c_bit_set(bus, 1, 0);200	snd_i2c_bit_set(bus, 0, 0);201}202 203static void snd_i2c_bit_stop(struct snd_i2c_bus *bus)204{205	snd_i2c_bit_set(bus, 0, 0);206	snd_i2c_bit_set(bus, 1, 0);207	snd_i2c_bit_set(bus, 1, 1);208	snd_i2c_bit_hw_stop(bus);209}210 211static void snd_i2c_bit_send(struct snd_i2c_bus *bus, int data)212{213	snd_i2c_bit_set(bus, 0, data);214	snd_i2c_bit_set(bus, 1, data);215	snd_i2c_bit_set(bus, 0, data);216}217 218static int snd_i2c_bit_ack(struct snd_i2c_bus *bus)219{220	int ack;221 222	snd_i2c_bit_set(bus, 0, 1);223	snd_i2c_bit_set(bus, 1, 1);224	snd_i2c_bit_direction(bus, 1, 0);	/* SCL - wr, SDA - rd */225	ack = snd_i2c_bit_data(bus, 1);226	snd_i2c_bit_direction(bus, 1, 1);	/* SCL - wr, SDA - wr */227	snd_i2c_bit_set(bus, 0, 1);228	return ack ? -EIO : 0;229}230 231static int snd_i2c_bit_sendbyte(struct snd_i2c_bus *bus, unsigned char data)232{233	int i, err;234 235	for (i = 7; i >= 0; i--)236		snd_i2c_bit_send(bus, !!(data & (1 << i)));237	err = snd_i2c_bit_ack(bus);238	if (err < 0)239		return err;240	return 0;241}242 243static int snd_i2c_bit_readbyte(struct snd_i2c_bus *bus, int last)244{245	int i;246	unsigned char data = 0;247 248	snd_i2c_bit_set(bus, 0, 1);249	snd_i2c_bit_direction(bus, 1, 0);	/* SCL - wr, SDA - rd */250	for (i = 7; i >= 0; i--) {251		snd_i2c_bit_set(bus, 1, 1);252		if (snd_i2c_bit_data(bus, 0))253			data |= (1 << i);254		snd_i2c_bit_set(bus, 0, 1);255	}256	snd_i2c_bit_direction(bus, 1, 1);	/* SCL - wr, SDA - wr */257	snd_i2c_bit_send(bus, !!last);258	return data;259}260 261static int snd_i2c_bit_sendbytes(struct snd_i2c_device *device,262				 unsigned char *bytes, int count)263{264	struct snd_i2c_bus *bus = device->bus;265	int err, res = 0;266 267	if (device->flags & SND_I2C_DEVICE_ADDRTEN)268		return -EIO;		/* not yet implemented */269	snd_i2c_bit_start(bus);270	err = snd_i2c_bit_sendbyte(bus, device->addr << 1);271	if (err < 0) {272		snd_i2c_bit_hw_stop(bus);273		return err;274	}275	while (count-- > 0) {276		err = snd_i2c_bit_sendbyte(bus, *bytes++);277		if (err < 0) {278			snd_i2c_bit_hw_stop(bus);279			return err;280		}281		res++;282	}283	snd_i2c_bit_stop(bus);284	return res;285}286 287static int snd_i2c_bit_readbytes(struct snd_i2c_device *device,288				 unsigned char *bytes, int count)289{290	struct snd_i2c_bus *bus = device->bus;291	int err, res = 0;292 293	if (device->flags & SND_I2C_DEVICE_ADDRTEN)294		return -EIO;		/* not yet implemented */295	snd_i2c_bit_start(bus);296	err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1);297	if (err < 0) {298		snd_i2c_bit_hw_stop(bus);299		return err;300	}301	while (count-- > 0) {302		err = snd_i2c_bit_readbyte(bus, count == 0);303		if (err < 0) {304			snd_i2c_bit_hw_stop(bus);305			return err;306		}307		*bytes++ = (unsigned char)err;308		res++;309	}310	snd_i2c_bit_stop(bus);311	return res;312}313 314static int snd_i2c_bit_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)315{316	int err;317 318	if (addr & 0x8000)	/* 10-bit address */319		return -EIO;	/* not yet implemented */320	if (addr & 0x7f80)	/* invalid address */321		return -EINVAL;322	snd_i2c_bit_start(bus);323	err = snd_i2c_bit_sendbyte(bus, addr << 1);324	snd_i2c_bit_stop(bus);325	return err;326}327