|  | #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 = { | 
|  | .owner		= THIS_MODULE, | 
|  | .name		= "tas", | 
|  | .flags		= I2C_DF_NOTIFY, | 
|  | .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); | 
|  | } |