diff options
Diffstat (limited to 'sound/ppc/tumbler.c')
| -rw-r--r-- | sound/ppc/tumbler.c | 492 |
1 files changed, 289 insertions, 203 deletions
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index b94437c024b..b9ffc17a479 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -24,14 +24,14 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/i2c.h> -#include <linux/i2c-dev.h> #include <linux/kmod.h> #include <linux/slab.h> #include <linux/interrupt.h> +#include <linux/string.h> +#include <linux/of_irq.h> #include <sound/core.h> #include <asm/io.h> #include <asm/irq.h> @@ -43,11 +43,13 @@ #undef DEBUG #ifdef DEBUG -#define DBG(fmt...) printk(fmt) +#define DBG(fmt...) printk(KERN_DEBUG fmt) #else #define DBG(fmt...) #endif +#define IS_G4DA (of_machine_is_compatible("PowerMac3,4")) + /* i2c address for tumbler */ #define TAS_I2C_ADDR 0x34 @@ -82,21 +84,21 @@ enum { VOL_IDX_LAST_MIX }; -typedef struct pmac_gpio { +struct pmac_gpio { unsigned int addr; u8 active_val; u8 inactive_val; u8 active_state; -} pmac_gpio_t; - -typedef struct pmac_tumbler_t { - pmac_keywest_t i2c; - pmac_gpio_t audio_reset; - pmac_gpio_t amp_mute; - pmac_gpio_t line_mute; - pmac_gpio_t line_detect; - pmac_gpio_t hp_mute; - pmac_gpio_t hp_detect; +}; + +struct pmac_tumbler { + struct pmac_keywest i2c; + struct pmac_gpio audio_reset; + struct pmac_gpio amp_mute; + struct pmac_gpio line_mute; + struct pmac_gpio line_detect; + struct pmac_gpio hp_mute; + struct pmac_gpio hp_detect; int headphone_irq; int lineout_irq; unsigned int save_master_vol[2]; @@ -112,13 +114,13 @@ typedef struct pmac_tumbler_t { int auto_mute_notify; int reset_on_sleep; u8 acs; -} pmac_tumbler_t; +}; /* */ -static int send_init_client(pmac_keywest_t *i2c, unsigned int *regs) +static int send_init_client(struct pmac_keywest *i2c, unsigned int *regs) { while (*regs > 0) { int err, count = 10; @@ -138,7 +140,7 @@ static int send_init_client(pmac_keywest_t *i2c, unsigned int *regs) } -static int tumbler_init_client(pmac_keywest_t *i2c) +static int tumbler_init_client(struct pmac_keywest *i2c) { static unsigned int regs[] = { /* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */ @@ -149,7 +151,7 @@ static int tumbler_init_client(pmac_keywest_t *i2c) return send_init_client(i2c, regs); } -static int snapper_init_client(pmac_keywest_t *i2c) +static int snapper_init_client(struct pmac_keywest *i2c) { static unsigned int regs[] = { /* normal operation, SCLK=64fps, i2s output, 16bit width */ @@ -173,7 +175,7 @@ static int snapper_init_client(pmac_keywest_t *i2c) pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, (gp)->addr, 0) #define tumbler_gpio_free(gp) /* NOP */ -static void write_audio_gpio(pmac_gpio_t *gp, int active) +static void write_audio_gpio(struct pmac_gpio *gp, int active) { if (! gp->addr) return; @@ -182,7 +184,7 @@ static void write_audio_gpio(pmac_gpio_t *gp, int active) DBG("(I) gpio %x write %d\n", gp->addr, active); } -static int check_audio_gpio(pmac_gpio_t *gp) +static int check_audio_gpio(struct pmac_gpio *gp) { int ret; @@ -191,22 +193,23 @@ static int check_audio_gpio(pmac_gpio_t *gp) ret = do_gpio_read(gp); - return (ret & 0xd) == (gp->active_val & 0xd); + return (ret & 0x1) == (gp->active_val & 0x1); } -static int read_audio_gpio(pmac_gpio_t *gp) +static int read_audio_gpio(struct pmac_gpio *gp) { int ret; if (! gp->addr) return 0; - ret = ((do_gpio_read(gp) & 0x02) !=0); + ret = do_gpio_read(gp); + ret = (ret & 0x02) !=0; return ret == gp->active_state; } /* * update master volume */ -static int tumbler_set_master_volume(pmac_tumbler_t *mix) +static int tumbler_set_master_volume(struct pmac_tumbler *mix) { unsigned char block[6]; unsigned int left_vol, right_vol; @@ -239,17 +242,19 @@ static int tumbler_set_master_volume(pmac_tumbler_t *mix) block[4] = (right_vol >> 8) & 0xff; block[5] = (right_vol >> 0) & 0xff; - if (i2c_smbus_write_block_data(mix->i2c.client, TAS_REG_VOL, - 6, block) < 0) { - snd_printk("failed to set volume \n"); + if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_VOL, 6, + block) < 0) { + snd_printk(KERN_ERR "failed to set volume \n"); return -EINVAL; } + DBG("(I) succeeded to set volume (%u, %u)\n", left_vol, right_vol); return 0; } /* output volume */ -static int tumbler_info_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int tumbler_info_master_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; @@ -258,51 +263,59 @@ static int tumbler_info_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_inf return 0; } -static int tumbler_get_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_get_master_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix = chip->mixer_data; - snd_assert(mix, return -ENODEV); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix = chip->mixer_data; + ucontrol->value.integer.value[0] = mix->master_vol[0]; ucontrol->value.integer.value[1] = mix->master_vol[1]; return 0; } -static int tumbler_put_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_put_master_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix = chip->mixer_data; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix = chip->mixer_data; + unsigned int vol[2]; int change; - snd_assert(mix, return -ENODEV); - change = mix->master_vol[0] != ucontrol->value.integer.value[0] || - mix->master_vol[1] != ucontrol->value.integer.value[1]; + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] >= ARRAY_SIZE(master_volume_table) || + vol[1] >= ARRAY_SIZE(master_volume_table)) + return -EINVAL; + change = mix->master_vol[0] != vol[0] || + mix->master_vol[1] != vol[1]; if (change) { - mix->master_vol[0] = ucontrol->value.integer.value[0]; - mix->master_vol[1] = ucontrol->value.integer.value[1]; + mix->master_vol[0] = vol[0]; + mix->master_vol[1] = vol[1]; tumbler_set_master_volume(mix); } return change; } /* output switch */ -static int tumbler_get_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_get_master_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix = chip->mixer_data; - snd_assert(mix, return -ENODEV); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix = chip->mixer_data; + ucontrol->value.integer.value[0] = mix->master_switch[0]; ucontrol->value.integer.value[1] = mix->master_switch[1]; return 0; } -static int tumbler_put_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_put_master_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix = chip->mixer_data; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix = chip->mixer_data; int change; - snd_assert(mix, return -ENODEV); change = mix->master_switch[0] != ucontrol->value.integer.value[0] || mix->master_switch[1] != ucontrol->value.integer.value[1]; if (change) { @@ -320,7 +333,7 @@ static int tumbler_put_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_valu #define TAS3001_DRC_MAX 0x5f -static int tumbler_set_drc(pmac_tumbler_t *mix) +static int tumbler_set_drc(struct pmac_tumbler *mix) { unsigned char val[2]; @@ -340,11 +353,12 @@ static int tumbler_set_drc(pmac_tumbler_t *mix) val[1] = 0; } - if (i2c_smbus_write_block_data(mix->i2c.client, TAS_REG_DRC, - 2, val) < 0) { - snd_printk("failed to set DRC\n"); + if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_DRC, + 2, val) < 0) { + snd_printk(KERN_ERR "failed to set DRC\n"); return -EINVAL; } + DBG("(I) succeeded to set DRC (%u, %u)\n", val[0], val[1]); return 0; } @@ -354,7 +368,7 @@ static int tumbler_set_drc(pmac_tumbler_t *mix) #define TAS3004_DRC_MAX 0xef -static int snapper_set_drc(pmac_tumbler_t *mix) +static int snapper_set_drc(struct pmac_tumbler *mix) { unsigned char val[6]; @@ -376,17 +390,19 @@ static int snapper_set_drc(pmac_tumbler_t *mix) val[4] = 0x60; val[5] = 0xa0; - if (i2c_smbus_write_block_data(mix->i2c.client, TAS_REG_DRC, - 6, val) < 0) { - snd_printk("failed to set DRC\n"); + if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_DRC, + 6, val) < 0) { + snd_printk(KERN_ERR "failed to set DRC\n"); return -EINVAL; } + DBG("(I) succeeded to set DRC (%u, %u)\n", val[0], val[1]); return 0; } -static int tumbler_info_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int tumbler_info_drc_value(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; @@ -395,27 +411,38 @@ static int tumbler_info_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t return 0; } -static int tumbler_get_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_get_drc_value(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix; if (! (mix = chip->mixer_data)) return -ENODEV; ucontrol->value.integer.value[0] = mix->drc_range; return 0; } -static int tumbler_put_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_put_drc_value(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix; + unsigned int val; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->drc_range != ucontrol->value.integer.value[0]; + val = ucontrol->value.integer.value[0]; + if (chip->model == PMAC_TUMBLER) { + if (val > TAS3001_DRC_MAX) + return -EINVAL; + } else { + if (val > TAS3004_DRC_MAX) + return -EINVAL; + } + change = mix->drc_range != val; if (change) { - mix->drc_range = ucontrol->value.integer.value[0]; + mix->drc_range = val; if (chip->model == PMAC_TUMBLER) tumbler_set_drc(mix); else @@ -424,20 +451,22 @@ static int tumbler_put_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t return change; } -static int tumbler_get_drc_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_get_drc_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix; if (! (mix = chip->mixer_data)) return -ENODEV; ucontrol->value.integer.value[0] = mix->drc_enable; return 0; } -static int tumbler_put_drc_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_put_drc_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix; int change; if (! (mix = chip->mixer_data)) @@ -466,7 +495,8 @@ struct tumbler_mono_vol { unsigned int *table; }; -static int tumbler_set_mono_volume(pmac_tumbler_t *mix, struct tumbler_mono_vol *info) +static int tumbler_set_mono_volume(struct pmac_tumbler *mix, + struct tumbler_mono_vol *info) { unsigned char block[4]; unsigned int vol; @@ -481,15 +511,17 @@ static int tumbler_set_mono_volume(pmac_tumbler_t *mix, struct tumbler_mono_vol vol = info->table[vol]; for (i = 0; i < info->bytes; i++) block[i] = (vol >> ((info->bytes - i - 1) * 8)) & 0xff; - if (i2c_smbus_write_block_data(mix->i2c.client, info->reg, - info->bytes, block) < 0) { - snd_printk("failed to set mono volume %d\n", info->index); + if (i2c_smbus_write_i2c_block_data(mix->i2c.client, info->reg, + info->bytes, block) < 0) { + snd_printk(KERN_ERR "failed to set mono volume %d\n", + info->index); return -EINVAL; } return 0; } -static int tumbler_info_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int tumbler_info_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; @@ -500,29 +532,35 @@ static int tumbler_info_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinf return 0; } -static int tumbler_get_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_get_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix; if (! (mix = chip->mixer_data)) return -ENODEV; ucontrol->value.integer.value[0] = mix->mono_vol[info->index]; return 0; } -static int tumbler_put_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_put_mono(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix; + unsigned int vol; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->mono_vol[info->index] != ucontrol->value.integer.value[0]; + vol = ucontrol->value.integer.value[0]; + if (vol >= info->max) + return -EINVAL; + change = mix->mono_vol[info->index] != vol; if (change) { - mix->mono_vol[info->index] = ucontrol->value.integer.value[0]; + mix->mono_vol[info->index] = vol; tumbler_set_mono_volume(mix, info); } return change; @@ -594,7 +632,7 @@ static struct tumbler_mono_vol snapper_treble_vol_info = { * snapper mixer volumes */ -static int snapper_set_mix_vol1(pmac_tumbler_t *mix, int idx, int ch, int reg) +static int snapper_set_mix_vol1(struct pmac_tumbler *mix, int idx, int ch, int reg) { int i, j, vol; unsigned char block[9]; @@ -611,14 +649,15 @@ static int snapper_set_mix_vol1(pmac_tumbler_t *mix, int idx, int ch, int reg) for (j = 0; j < 3; j++) block[i * 3 + j] = (vol >> ((2 - j) * 8)) & 0xff; } - if (i2c_smbus_write_block_data(mix->i2c.client, reg, 9, block) < 0) { - snd_printk("failed to set mono volume %d\n", reg); + if (i2c_smbus_write_i2c_block_data(mix->i2c.client, reg, + 9, block) < 0) { + snd_printk(KERN_ERR "failed to set mono volume %d\n", reg); return -EINVAL; } return 0; } -static int snapper_set_mix_vol(pmac_tumbler_t *mix, int idx) +static int snapper_set_mix_vol(struct pmac_tumbler *mix, int idx) { if (! mix->i2c.client) return -ENODEV; @@ -628,7 +667,8 @@ static int snapper_set_mix_vol(pmac_tumbler_t *mix, int idx) return 0; } -static int snapper_info_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snapper_info_mix(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; @@ -637,11 +677,12 @@ static int snapper_info_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo return 0; } -static int snapper_get_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snapper_get_mix(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { int idx = (int)kcontrol->private_value; - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix; if (! (mix = chip->mixer_data)) return -ENODEV; ucontrol->value.integer.value[0] = mix->mix_vol[idx][0]; @@ -649,20 +690,27 @@ static int snapper_get_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucont return 0; } -static int snapper_put_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snapper_put_mix(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { int idx = (int)kcontrol->private_value; - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix; + unsigned int vol[2]; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->mix_vol[idx][0] != ucontrol->value.integer.value[0] || - mix->mix_vol[idx][1] != ucontrol->value.integer.value[1]; + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] >= ARRAY_SIZE(mixer_volume_table) || + vol[1] >= ARRAY_SIZE(mixer_volume_table)) + return -EINVAL; + change = mix->mix_vol[idx][0] != vol[0] || + mix->mix_vol[idx][1] != vol[1]; if (change) { - mix->mix_vol[idx][0] = ucontrol->value.integer.value[0]; - mix->mix_vol[idx][1] = ucontrol->value.integer.value[1]; + mix->mix_vol[idx][0] = vol[0]; + mix->mix_vol[idx][1] = vol[1]; snapper_set_mix_vol(mix, idx); } return change; @@ -676,11 +724,12 @@ static int snapper_put_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucont enum { TUMBLER_MUTE_HP, TUMBLER_MUTE_AMP, TUMBLER_MUTE_LINE }; -static int tumbler_get_mute_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_get_mute_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix; - pmac_gpio_t *gp; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix; + struct pmac_gpio *gp; if (! (mix = chip->mixer_data)) return -ENODEV; switch(kcontrol->private_value) { @@ -699,11 +748,12 @@ static int tumbler_get_mute_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_ return 0; } -static int tumbler_put_mute_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int tumbler_put_mute_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix; - pmac_gpio_t *gp; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix; + struct pmac_gpio *gp; int val; #ifdef PMAC_SUPPORT_AUTOMUTE if (chip->update_automute && chip->auto_mute) @@ -731,18 +781,19 @@ static int tumbler_put_mute_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_ return 0; } -static int snapper_set_capture_source(pmac_tumbler_t *mix) +static int snapper_set_capture_source(struct pmac_tumbler *mix) { if (! mix->i2c.client) return -ENODEV; if (mix->capture_source) - mix->acs = mix->acs |= 2; + mix->acs |= 2; else mix->acs &= ~2; return i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, mix->acs); } -static int snapper_info_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snapper_info_capture_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { static char *texts[2] = { "Line", "Mic" @@ -756,26 +807,26 @@ static int snapper_info_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_in return 0; } -static int snapper_get_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snapper_get_capture_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix = chip->mixer_data; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix = chip->mixer_data; - snd_assert(mix, return -ENODEV); - ucontrol->value.integer.value[0] = mix->capture_source; + ucontrol->value.enumerated.item[0] = mix->capture_source; return 0; } -static int snapper_put_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snapper_put_capture_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_tumbler_t *mix = chip->mixer_data; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_tumbler *mix = chip->mixer_data; int change; - snd_assert(mix, return -ENODEV); - change = ucontrol->value.integer.value[0] != mix->capture_source; + change = ucontrol->value.enumerated.item[0] != mix->capture_source; if (change) { - mix->capture_source = !!ucontrol->value.integer.value[0]; + mix->capture_source = !!ucontrol->value.enumerated.item[0]; snapper_set_capture_source(mix); } return change; @@ -794,7 +845,7 @@ static int snapper_put_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_val /* */ -static snd_kcontrol_new_t tumbler_mixers[] __initdata = { +static struct snd_kcontrol_new tumbler_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Volume", .info = tumbler_info_master_volume, @@ -818,7 +869,7 @@ static snd_kcontrol_new_t tumbler_mixers[] __initdata = { }, }; -static snd_kcontrol_new_t snapper_mixers[] __initdata = { +static struct snd_kcontrol_new snapper_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Volume", .info = tumbler_info_master_volume, @@ -832,7 +883,8 @@ static snd_kcontrol_new_t snapper_mixers[] __initdata = { .put = tumbler_put_master_switch }, DEFINE_SNAPPER_MIX("PCM Playback Volume", 0, VOL_IDX_PCM), - DEFINE_SNAPPER_MIX("PCM Playback Volume", 1, VOL_IDX_PCM2), + /* Alternative PCM is assigned to Mic analog loopback on iBook G4 */ + DEFINE_SNAPPER_MIX("Mic Playback Volume", 0, VOL_IDX_PCM2), DEFINE_SNAPPER_MIX("Monitor Mix Volume", 0, VOL_IDX_ADC), DEFINE_SNAPPER_MONO("Tone Control - Bass", bass), DEFINE_SNAPPER_MONO("Tone Control - Treble", treble), @@ -850,7 +902,7 @@ static snd_kcontrol_new_t snapper_mixers[] __initdata = { }, }; -static snd_kcontrol_new_t tumbler_hp_sw __initdata = { +static struct snd_kcontrol_new tumbler_hp_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Playback Switch", .info = snd_pmac_boolean_mono_info, @@ -858,15 +910,15 @@ static snd_kcontrol_new_t tumbler_hp_sw __initdata = { .put = tumbler_put_mute_switch, .private_value = TUMBLER_MUTE_HP, }; -static snd_kcontrol_new_t tumbler_speaker_sw __initdata = { +static struct snd_kcontrol_new tumbler_speaker_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PC Speaker Playback Switch", + .name = "Speaker Playback Switch", .info = snd_pmac_boolean_mono_info, .get = tumbler_get_mute_switch, .put = tumbler_put_mute_switch, .private_value = TUMBLER_MUTE_AMP, }; -static snd_kcontrol_new_t tumbler_lineout_sw __initdata = { +static struct snd_kcontrol_new tumbler_lineout_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line Out Playback Switch", .info = snd_pmac_boolean_mono_info, @@ -874,7 +926,7 @@ static snd_kcontrol_new_t tumbler_lineout_sw __initdata = { .put = tumbler_put_mute_switch, .private_value = TUMBLER_MUTE_LINE, }; -static snd_kcontrol_new_t tumbler_drc_sw __initdata = { +static struct snd_kcontrol_new tumbler_drc_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "DRC Switch", .info = snd_pmac_boolean_mono_info, @@ -887,9 +939,9 @@ static snd_kcontrol_new_t tumbler_drc_sw __initdata = { /* * auto-mute stuffs */ -static int tumbler_detect_headphone(pmac_t *chip) +static int tumbler_detect_headphone(struct snd_pmac *chip) { - pmac_tumbler_t *mix = chip->mixer_data; + struct pmac_tumbler *mix = chip->mixer_data; int detect = 0; if (mix->hp_detect.addr) @@ -897,9 +949,9 @@ static int tumbler_detect_headphone(pmac_t *chip) return detect; } -static int tumbler_detect_lineout(pmac_t *chip) +static int tumbler_detect_lineout(struct snd_pmac *chip) { - pmac_tumbler_t *mix = chip->mixer_data; + struct pmac_tumbler *mix = chip->mixer_data; int detect = 0; if (mix->line_detect.addr) @@ -907,7 +959,8 @@ static int tumbler_detect_lineout(pmac_t *chip) return detect; } -static void check_mute(pmac_t *chip, pmac_gpio_t *gp, int val, int do_notify, snd_kcontrol_t *sw) +static void check_mute(struct snd_pmac *chip, struct pmac_gpio *gp, int val, int do_notify, + struct snd_kcontrol *sw) { if (check_audio_gpio(gp) != val) { write_audio_gpio(gp, val); @@ -918,18 +971,20 @@ static void check_mute(pmac_t *chip, pmac_gpio_t *gp, int val, int do_notify, sn } static struct work_struct device_change; +static struct snd_pmac *device_change_chip; -static void device_change_handler(void *self) +static void device_change_handler(struct work_struct *work) { - pmac_t *chip = (pmac_t*) self; - pmac_tumbler_t *mix; + struct snd_pmac *chip = device_change_chip; + struct pmac_tumbler *mix; int headphone, lineout; if (!chip) return; mix = chip->mixer_data; - snd_assert(mix, return); + if (snd_BUG_ON(!mix)) + return; headphone = tumbler_detect_headphone(chip); lineout = tumbler_detect_lineout(chip); @@ -946,9 +1001,8 @@ static void device_change_handler(void *self) chip->lineout_sw_ctl); if (mix->anded_reset) msleep(10); - check_mute(chip, &mix->amp_mute, 1, mix->auto_mute_notify, + check_mute(chip, &mix->amp_mute, !IS_G4DA, mix->auto_mute_notify, chip->speaker_sw_ctl); - mix->drc_enable = 0; } else { /* unmute speaker, mute others */ check_mute(chip, &mix->amp_mute, 0, mix->auto_mute_notify, @@ -960,31 +1014,33 @@ static void device_change_handler(void *self) if (mix->line_mute.addr != 0) check_mute(chip, &mix->line_mute, 1, mix->auto_mute_notify, chip->lineout_sw_ctl); - mix->drc_enable = 1; } - if (mix->auto_mute_notify) { + if (mix->auto_mute_notify) snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hp_detect_ctl->id); + +#ifdef CONFIG_SND_POWERMAC_AUTO_DRC + mix->drc_enable = ! (headphone || lineout); + if (mix->auto_mute_notify) snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->drc_sw_ctl->id); - } - - /* first set the DRC so the speaker do not explode -ReneR */ if (chip->model == PMAC_TUMBLER) tumbler_set_drc(mix); else snapper_set_drc(mix); +#endif /* reset the master volume so the correct amplification is applied */ tumbler_set_master_volume(mix); } -static void tumbler_update_automute(pmac_t *chip, int do_notify) +static void tumbler_update_automute(struct snd_pmac *chip, int do_notify) { if (chip->auto_mute) { - pmac_tumbler_t *mix; + struct pmac_tumbler *mix; mix = chip->mixer_data; - snd_assert(mix, return); + if (snd_BUG_ON(!mix)) + return; mix->auto_mute_notify = do_notify; schedule_work(&device_change); } @@ -993,9 +1049,9 @@ static void tumbler_update_automute(pmac_t *chip, int do_notify) /* interrupt - headphone plug changed */ -static irqreturn_t headphone_intr(int irq, void *devid, struct pt_regs *regs) +static irqreturn_t headphone_intr(int irq, void *devid) { - pmac_t *chip = devid; + struct snd_pmac *chip = devid; if (chip->update_automute && chip->initialized) { chip->update_automute(chip, 1); return IRQ_HANDLED; @@ -1006,39 +1062,50 @@ static irqreturn_t headphone_intr(int irq, void *devid, struct pt_regs *regs) /* look for audio-gpio device */ static struct device_node *find_audio_device(const char *name) { + struct device_node *gpiop; struct device_node *np; - if (! (np = find_devices("gpio"))) + gpiop = of_find_node_by_name(NULL, "gpio"); + if (! gpiop) return NULL; - for (np = np->child; np; np = np->sibling) { - char *property = get_property(np, "audio-gpio", NULL); + for (np = of_get_next_child(gpiop, NULL); np; + np = of_get_next_child(gpiop, np)) { + const char *property = of_get_property(np, "audio-gpio", NULL); if (property && strcmp(property, name) == 0) - return np; + break; } - return NULL; + of_node_put(gpiop); + return np; } /* look for audio-gpio device */ static struct device_node *find_compatible_audio_device(const char *name) { + struct device_node *gpiop; struct device_node *np; - if (! (np = find_devices("gpio"))) + gpiop = of_find_node_by_name(NULL, "gpio"); + if (!gpiop) return NULL; - for (np = np->child; np; np = np->sibling) { - if (device_is_compatible(np, name)) - return np; + for (np = of_get_next_child(gpiop, NULL); np; + np = of_get_next_child(gpiop, np)) { + if (of_device_is_compatible(np, name)) + break; } - return NULL; + of_node_put(gpiop); + return np; } /* find an audio device and get its address */ -static long tumbler_find_device(const char *device, const char *platform, pmac_gpio_t *gp, int is_compatible) +static long tumbler_find_device(const char *device, const char *platform, + struct pmac_gpio *gp, int is_compatible) { struct device_node *node; - u32 *base, addr; + const u32 *base; + u32 addr; + long ret; if (is_compatible) node = find_compatible_audio_device(device); @@ -1050,12 +1117,13 @@ static long tumbler_find_device(const char *device, const char *platform, pmac_g return -ENODEV; } - base = (u32 *)get_property(node, "AAPL,address", NULL); + base = of_get_property(node, "AAPL,address", NULL); if (! base) { - base = (u32 *)get_property(node, "reg", NULL); + base = of_get_property(node, "reg", NULL); if (!base) { DBG("(E) cannot find address for device %s !\n", device); snd_printd("cannot find address for device %s\n", device); + of_node_put(node); return -ENODEV; } addr = *base; @@ -1066,14 +1134,15 @@ static long tumbler_find_device(const char *device, const char *platform, pmac_g gp->addr = addr & 0x0000ffff; /* Try to find the active state, default to 0 ! */ - base = (u32 *)get_property(node, "audio-gpio-active-state", NULL); + base = of_get_property(node, "audio-gpio-active-state", NULL); if (base) { gp->active_state = *base; gp->active_val = (*base) ? 0x5 : 0x4; gp->inactive_val = (*base) ? 0x4 : 0x5; } else { - u32 *prop = NULL; - gp->active_state = 0; + const u32 *prop = NULL; + gp->active_state = IS_G4DA + && !strncmp(device, "keywest-gpio1", 13); gp->active_val = 0x4; gp->inactive_val = 0x5; /* Here are some crude hacks to extract the GPIO polarity and @@ -1081,7 +1150,7 @@ static long tumbler_find_device(const char *device, const char *platform, pmac_g * as we don't yet have an interpreter for these things */ if (platform) - prop = (u32 *)get_property(node, platform, NULL); + prop = of_get_property(node, platform, NULL); if (prop) { if (prop[3] == 0x9 && prop[4] == 0x9) { gp->active_val = 0xd; @@ -1097,13 +1166,15 @@ static long tumbler_find_device(const char *device, const char *platform, pmac_g DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n", device, gp->addr, gp->active_state); - return (node->n_intrs > 0) ? node->intrs[0].line : 0; + ret = irq_of_parse_and_map(node, 0); + of_node_put(node); + return ret; } /* reset audio */ -static void tumbler_reset_audio(pmac_t *chip) +static void tumbler_reset_audio(struct snd_pmac *chip) { - pmac_tumbler_t *mix = chip->mixer_data; + struct pmac_tumbler *mix = chip->mixer_data; if (mix->anded_reset) { DBG("(I) codec anded reset !\n"); @@ -1130,9 +1201,9 @@ static void tumbler_reset_audio(pmac_t *chip) #ifdef CONFIG_PM /* suspend mixer */ -static void tumbler_suspend(pmac_t *chip) +static void tumbler_suspend(struct snd_pmac *chip) { - pmac_tumbler_t *mix = chip->mixer_data; + struct pmac_tumbler *mix = chip->mixer_data; if (mix->headphone_irq >= 0) disable_irq(mix->headphone_irq); @@ -1160,11 +1231,9 @@ static void tumbler_suspend(pmac_t *chip) } /* resume mixer */ -static void tumbler_resume(pmac_t *chip) +static void tumbler_resume(struct snd_pmac *chip) { - pmac_tumbler_t *mix = chip->mixer_data; - - snd_assert(mix, return); + struct pmac_tumbler *mix = chip->mixer_data; mix->acs &= ~1; mix->master_switch[0] = mix->save_master_switch[0]; @@ -1194,19 +1263,24 @@ static void tumbler_resume(pmac_t *chip) tumbler_set_master_volume(mix); if (chip->update_automute) chip->update_automute(chip, 0); - if (mix->headphone_irq >= 0) + if (mix->headphone_irq >= 0) { + unsigned char val; + enable_irq(mix->headphone_irq); + /* activate headphone status interrupts */ + val = do_gpio_read(&mix->hp_detect); + do_gpio_write(&mix->hp_detect, val | 0x80); + } if (mix->lineout_irq >= 0) enable_irq(mix->lineout_irq); } #endif /* initialize tumbler */ -static int __init tumbler_init(pmac_t *chip) +static int tumbler_init(struct snd_pmac *chip) { int irq; - pmac_tumbler_t *mix = chip->mixer_data; - snd_assert(mix, return -EINVAL); + struct pmac_tumbler *mix = chip->mixer_data; if (tumbler_find_device("audio-hw-reset", "platform-do-hw-reset", @@ -1234,18 +1308,21 @@ static int __init tumbler_init(pmac_t *chip) &mix->line_mute, 1); irq = tumbler_find_device("headphone-detect", NULL, &mix->hp_detect, 0); - if (irq < 0) + if (irq <= NO_IRQ) irq = tumbler_find_device("headphone-detect", NULL, &mix->hp_detect, 1); - if (irq < 0) + if (irq <= NO_IRQ) irq = tumbler_find_device("keywest-gpio15", NULL, &mix->hp_detect, 1); mix->headphone_irq = irq; irq = tumbler_find_device("line-output-detect", NULL, &mix->line_detect, 0); - if (irq < 0) + if (irq <= NO_IRQ) irq = tumbler_find_device("line-output-detect", NULL, &mix->line_detect, 1); + if (IS_G4DA && irq <= NO_IRQ) + irq = tumbler_find_device("keywest-gpio16", + NULL, &mix->line_detect, 1); mix->lineout_irq = irq; tumbler_reset_audio(chip); @@ -1253,9 +1330,9 @@ static int __init tumbler_init(pmac_t *chip) return 0; } -static void tumbler_cleanup(pmac_t *chip) +static void tumbler_cleanup(struct snd_pmac *chip) { - pmac_tumbler_t *mix = chip->mixer_data; + struct pmac_tumbler *mix = chip->mixer_data; if (! mix) return; @@ -1273,23 +1350,19 @@ static void tumbler_cleanup(pmac_t *chip) } /* exported */ -int __init snd_pmac_tumbler_init(pmac_t *chip) +int snd_pmac_tumbler_init(struct snd_pmac *chip) { int i, err; - pmac_tumbler_t *mix; - u32 *paddr; + struct pmac_tumbler *mix; + const u32 *paddr; struct device_node *tas_node, *np; char *chipname; -#ifdef CONFIG_KMOD - if (current->fs->root) - request_module("i2c-keywest"); -#endif /* CONFIG_KMOD */ + request_module("i2c-powermac"); - mix = kmalloc(sizeof(*mix), GFP_KERNEL); + mix = kzalloc(sizeof(*mix), GFP_KERNEL); if (! mix) return -ENOMEM; - memset(mix, 0, sizeof(*mix)); mix->headphone_irq = -1; chip->mixer_data = mix; @@ -1299,9 +1372,9 @@ int __init snd_pmac_tumbler_init(pmac_t *chip) for (np = chip->node->child; np; np = np->sibling) { if (!strcmp(np->name, "sound")) { - if (get_property(np, "has-anded-reset", NULL)) + if (of_get_property(np, "has-anded-reset", NULL)) mix->anded_reset = 1; - if (get_property(np, "layout-id", NULL)) + if (of_get_property(np, "layout-id", NULL)) mix->reset_on_sleep = 0; break; } @@ -1310,19 +1383,20 @@ int __init snd_pmac_tumbler_init(pmac_t *chip) return err; /* set up TAS */ - tas_node = find_devices("deq"); + tas_node = of_find_node_by_name(NULL, "deq"); if (tas_node == NULL) - tas_node = find_devices("codec"); + tas_node = of_find_node_by_name(NULL, "codec"); if (tas_node == NULL) return -ENODEV; - paddr = (u32 *)get_property(tas_node, "i2c-address", NULL); + paddr = of_get_property(tas_node, "i2c-address", NULL); if (paddr == NULL) - paddr = (u32 *)get_property(tas_node, "reg", NULL); + paddr = of_get_property(tas_node, "reg", NULL); if (paddr) mix->i2c.addr = (*paddr) >> 1; else mix->i2c.addr = TAS_I2C_ADDR; + of_node_put(tas_node); DBG("(I) TAS i2c address is: %x\n", mix->i2c.addr); @@ -1370,12 +1444,24 @@ int __init snd_pmac_tumbler_init(pmac_t *chip) if ((err = snd_ctl_add(chip->card, chip->drc_sw_ctl)) < 0) return err; + /* set initial DRC range to 60% */ + if (chip->model == PMAC_TUMBLER) + mix->drc_range = (TAS3001_DRC_MAX * 6) / 10; + else + mix->drc_range = (TAS3004_DRC_MAX * 6) / 10; + mix->drc_enable = 1; /* will be changed later if AUTO_DRC is set */ + if (chip->model == PMAC_TUMBLER) + tumbler_set_drc(mix); + else + snapper_set_drc(mix); + #ifdef CONFIG_PM chip->suspend = tumbler_suspend; chip->resume = tumbler_resume; #endif - INIT_WORK(&device_change, device_change_handler, (void *)chip); + INIT_WORK(&device_change, device_change_handler); + device_change_chip = chip; #ifdef PMAC_SUPPORT_AUTOMUTE if ((mix->headphone_irq >=0 || mix->lineout_irq >= 0) |
