| #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 int tas_attach_adapter(struct i2c_adapter *); |
| static int tas_detach_client(struct i2c_client *); |
| |
| struct i2c_driver tas_driver = { |
| .driver = { |
| .name = "tas", |
| }, |
| .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 = kzalloc(sizeof(*new_client), GFP_KERNEL); |
| if (!new_client) |
| return -ENOMEM; |
| |
| 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) |
| { |
| const u32* paddr; |
| struct device_node *tas_node; |
| |
| printk(KERN_INFO "tas driver [%s])\n", driver_name); |
| |
| #ifndef CONFIG_I2C_POWERMAC |
| request_module("i2c-powermac"); |
| #endif |
| tas_node = of_find_node_by_name("deq"); |
| if (tas_node == NULL) |
| return -ENODEV; |
| paddr = of_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); |
| of_node_put(tas_node); |
| |
| return i2c_add_driver(&tas_driver); |
| } |