| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | #include <linux/module.h> | 
 | 2 | #include <linux/slab.h> | 
 | 3 | #include <linux/proc_fs.h> | 
 | 4 | #include <linux/ioport.h> | 
 | 5 | #include <linux/sysctl.h> | 
 | 6 | #include <linux/types.h> | 
 | 7 | #include <linux/i2c.h> | 
 | 8 | #include <linux/init.h> | 
 | 9 | #include <linux/soundcard.h> | 
 | 10 | #include <asm/uaccess.h> | 
 | 11 | #include <asm/errno.h> | 
 | 12 | #include <asm/io.h> | 
 | 13 | #include <asm/prom.h> | 
 | 14 |  | 
 | 15 | #include "tas_common.h" | 
 | 16 |  | 
 | 17 | #define CALL0(proc)								\ | 
 | 18 | 	do {									\ | 
 | 19 | 		struct tas_data_t *self;					\ | 
 | 20 | 		if (!tas_client || driver_hooks == NULL)			\ | 
 | 21 | 			return -1;						\ | 
 | 22 | 		self = dev_get_drvdata(&tas_client->dev);			\ | 
 | 23 | 		if (driver_hooks->proc)						\ | 
 | 24 | 			return driver_hooks->proc(self);			\ | 
 | 25 | 		else								\ | 
 | 26 | 			return -EINVAL;						\ | 
 | 27 | 	} while (0) | 
 | 28 |  | 
 | 29 | #define CALL(proc,arg...)							\ | 
 | 30 | 	do {									\ | 
 | 31 | 		struct tas_data_t *self;					\ | 
 | 32 | 		if (!tas_client || driver_hooks == NULL)			\ | 
 | 33 | 			return -1;						\ | 
 | 34 | 		self = dev_get_drvdata(&tas_client->dev);			\ | 
 | 35 | 		if (driver_hooks->proc)						\ | 
 | 36 | 			return driver_hooks->proc(self, ## arg);		\ | 
 | 37 | 		else								\ | 
 | 38 | 			return -EINVAL;						\ | 
 | 39 | 	} while (0) | 
 | 40 |  | 
 | 41 |  | 
 | 42 | static u8 tas_i2c_address = 0x34; | 
 | 43 | static struct i2c_client *tas_client; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 |  | 
 | 45 | static int tas_attach_adapter(struct i2c_adapter *); | 
 | 46 | static int tas_detach_client(struct i2c_client *); | 
 | 47 |  | 
 | 48 | struct i2c_driver tas_driver = { | 
| Laurent Riffard | d74cdab | 2005-11-26 20:46:32 +0100 | [diff] [blame] | 49 | 	.driver = { | 
| Laurent Riffard | d74cdab | 2005-11-26 20:46:32 +0100 | [diff] [blame] | 50 | 		.name	= "tas", | 
 | 51 | 	}, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 52 | 	.attach_adapter	= tas_attach_adapter, | 
 | 53 | 	.detach_client	= tas_detach_client, | 
 | 54 | }; | 
 | 55 |  | 
 | 56 | struct tas_driver_hooks_t *driver_hooks; | 
 | 57 |  | 
 | 58 | int | 
 | 59 | tas_register_driver(struct tas_driver_hooks_t *hooks) | 
 | 60 | { | 
 | 61 | 	driver_hooks = hooks; | 
 | 62 | 	return 0; | 
 | 63 | } | 
 | 64 |  | 
 | 65 | int | 
 | 66 | tas_get_mixer_level(int mixer, uint *level) | 
 | 67 | { | 
 | 68 | 	CALL(get_mixer_level,mixer,level); | 
 | 69 | } | 
 | 70 |  | 
 | 71 | int | 
 | 72 | tas_set_mixer_level(int mixer,uint level) | 
 | 73 | { | 
 | 74 | 	CALL(set_mixer_level,mixer,level); | 
 | 75 | } | 
 | 76 |  | 
 | 77 | int | 
 | 78 | tas_enter_sleep(void) | 
 | 79 | { | 
 | 80 | 	CALL0(enter_sleep); | 
 | 81 | } | 
 | 82 |  | 
 | 83 | int | 
 | 84 | tas_leave_sleep(void) | 
 | 85 | { | 
 | 86 | 	CALL0(leave_sleep); | 
 | 87 | } | 
 | 88 |  | 
 | 89 | int | 
 | 90 | tas_supported_mixers(void) | 
 | 91 | { | 
 | 92 | 	CALL0(supported_mixers); | 
 | 93 | } | 
 | 94 |  | 
 | 95 | int | 
 | 96 | tas_mixer_is_stereo(int mixer) | 
 | 97 | { | 
 | 98 | 	CALL(mixer_is_stereo,mixer); | 
 | 99 | } | 
 | 100 |  | 
 | 101 | int | 
 | 102 | tas_stereo_mixers(void) | 
 | 103 | { | 
 | 104 | 	CALL0(stereo_mixers); | 
 | 105 | } | 
 | 106 |  | 
 | 107 | int | 
 | 108 | tas_output_device_change(int device_id,int layout_id,int speaker_id) | 
 | 109 | { | 
 | 110 | 	CALL(output_device_change,device_id,layout_id,speaker_id); | 
 | 111 | } | 
 | 112 |  | 
 | 113 | int | 
 | 114 | tas_device_ioctl(u_int cmd, u_long arg) | 
 | 115 | { | 
 | 116 | 	CALL(device_ioctl,cmd,arg); | 
 | 117 | } | 
 | 118 |  | 
 | 119 | int | 
 | 120 | tas_post_init(void) | 
 | 121 | { | 
 | 122 | 	CALL0(post_init); | 
 | 123 | } | 
 | 124 |  | 
 | 125 | static int | 
 | 126 | tas_detect_client(struct i2c_adapter *adapter, int address) | 
 | 127 | { | 
 | 128 | 	static const char *client_name = "tas Digital Equalizer"; | 
 | 129 | 	struct i2c_client *new_client; | 
 | 130 | 	int rc = -ENODEV; | 
 | 131 |  | 
 | 132 | 	if (!driver_hooks) { | 
 | 133 | 		printk(KERN_ERR "tas_detect_client called with no hooks !\n"); | 
 | 134 | 		return -ENODEV; | 
 | 135 | 	} | 
 | 136 | 	 | 
| Robert P. J. Day | 3159f06 | 2007-02-14 00:33:16 -0800 | [diff] [blame] | 137 | 	new_client = kzalloc(sizeof(*new_client), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 138 | 	if (!new_client) | 
 | 139 | 		return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 140 |  | 
 | 141 | 	new_client->addr = address; | 
 | 142 | 	new_client->adapter = adapter; | 
 | 143 | 	new_client->driver = &tas_driver; | 
 | 144 | 	strlcpy(new_client->name, client_name, DEVICE_NAME_SIZE); | 
 | 145 |  | 
 | 146 |         if (driver_hooks->init(new_client)) | 
 | 147 | 		goto bail; | 
 | 148 |  | 
 | 149 | 	/* Tell the i2c layer a new client has arrived */ | 
 | 150 | 	if (i2c_attach_client(new_client)) { | 
 | 151 | 		driver_hooks->uninit(dev_get_drvdata(&new_client->dev)); | 
 | 152 | 		goto bail; | 
 | 153 | 	} | 
 | 154 |  | 
 | 155 | 	tas_client = new_client; | 
 | 156 | 	return 0; | 
 | 157 |  bail: | 
 | 158 | 	tas_client = NULL; | 
 | 159 | 	kfree(new_client); | 
 | 160 | 	return rc; | 
 | 161 | } | 
 | 162 |  | 
 | 163 | static int | 
 | 164 | tas_attach_adapter(struct i2c_adapter *adapter) | 
 | 165 | { | 
 | 166 | 	if (!strncmp(adapter->name, "mac-io", 6)) | 
 | 167 | 		return tas_detect_client(adapter, tas_i2c_address); | 
 | 168 | 	return 0; | 
 | 169 | } | 
 | 170 |  | 
 | 171 | static int | 
 | 172 | tas_detach_client(struct i2c_client *client) | 
 | 173 | { | 
 | 174 | 	if (client == tas_client) { | 
 | 175 | 		driver_hooks->uninit(dev_get_drvdata(&client->dev)); | 
 | 176 |  | 
 | 177 | 		i2c_detach_client(client); | 
 | 178 | 		kfree(client); | 
 | 179 | 	} | 
 | 180 | 	return 0; | 
 | 181 | } | 
 | 182 |  | 
 | 183 | void | 
 | 184 | tas_cleanup(void) | 
 | 185 | { | 
 | 186 | 	i2c_del_driver(&tas_driver); | 
 | 187 | } | 
 | 188 |  | 
 | 189 | int __init | 
 | 190 | tas_init(int driver_id, const char *driver_name) | 
 | 191 | { | 
| Stephen Rothwell | a7edd0e | 2007-04-03 10:52:17 +1000 | [diff] [blame] | 192 | 	const u32* paddr; | 
| Stephen Rothwell | 30686ba | 2007-04-24 13:53:04 +1000 | [diff] [blame] | 193 | 	struct device_node *tas_node; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 194 |  | 
 | 195 | 	printk(KERN_INFO "tas driver [%s])\n", driver_name); | 
 | 196 |  | 
| Benjamin Herrenschmidt | 4d6c588 | 2006-04-21 15:04:22 +1000 | [diff] [blame] | 197 | #ifndef CONFIG_I2C_POWERMAC | 
 | 198 | 	request_module("i2c-powermac"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 199 | #endif | 
| Stephen Rothwell | 30686ba | 2007-04-24 13:53:04 +1000 | [diff] [blame] | 200 | 	tas_node = of_find_node_by_name("deq"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 201 | 	if (tas_node == NULL) | 
 | 202 | 		return -ENODEV; | 
| Stephen Rothwell | c4f55b3 | 2007-04-03 22:39:14 +1000 | [diff] [blame] | 203 | 	paddr = of_get_property(tas_node, "i2c-address", NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 204 | 	if (paddr) { | 
 | 205 | 		tas_i2c_address = (*paddr) >> 1; | 
 | 206 | 		printk(KERN_INFO "using i2c address: 0x%x from device-tree\n", | 
 | 207 | 				tas_i2c_address); | 
 | 208 | 	} else     | 
 | 209 | 		printk(KERN_INFO "using i2c address: 0x%x (default)\n", | 
 | 210 | 				tas_i2c_address); | 
| Stephen Rothwell | 30686ba | 2007-04-24 13:53:04 +1000 | [diff] [blame] | 211 | 	of_node_put(tas_node); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 212 |  | 
 | 213 | 	return i2c_add_driver(&tas_driver); | 
 | 214 | } |