| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/proc_fs.h>
 | 
					
						
							|  |  |  | #include <linux/ioport.h>
 | 
					
						
							|  |  |  | #include <linux/sysctl.h>
 | 
					
						
							|  |  |  | #include <linux/types.h>
 | 
					
						
							|  |  |  | #include <linux/i2c.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/soundcard.h>
 | 
					
						
							|  |  |  | #include <asm/uaccess.h>
 | 
					
						
							|  |  |  | #include <asm/errno.h>
 | 
					
						
							|  |  |  | #include <asm/io.h>
 | 
					
						
							|  |  |  | #include <asm/prom.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "tas_common.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CALL0(proc)								\
 | 
					
						
							|  |  |  | 	do {									\ | 
					
						
							|  |  |  | 		struct tas_data_t *self;					\ | 
					
						
							|  |  |  | 		if (!tas_client || driver_hooks == NULL)			\ | 
					
						
							|  |  |  | 			return -1;						\ | 
					
						
							|  |  |  | 		self = dev_get_drvdata(&tas_client->dev);			\ | 
					
						
							|  |  |  | 		if (driver_hooks->proc)						\ | 
					
						
							|  |  |  | 			return driver_hooks->proc(self);			\ | 
					
						
							|  |  |  | 		else								\ | 
					
						
							|  |  |  | 			return -EINVAL;						\ | 
					
						
							|  |  |  | 	} while (0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CALL(proc,arg...)							\
 | 
					
						
							|  |  |  | 	do {									\ | 
					
						
							|  |  |  | 		struct tas_data_t *self;					\ | 
					
						
							|  |  |  | 		if (!tas_client || driver_hooks == NULL)			\ | 
					
						
							|  |  |  | 			return -1;						\ | 
					
						
							|  |  |  | 		self = dev_get_drvdata(&tas_client->dev);			\ | 
					
						
							|  |  |  | 		if (driver_hooks->proc)						\ | 
					
						
							|  |  |  | 			return driver_hooks->proc(self, ## arg);		\ | 
					
						
							|  |  |  | 		else								\ | 
					
						
							|  |  |  | 			return -EINVAL;						\ | 
					
						
							|  |  |  | 	} while (0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u8 tas_i2c_address = 0x34; | 
					
						
							|  |  |  | static struct i2c_client *tas_client; | 
					
						
							|  |  |  | static struct device_node* tas_node; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int tas_attach_adapter(struct i2c_adapter *); | 
					
						
							|  |  |  | static int tas_detach_client(struct i2c_client *); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct i2c_driver tas_driver = { | 
					
						
							| 
									
										
										
										
											2005-11-26 20:46:32 +01:00
										 |  |  | 	.driver = { | 
					
						
							|  |  |  | 		.name	= "tas", | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	.attach_adapter	= tas_attach_adapter, | 
					
						
							|  |  |  | 	.detach_client	= tas_detach_client, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct tas_driver_hooks_t *driver_hooks; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_register_driver(struct tas_driver_hooks_t *hooks) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	driver_hooks = hooks; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_get_mixer_level(int mixer, uint *level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CALL(get_mixer_level,mixer,level); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_set_mixer_level(int mixer,uint level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CALL(set_mixer_level,mixer,level); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_enter_sleep(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CALL0(enter_sleep); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_leave_sleep(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CALL0(leave_sleep); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_supported_mixers(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CALL0(supported_mixers); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_mixer_is_stereo(int mixer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CALL(mixer_is_stereo,mixer); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_stereo_mixers(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CALL0(stereo_mixers); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_output_device_change(int device_id,int layout_id,int speaker_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CALL(output_device_change,device_id,layout_id,speaker_id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_device_ioctl(u_int cmd, u_long arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CALL(device_ioctl,cmd,arg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | tas_post_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CALL0(post_init); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | tas_detect_client(struct i2c_adapter *adapter, int address) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	static const char *client_name = "tas Digital Equalizer"; | 
					
						
							|  |  |  | 	struct i2c_client *new_client; | 
					
						
							|  |  |  | 	int rc = -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!driver_hooks) { | 
					
						
							|  |  |  | 		printk(KERN_ERR "tas_detect_client called with no hooks !\n"); | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	new_client = kmalloc(sizeof(*new_client), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!new_client) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	memset(new_client, 0, sizeof(*new_client)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	new_client->addr = address; | 
					
						
							|  |  |  | 	new_client->adapter = adapter; | 
					
						
							|  |  |  | 	new_client->driver = &tas_driver; | 
					
						
							|  |  |  | 	strlcpy(new_client->name, client_name, DEVICE_NAME_SIZE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (driver_hooks->init(new_client)) | 
					
						
							|  |  |  | 		goto bail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Tell the i2c layer a new client has arrived */ | 
					
						
							|  |  |  | 	if (i2c_attach_client(new_client)) { | 
					
						
							|  |  |  | 		driver_hooks->uninit(dev_get_drvdata(&new_client->dev)); | 
					
						
							|  |  |  | 		goto bail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tas_client = new_client; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  |  bail: | 
					
						
							|  |  |  | 	tas_client = NULL; | 
					
						
							|  |  |  | 	kfree(new_client); | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | tas_attach_adapter(struct i2c_adapter *adapter) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!strncmp(adapter->name, "mac-io", 6)) | 
					
						
							|  |  |  | 		return tas_detect_client(adapter, tas_i2c_address); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | tas_detach_client(struct i2c_client *client) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (client == tas_client) { | 
					
						
							|  |  |  | 		driver_hooks->uninit(dev_get_drvdata(&client->dev)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		i2c_detach_client(client); | 
					
						
							|  |  |  | 		kfree(client); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | tas_cleanup(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	i2c_del_driver(&tas_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int __init | 
					
						
							|  |  |  | tas_init(int driver_id, const char *driver_name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32* paddr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	printk(KERN_INFO "tas driver [%s])\n", driver_name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef CONFIG_I2C_KEYWEST
 | 
					
						
							|  |  |  | 	request_module("i2c-keywest"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	tas_node = find_devices("deq"); | 
					
						
							|  |  |  | 	if (tas_node == NULL) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 	paddr = (u32 *)get_property(tas_node, "i2c-address", NULL); | 
					
						
							|  |  |  | 	if (paddr) { | 
					
						
							|  |  |  | 		tas_i2c_address = (*paddr) >> 1; | 
					
						
							|  |  |  | 		printk(KERN_INFO "using i2c address: 0x%x from device-tree\n", | 
					
						
							|  |  |  | 				tas_i2c_address); | 
					
						
							|  |  |  | 	} else     | 
					
						
							|  |  |  | 		printk(KERN_INFO "using i2c address: 0x%x (default)\n", | 
					
						
							|  |  |  | 				tas_i2c_address); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return i2c_add_driver(&tas_driver); | 
					
						
							|  |  |  | } |