/* * Driver for the i2c/i2s based TA3001C sound chip used * on some Apple hardware. Also known as "tumbler". * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. * * Modified by Christopher C. Chimelis : * * TODO: * ----- * * Enable DRC since the TiBook speakers are less than good * * Enable control over input line 2 (is this connected?) * * Play with the dual six-stage cascading biquad filtering to see how * we can use it to our advantage (currently not implemented) * * Reorganise driver a bit to make it cleaner and easier to work with * (read: use the header file more :-P) * * Implement sleep support * * Version 0.4: * ------------ * * Balance control finally works (can someone document OSS better please?) * * Moved to a struct for common values referenced in the driver * * Put stubs in for sleep/wake-up support for now. This will take some * experimentation to make sure that the timing is right, since the * TAS hardware requires specific timing while enabling low-power mode. * I may cheat for now and just reset the chip on wake-up, but I'd rather * not if I don't have to. * * Version 0.3: * ------------ * * Fixed volume control * * Added bass and treble control * * Added PCM line level control (mixer 1 in the TAS manual) * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dmasound.h" #include "tas3001c.h" #define I2C_DRIVERID_TAS (0xFEBA) #define TAS_VERSION "0.3" #define TAS_DATE "20011214" #define TAS_SETTING_MAX 100 #define VOL_DEFAULT (((((TAS_SETTING_MAX*4)/5)<<0)<<8) | (((TAS_SETTING_MAX*4)/5)<<0)) #define INPUT_DEFAULT (((TAS_SETTING_MAX*4)/5)<<0) #define BASS_DEFAULT ((TAS_SETTING_MAX/2)<<0) #define TREBLE_DEFAULT ((TAS_SETTING_MAX/2)<<0) static struct i2c_client * tumbler_client = NULL; int tumbler_enter_sleep(void); int tumbler_leave_sleep(void); static int tas_attach_adapter(struct i2c_adapter *adapter); static int tas_detect_client(struct i2c_adapter *adapter, int address); static int tas_detach_client(struct i2c_client *client); /* Unique ID allocation */ static int tas_id; static int tas_initialized; static struct device_node* tas_node; static u8 tas_i2c_address = 0x34; struct tas_data_t { uint left_vol; /* left volume */ uint right_vol; /* right volume */ uint treble; /* treble */ uint bass; /* bass */ uint pcm_level; /* pcm level */ }; struct i2c_driver tas_driver = { name: "TAS3001C driver V 0.3", id: I2C_DRIVERID_TAS, flags: I2C_DF_NOTIFY, attach_adapter: &tas_attach_adapter, detach_client: &tas_detach_client, command: NULL, inc_use: NULL, /* &tas_inc_use, */ dec_use: NULL /* &tas_dev_use */ }; int tumbler_get_volume(uint * left_vol, uint *right_vol) { struct tas_data_t *data; if (!tumbler_client) return -1; data = (struct tas_data_t *) (tumbler_client->data); *left_vol = data->left_vol; *right_vol = data->right_vol; return 0; } int tumbler_set_register(uint reg, uint size, char *block) { if (i2c_smbus_write_block_data(tumbler_client, reg, size, block) < 0) { printk("tas3001c: I2C write failed \n"); return -1; } return 0; } int tumbler_get_pcm_lvl(uint *pcm_lvl) { struct tas_data_t *data; if (!tumbler_client) return -1; data = (struct tas_data_t *) (tumbler_client->data); *pcm_lvl = data->pcm_level; return 0; } int tumbler_get_treble(uint *treble) { struct tas_data_t *data; if (!tumbler_client) return -1; data = (struct tas_data_t *) (tumbler_client->data); *treble = data->treble; return 0; } int tumbler_get_bass(uint *bass) { struct tas_data_t *data; if (!tumbler_client) return -1; data = (struct tas_data_t *) (tumbler_client->data); *bass = data->bass; return 0; } int tumbler_set_bass(uint bass) { uint cur_bass_pers = bass; char block; struct tas_data_t *data; if (!tumbler_client) return -1; data = (struct tas_data_t *) (tumbler_client->data); bass &= 0xff; if (bass > TAS_SETTING_MAX) bass = TAS_SETTING_MAX; bass = ((bass * 72) / TAS_SETTING_MAX) << 0; bass = tas_bass_table[bass]; block = (bass >> 0) & 0xff; if (tumbler_set_register(TAS_SET_BASS, &block) < 0) { printk("tas3001c: failed to set bass \n"); return -1; } data->bass = cur_bass_pers; return 0; } int tumbler_set_treble(uint treble) { uint cur_treble_pers = treble; char block; struct tas_data_t *data; if (!tumbler_client) return -1; data = (struct tas_data_t *) (tumbler_client->data); treble &= 0xff; if (treble > TAS_SETTING_MAX) treble = TAS_SETTING_MAX; treble = ((treble * 72) / TAS_SETTING_MAX) << 0; treble = tas_treble_table[treble]; block = (treble >> 0) & 0xff; if (tumbler_set_register(TAS_SET_TREBLE, &block) < 0) { printk("tas3001c: failed to set treble \n"); return -1; } data->treble = cur_treble_pers; return 0; } int tumbler_set_pcm_lvl(uint pcm_lvl) { uint pcm_lvl_pers = pcm_lvl; unsigned char block[3]; struct tas_data_t *data; if (!tumbler_client) return -1; data = (struct tas_data_t *) (tumbler_client->data); pcm_lvl &= 0xff; if (pcm_lvl > TAS_SETTING_MAX) pcm_lvl = TAS_SETTING_MAX; pcm_lvl = ((pcm_lvl * 176) / TAS_SETTING_MAX) << 0; pcm_lvl = tas_input_table[pcm_lvl]; block[0] = (pcm_lvl >> 16) & 0xff; block[1] = (pcm_lvl >> 8) & 0xff; block[2] = (pcm_lvl >> 0) & 0xff; if (tumbler_set_register(TAS_SET_MIXER1, block) < 0) { printk("tas3001c: failed to set input level \n"); return -1; } data->pcm_level = pcm_lvl_pers; return 0; } int tumbler_set_volume(uint left_vol, uint right_vol) { uint left_vol_pers = left_vol; uint right_vol_pers = right_vol; unsigned char block[6]; struct tas_data_t *data; if (!tumbler_client) return -1; data = (struct tas_data_t *) (tumbler_client->data); left_vol &= 0xff; if (left_vol > TAS_SETTING_MAX) left_vol = TAS_SETTING_MAX; right_vol = (right_vol >> 8) & 0xff; if (right_vol > TAS_SETTING_MAX) right_vol = TAS_SETTING_MAX; left_vol = ((left_vol * 176) / TAS_SETTING_MAX) << 0; right_vol = ((right_vol * 176) / TAS_SETTING_MAX) << 0; left_vol = tas_volume_table[left_vol]; right_vol = tas_volume_table[right_vol]; block[0] = (left_vol >> 16) & 0xff; block[1] = (left_vol >> 8) & 0xff; block[2] = (left_vol >> 0) & 0xff; block[3] = (right_vol >> 16) & 0xff; block[4] = (right_vol >> 8) & 0xff; block[5] = (right_vol >> 0) & 0xff; if (tumbler_set_register(TAS_SET_VOLUME, block) < 0) { printk("tas3001c: failed to set volume \n"); return -1; } data->left_vol = left_vol_pers; data->right_vol = right_vol_pers; return 0; } int tumbler_leave_sleep(void) { /* Stub for now, but I have the details on low-power mode */ if (!tumbler_client) return -1; return 0; } int tumbler_enter_sleep(void) { /* Stub for now, but I have the details on low-power mode */ if (!tumbler_client) return -1; return 0; } static int tas_attach_adapter(struct i2c_adapter *adapter) { if (!strncmp(adapter->name, "mac-io", 6)) tas_detect_client(adapter, tas_i2c_address); return 0; } static int tas_init_client(struct i2c_client * new_client) { /* Make sure something answers on the i2c bus */ if (i2c_smbus_write_byte_data(new_client, 1, (1<<6)+(2<<4)+(2<<2)+0) < 0) return -1; tumbler_client = new_client; tumbler_set_volume(VOL_DEFAULT, VOL_DEFAULT); tumbler_set_pcm_lvl(INPUT_DEFAULT); tumbler_set_bass(BASS_DEFAULT); tumbler_set_treble(TREBLE_DEFAULT); return 0; } static int tas_detect_client(struct i2c_adapter *adapter, int address) { int rc = 0; struct i2c_client *new_client; struct tas_data_t *data; const char *client_name = "tas 3001c Digital Equalizer"; new_client = kmalloc( sizeof(struct i2c_client) + sizeof(struct tas_data_t), GFP_KERNEL); if (!new_client) { rc = -ENOMEM; goto bail; } /* This is tricky, but it will set the data to the right value. */ new_client->data = new_client + 1; data = (struct tas_data_t *) (new_client->data); new_client->addr = address; new_client->data = data; new_client->adapter = adapter; new_client->driver = &tas_driver; new_client->flags = 0; strcpy(new_client->name,client_name); new_client->id = tas_id++; /* Automatically unique */ if (tas_init_client(new_client)) { rc = -ENODEV; goto bail; } /* Tell the i2c layer a new client has arrived */ if (i2c_attach_client(new_client)) { rc = -ENODEV; goto bail; } bail: if (rc && new_client) kfree(new_client); return rc; } static int tas_detach_client(struct i2c_client *client) { if (client == tumbler_client) tumbler_client = NULL; i2c_detach_client(client); kfree(client); return 0; } int tas_cleanup(void) { if (!tas_initialized) return -ENODEV; i2c_del_driver(&tas_driver); tas_initialized = 0; return 0; } int tas_init(void) { int rc; u32* paddr; if (tas_initialized) return 0; tas_node = find_devices("deq"); if (tas_node == NULL) return -ENODEV; printk(KERN_INFO "tas3001c driver version %s (%s)\n",TAS_VERSION,TAS_DATE); 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); if ((rc = i2c_add_driver(&tas_driver))) { printk("tas3001c: Driver registration failed, module not inserted.\n"); tas_cleanup(); return rc; } tas_initialized = 1; return 0; }