diff options
Diffstat (limited to 'sound/ppc')
| -rw-r--r-- | sound/ppc/Kconfig | 47 | ||||
| -rw-r--r-- | sound/ppc/Makefile | 7 | ||||
| -rw-r--r-- | sound/ppc/awacs.c | 581 | ||||
| -rw-r--r-- | sound/ppc/awacs.h | 21 | ||||
| -rw-r--r-- | sound/ppc/beep.c | 126 | ||||
| -rw-r--r-- | sound/ppc/burgundy.c | 513 | ||||
| -rw-r--r-- | sound/ppc/burgundy.h | 31 | ||||
| -rw-r--r-- | sound/ppc/daca.c | 107 | ||||
| -rw-r--r-- | sound/ppc/keywest.c | 114 | ||||
| -rw-r--r-- | sound/ppc/pmac.c | 710 | ||||
| -rw-r--r-- | sound/ppc/pmac.h | 103 | ||||
| -rw-r--r-- | sound/ppc/powermac.c | 87 | ||||
| -rw-r--r-- | sound/ppc/snd_ps3.c | 1163 | ||||
| -rw-r--r-- | sound/ppc/snd_ps3.h | 136 | ||||
| -rw-r--r-- | sound/ppc/snd_ps3_reg.h | 891 | ||||
| -rw-r--r-- | sound/ppc/toonie.c | 379 | ||||
| -rw-r--r-- | sound/ppc/tumbler.c | 492 |
17 files changed, 4024 insertions, 1484 deletions
diff --git a/sound/ppc/Kconfig b/sound/ppc/Kconfig index 75213bf4d56..0519c60f5be 100644 --- a/sound/ppc/Kconfig +++ b/sound/ppc/Kconfig @@ -1,23 +1,52 @@ # ALSA PowerMac drivers -menu "ALSA PowerMac devices" - depends on SND!=n && PPC - -comment "ALSA PowerMac requires I2C" - depends on SND && I2C=n +menuconfig SND_PPC + bool "PowerPC sound devices" + depends on PPC + default y + help + Support for sound devices specific to PowerPC architectures. -comment "ALSA PowerMac requires INPUT" - depends on SND && INPUT=n +if SND_PPC config SND_POWERMAC tristate "PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)" - depends on SND && I2C && INPUT && PPC_PMAC + depends on I2C && INPUT && PPC_PMAC select SND_PCM + select SND_VMASTER help Say Y here to include support for the integrated sound device. To compile this driver as a module, choose M here: the module will be called snd-powermac. -endmenu +config SND_POWERMAC_AUTO_DRC + bool "Toggle DRC automatically at headphone/line plug-in" + depends on SND_POWERMAC + default y + help + Say Y here to enable the automatic toggle of DRC (dynamic + range compression) on Tumbler/Snapper. + If this feature is enabled, DRC is turned off when the + headphone/line jack is plugged, and turned on when unplugged. + + Note that you can turn on/off DRC manually even without this + option. + +config SND_PS3 + tristate "PS3 Audio support" + depends on PS3_PS3AV + select SND_PCM + default m + help + Say Y here to include support for audio on the PS3 + + To compile this driver as a module, choose M here: the module + will be called snd_ps3. + +config SND_PS3_DEFAULT_START_DELAY + int "Startup delay time in ms" + depends on SND_PS3 + default "2000" +endif # SND_PPC diff --git a/sound/ppc/Makefile b/sound/ppc/Makefile index d6ba9959097..679c45a8da2 100644 --- a/sound/ppc/Makefile +++ b/sound/ppc/Makefile @@ -1,9 +1,10 @@ # # Makefile for ALSA -# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> +# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o toonie.o keywest.o beep.o +snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o # Toplevel Module Dependency -obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o +obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o +obj-$(CONFIG_SND_PS3) += snd_ps3.o diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 758ca1bcbcf..5fbf5db2543 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -20,7 +20,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/nvram.h> #include <linux/init.h> @@ -35,18 +34,18 @@ #endif #ifdef PMAC_AMP_AVAIL -typedef struct awacs_amp { +struct awacs_amp { unsigned char amp_master; unsigned char amp_vol[2][2]; unsigned char amp_tone[2]; -} awacs_amp_t; +}; #define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA) #endif /* PMAC_AMP_AVAIL */ -static void snd_pmac_screamer_wait(pmac_t *chip) +static void snd_pmac_screamer_wait(struct snd_pmac *chip) { long timeout = 2000; while (!(in_le32(&chip->awacs->codec_stat) & MASK_VALID)) { @@ -62,7 +61,7 @@ static void snd_pmac_screamer_wait(pmac_t *chip) * write AWACS register */ static void -snd_pmac_awacs_write(pmac_t *chip, int val) +snd_pmac_awacs_write(struct snd_pmac *chip, int val) { long timeout = 5000000; @@ -78,21 +77,21 @@ snd_pmac_awacs_write(pmac_t *chip, int val) } static void -snd_pmac_awacs_write_reg(pmac_t *chip, int reg, int val) +snd_pmac_awacs_write_reg(struct snd_pmac *chip, int reg, int val) { snd_pmac_awacs_write(chip, val | (reg << 12)); chip->awacs_reg[reg] = val; } static void -snd_pmac_awacs_write_noreg(pmac_t *chip, int reg, int val) +snd_pmac_awacs_write_noreg(struct snd_pmac *chip, int reg, int val) { snd_pmac_awacs_write(chip, val | (reg << 12)); } #ifdef CONFIG_PM /* Recalibrate chip */ -static void screamer_recalibrate(pmac_t *chip) +static void screamer_recalibrate(struct snd_pmac *chip) { if (chip->model != PMAC_SCREAMER) return; @@ -105,7 +104,8 @@ static void screamer_recalibrate(pmac_t *chip) /* delay for broken crystal part */ msleep(750); snd_pmac_awacs_write_noreg(chip, 1, - chip->awacs_reg[1] | MASK_RECALIBRATE | MASK_CMUTE | MASK_AMUTE); + chip->awacs_reg[1] | MASK_RECALIBRATE | + MASK_CMUTE | MASK_AMUTE); snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); } @@ -118,7 +118,7 @@ static void screamer_recalibrate(pmac_t *chip) /* * additional callback to set the pcm format */ -static void snd_pmac_awacs_set_format(pmac_t *chip) +static void snd_pmac_awacs_set_format(struct snd_pmac *chip) { chip->awacs_reg[1] &= ~MASK_SAMPLERATE; chip->awacs_reg[1] |= chip->rate_index << 3; @@ -132,7 +132,8 @@ static void snd_pmac_awacs_set_format(pmac_t *chip) /* * volumes: 0-15 stereo */ -static int snd_pmac_awacs_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_pmac_awacs_info_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; @@ -140,10 +141,11 @@ static int snd_pmac_awacs_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_inf uinfo->value.integer.max = 15; return 0; } - -static int snd_pmac_awacs_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) + +static int snd_pmac_awacs_get_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int lshift = (kcontrol->private_value >> 8) & 0xff; int inverted = (kcontrol->private_value >> 16) & 1; @@ -163,18 +165,21 @@ static int snd_pmac_awacs_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_valu return 0; } -static int snd_pmac_awacs_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int lshift = (kcontrol->private_value >> 8) & 0xff; int inverted = (kcontrol->private_value >> 16) & 1; int val, oldval; unsigned long flags; - int vol[2]; + unsigned int vol[2]; vol[0] = ucontrol->value.integer.value[0]; vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] > 0x0f || vol[1] > 0x0f) + return -EINVAL; if (inverted) { vol[0] = 0x0f - vol[0]; vol[1] = 0x0f - vol[1]; @@ -203,9 +208,10 @@ static int snd_pmac_awacs_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_valu /* * mute master/ogain for AWACS: mono */ -static int snd_pmac_awacs_get_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_awacs_get_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int invert = (kcontrol->private_value >> 16) & 1; @@ -221,9 +227,10 @@ static int snd_pmac_awacs_get_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_valu return 0; } -static int snd_pmac_awacs_put_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_awacs_put_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int invert = (kcontrol->private_value >> 16) & 1; @@ -260,7 +267,8 @@ static int snd_pmac_awacs_put_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_valu static void awacs_set_cuda(int reg, int val) { struct adb_request req; - cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, reg, val); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, + reg, val); while (! req.complete) cuda_poll(); } @@ -268,7 +276,7 @@ static void awacs_set_cuda(int reg, int val) /* * level = 0 - 14, 7 = 0 dB */ -static void awacs_amp_set_tone(awacs_amp_t *amp, int bass, int treble) +static void awacs_amp_set_tone(struct awacs_amp *amp, int bass, int treble) { amp->amp_tone[0] = bass; amp->amp_tone[1] = treble; @@ -282,10 +290,11 @@ static void awacs_amp_set_tone(awacs_amp_t *amp, int bass, int treble) /* * vol = 0 - 31 (attenuation), 32 = mute bit, stereo */ -static int awacs_amp_set_vol(awacs_amp_t *amp, int index, int lvol, int rvol, int do_check) +static int awacs_amp_set_vol(struct awacs_amp *amp, int index, + int lvol, int rvol, int do_check) { if (do_check && amp->amp_vol[index][0] == lvol && - amp->amp_vol[index][1] == rvol) + amp->amp_vol[index][1] == rvol) return 0; awacs_set_cuda(3 + index, lvol); awacs_set_cuda(5 + index, rvol); @@ -297,7 +306,7 @@ static int awacs_amp_set_vol(awacs_amp_t *amp, int index, int lvol, int rvol, in /* * 0 = -79 dB, 79 = 0 dB, 99 = +20 dB */ -static void awacs_amp_set_master(awacs_amp_t *amp, int vol) +static void awacs_amp_set_master(struct awacs_amp *amp, int vol) { amp->amp_master = vol; if (vol <= 79) @@ -307,10 +316,11 @@ static void awacs_amp_set_master(awacs_amp_t *amp, int vol) awacs_set_cuda(1, vol); } -static void awacs_amp_free(pmac_t *chip) +static void awacs_amp_free(struct snd_pmac *chip) { - awacs_amp_t *amp = chip->mixer_data; - snd_assert(amp, return); + struct awacs_amp *amp = chip->mixer_data; + if (!amp) + return; kfree(amp); chip->mixer_data = NULL; chip->mixer_free = NULL; @@ -320,7 +330,8 @@ static void awacs_amp_free(pmac_t *chip) /* * mixer controls */ -static int snd_pmac_awacs_info_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_pmac_awacs_info_volume_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; @@ -328,60 +339,65 @@ static int snd_pmac_awacs_info_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem uinfo->value.integer.max = 31; return 0; } - -static int snd_pmac_awacs_get_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) + +static int snd_pmac_awacs_get_volume_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; - awacs_amp_t *amp = chip->mixer_data; - snd_assert(amp, return -EINVAL); - snd_assert(index >= 0 && index <= 1, return -EINVAL); + struct awacs_amp *amp = chip->mixer_data; + ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31); ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31); return 0; } -static int snd_pmac_awacs_put_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_awacs_put_volume_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; int vol[2]; - awacs_amp_t *amp = chip->mixer_data; - snd_assert(amp, return -EINVAL); - snd_assert(index >= 0 && index <= 1, return -EINVAL); + struct awacs_amp *amp = chip->mixer_data; - vol[0] = (31 - (ucontrol->value.integer.value[0] & 31)) | (amp->amp_vol[index][0] & 32); - vol[1] = (31 - (ucontrol->value.integer.value[1] & 31)) | (amp->amp_vol[index][1] & 32); + vol[0] = (31 - (ucontrol->value.integer.value[0] & 31)) + | (amp->amp_vol[index][0] & 32); + vol[1] = (31 - (ucontrol->value.integer.value[1] & 31)) + | (amp->amp_vol[index][1] & 32); return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); } -static int snd_pmac_awacs_get_switch_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_awacs_get_switch_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; - awacs_amp_t *amp = chip->mixer_data; - snd_assert(amp, return -EINVAL); - snd_assert(index >= 0 && index <= 1, return -EINVAL); - ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32) ? 0 : 1; - ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) ? 0 : 1; + struct awacs_amp *amp = chip->mixer_data; + + ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32) + ? 0 : 1; + ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) + ? 0 : 1; return 0; } -static int snd_pmac_awacs_put_switch_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_awacs_put_switch_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; int vol[2]; - awacs_amp_t *amp = chip->mixer_data; - snd_assert(amp, return -EINVAL); - snd_assert(index >= 0 && index <= 1, return -EINVAL); + struct awacs_amp *amp = chip->mixer_data; - vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32) | (amp->amp_vol[index][0] & 31); - vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32) | (amp->amp_vol[index][1] & 31); + vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32) + | (amp->amp_vol[index][0] & 31); + vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32) + | (amp->amp_vol[index][1] & 31); return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); } -static int snd_pmac_awacs_info_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_pmac_awacs_info_tone_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; @@ -389,34 +405,39 @@ static int snd_pmac_awacs_info_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_i uinfo->value.integer.max = 14; return 0; } - -static int snd_pmac_awacs_get_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) + +static int snd_pmac_awacs_get_tone_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; - awacs_amp_t *amp = chip->mixer_data; - snd_assert(amp, return -EINVAL); - snd_assert(index >= 0 && index <= 1, return -EINVAL); + struct awacs_amp *amp = chip->mixer_data; + ucontrol->value.integer.value[0] = amp->amp_tone[index]; return 0; } -static int snd_pmac_awacs_put_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; - awacs_amp_t *amp = chip->mixer_data; - snd_assert(amp, return -EINVAL); - snd_assert(index >= 0 && index <= 1, return -EINVAL); - if (ucontrol->value.integer.value[0] != amp->amp_tone[index]) { - amp->amp_tone[index] = ucontrol->value.integer.value[0]; + struct awacs_amp *amp = chip->mixer_data; + unsigned int val; + + val = ucontrol->value.integer.value[0]; + if (val > 14) + return -EINVAL; + if (val != amp->amp_tone[index]) { + amp->amp_tone[index] = val; awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); return 1; } return 0; } -static int snd_pmac_awacs_info_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_pmac_awacs_info_master_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; @@ -424,23 +445,29 @@ static int snd_pmac_awacs_info_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem uinfo->value.integer.max = 99; return 0; } - -static int snd_pmac_awacs_get_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) + +static int snd_pmac_awacs_get_master_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - awacs_amp_t *amp = chip->mixer_data; - snd_assert(amp, return -EINVAL); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct awacs_amp *amp = chip->mixer_data; + ucontrol->value.integer.value[0] = amp->amp_master; return 0; } -static int snd_pmac_awacs_put_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - awacs_amp_t *amp = chip->mixer_data; - snd_assert(amp, return -EINVAL); - if (ucontrol->value.integer.value[0] != amp->amp_master) { - amp->amp_master = ucontrol->value.integer.value[0]; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct awacs_amp *amp = chip->mixer_data; + unsigned int val; + + val = ucontrol->value.integer.value[0]; + if (val > 99) + return -EINVAL; + if (val != amp->amp_master) { + amp->amp_master = val; awacs_amp_set_master(amp, amp->amp_master); return 1; } @@ -450,9 +477,9 @@ static int snd_pmac_awacs_put_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_ #define AMP_CH_SPK 0 #define AMP_CH_HD 1 -static snd_kcontrol_new_t snd_pmac_awacs_amp_vol[] __initdata = { +static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PC Speaker Playback Volume", + .name = "Speaker Playback Volume", .info = snd_pmac_awacs_info_volume_amp, .get = snd_pmac_awacs_get_volume_amp, .put = snd_pmac_awacs_put_volume_amp, @@ -487,7 +514,7 @@ static snd_kcontrol_new_t snd_pmac_awacs_amp_vol[] __initdata = { }, }; -static snd_kcontrol_new_t snd_pmac_awacs_amp_hp_sw __initdata = { +static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Playback Switch", .info = snd_pmac_boolean_stereo_info, @@ -496,9 +523,9 @@ static snd_kcontrol_new_t snd_pmac_awacs_amp_hp_sw __initdata = { .private_value = AMP_CH_HD, }; -static snd_kcontrol_new_t snd_pmac_awacs_amp_spk_sw __initdata = { +static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PC Speaker Playback Switch", + .name = "Speaker Playback Switch", .info = snd_pmac_boolean_stereo_info, .get = snd_pmac_awacs_get_switch_amp, .put = snd_pmac_awacs_put_switch_amp, @@ -511,36 +538,37 @@ static snd_kcontrol_new_t snd_pmac_awacs_amp_spk_sw __initdata = { /* * mic boost for screamer */ -static int snd_pmac_screamer_mic_boost_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_pmac_screamer_mic_boost_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 2; + uinfo->value.integer.max = 3; return 0; } -static int snd_pmac_screamer_mic_boost_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_screamer_mic_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - int val; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + int val = 0; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); if (chip->awacs_reg[6] & MASK_MIC_BOOST) - val = 2; - else if (chip->awacs_reg[0] & MASK_GAINLINE) - val = 1; - else - val = 0; + val |= 2; + if (chip->awacs_reg[0] & MASK_GAINLINE) + val |= 1; spin_unlock_irqrestore(&chip->reg_lock, flags); ucontrol->value.integer.value[0] = val; return 0; } -static int snd_pmac_screamer_mic_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int changed = 0; int val0, val6; unsigned long flags; @@ -548,11 +576,10 @@ static int snd_pmac_screamer_mic_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_ele spin_lock_irqsave(&chip->reg_lock, flags); val0 = chip->awacs_reg[0] & ~MASK_GAINLINE; val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST; - if (ucontrol->value.integer.value[0] > 0) { + if (ucontrol->value.integer.value[0] & 1) val0 |= MASK_GAINLINE; - if (ucontrol->value.integer.value[0] > 1) - val6 |= MASK_MIC_BOOST; - } + if (ucontrol->value.integer.value[0] & 2) + val6 |= MASK_MIC_BOOST; if (val0 != chip->awacs_reg[0]) { snd_pmac_awacs_write_reg(chip, 0, val0); changed = 1; @@ -568,58 +595,131 @@ static int snd_pmac_screamer_mic_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_ele /* * lists of mixer elements */ -static snd_kcontrol_new_t snd_pmac_awacs_mixers[] __initdata = { - AWACS_VOLUME("Master Playback Volume", 2, 6, 1), +static struct snd_kcontrol_new snd_pmac_awacs_mixers[] = { AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0), - AWACS_VOLUME("Capture Volume", 0, 4, 0), + AWACS_VOLUME("Master Capture Volume", 0, 4, 0), +/* AWACS_SWITCH("Unknown Playback Switch", 6, SHIFT_PAROUT0, 0), */ +}; + +static struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] = { + AWACS_VOLUME("Master Playback Volume", 2, 6, 1), + AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1), + AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), + AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_LINE, 0), +}; + +static struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] = { + AWACS_VOLUME("Line out Playback Volume", 2, 6, 1), +}; + +static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] = { + AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1), + AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), +}; + +static struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] = { + AWACS_VOLUME("Line out Playback Volume", 2, 6, 1), + AWACS_VOLUME("Master Playback Volume", 5, 6, 1), + AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), + AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), +}; + +static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] = { + AWACS_VOLUME("Line out Playback Volume", 2, 6, 1), + AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), + AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), +}; + +static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac5500[] = { + AWACS_VOLUME("Headphone Playback Volume", 2, 6, 1), +}; + +static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] = { + AWACS_VOLUME("Master Playback Volume", 2, 6, 1), AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), }; /* FIXME: is this correct order? * screamer (powerbook G3 pismo) seems to have different bits... */ -static snd_kcontrol_new_t snd_pmac_awacs_mixers2[] __initdata = { +static struct snd_kcontrol_new snd_pmac_awacs_mixers2[] = { AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0), AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0), }; -static snd_kcontrol_new_t snd_pmac_screamer_mixers2[] __initdata = { +static struct snd_kcontrol_new snd_pmac_screamer_mixers2[] = { AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0), }; -static snd_kcontrol_new_t snd_pmac_awacs_master_sw __initdata = +static struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] = { + AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), +}; + +static struct snd_kcontrol_new snd_pmac_awacs_master_sw = AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1); -static snd_kcontrol_new_t snd_pmac_awacs_mic_boost[] __initdata = { - AWACS_SWITCH("Mic Boost", 0, SHIFT_GAINLINE, 0), +static struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac = +AWACS_SWITCH("Line out Playback Switch", 1, SHIFT_HDMUTE, 1); + +static struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 = +AWACS_SWITCH("Headphone Playback Switch", 1, SHIFT_HDMUTE, 1); + +static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] = { + AWACS_SWITCH("Mic Boost Capture Switch", 0, SHIFT_GAINLINE, 0), }; -static snd_kcontrol_new_t snd_pmac_screamer_mic_boost[] __initdata = { +static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic Boost", + .name = "Mic Boost Capture Volume", .info = snd_pmac_screamer_mic_boost_info, .get = snd_pmac_screamer_mic_boost_get, .put = snd_pmac_screamer_mic_boost_put, }, }; -static snd_kcontrol_new_t snd_pmac_awacs_speaker_vol[] __initdata = { - AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1), +static struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] = +{ + AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0), +}; + +static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] = +{ + AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0), + AWACS_SWITCH("CD Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0), +}; + +static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] = +{ + AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0), + AWACS_SWITCH("Mic Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0), +}; + +static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] = { + AWACS_VOLUME("Speaker Playback Volume", 4, 6, 1), }; -static snd_kcontrol_new_t snd_pmac_awacs_speaker_sw __initdata = -AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1); + +static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw = +AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1); + +static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 = +AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 1); + +static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 = +AWACS_SWITCH("Speaker Playback Switch", 1, SHIFT_PAROUT1, 0); /* * add new mixer elements to the card */ -static int build_mixers(pmac_t *chip, int nums, snd_kcontrol_new_t *mixers) +static int build_mixers(struct snd_pmac *chip, int nums, + struct snd_kcontrol_new *mixers) { int i, err; for (i = 0; i < nums; i++) { - if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0) + err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip)); + if (err < 0) return err; } return 0; @@ -629,7 +729,7 @@ static int build_mixers(pmac_t *chip, int nums, snd_kcontrol_new_t *mixers) /* * restore all registers */ -static void awacs_restore_all_regs(pmac_t *chip) +static void awacs_restore_all_regs(struct snd_pmac *chip) { snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]); snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); @@ -643,16 +743,16 @@ static void awacs_restore_all_regs(pmac_t *chip) } #ifdef CONFIG_PM -static void snd_pmac_awacs_suspend(pmac_t *chip) +static void snd_pmac_awacs_suspend(struct snd_pmac *chip) { snd_pmac_awacs_write_noreg(chip, 1, (chip->awacs_reg[1] | MASK_AMUTE | MASK_CMUTE)); } -static void snd_pmac_awacs_resume(pmac_t *chip) +static void snd_pmac_awacs_resume(struct snd_pmac *chip) { - if (machine_is_compatible("PowerBook3,1") - || machine_is_compatible("PowerBook3,2")) { + if (of_machine_is_compatible("PowerBook3,1") + || of_machine_is_compatible("PowerBook3,2")) { msleep(100); snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1] & ~MASK_PAROUT); @@ -668,9 +768,11 @@ static void snd_pmac_awacs_resume(pmac_t *chip) screamer_recalibrate(chip); #ifdef PMAC_AMP_AVAIL if (chip->mixer_data) { - awacs_amp_t *amp = chip->mixer_data; - awacs_amp_set_vol(amp, 0, amp->amp_vol[0][0], amp->amp_vol[0][1], 0); - awacs_amp_set_vol(amp, 1, amp->amp_vol[1][0], amp->amp_vol[1][1], 0); + struct awacs_amp *amp = chip->mixer_data; + awacs_amp_set_vol(amp, 0, + amp->amp_vol[0][0], amp->amp_vol[0][1], 0); + awacs_amp_set_vol(amp, 1, + amp->amp_vol[1][0], amp->amp_vol[1][1], 0); awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); awacs_amp_set_master(amp, amp->amp_master); } @@ -678,17 +780,30 @@ static void snd_pmac_awacs_resume(pmac_t *chip) } #endif /* CONFIG_PM */ +#define IS_PM7500 (of_machine_is_compatible("AAPL,7500") \ + || of_machine_is_compatible("AAPL,8500") \ + || of_machine_is_compatible("AAPL,9500")) +#define IS_PM5500 (of_machine_is_compatible("AAPL,e411")) +#define IS_BEIGE (of_machine_is_compatible("AAPL,Gossamer")) +#define IS_IMAC1 (of_machine_is_compatible("PowerMac2,1")) +#define IS_IMAC2 (of_machine_is_compatible("PowerMac2,2") \ + || of_machine_is_compatible("PowerMac4,1")) +#define IS_G4AGP (of_machine_is_compatible("PowerMac3,1")) +#define IS_LOMBARD (of_machine_is_compatible("PowerBook1,1")) + +static int imac1, imac2; + #ifdef PMAC_SUPPORT_AUTOMUTE /* * auto-mute stuffs */ -static int snd_pmac_awacs_detect_headphone(pmac_t *chip) +static int snd_pmac_awacs_detect_headphone(struct snd_pmac *chip) { return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0; } #ifdef PMAC_AMP_AVAIL -static int toggle_amp_mute(awacs_amp_t *amp, int index, int mute) +static int toggle_amp_mute(struct awacs_amp *amp, int index, int mute) { int vol[2]; vol[0] = amp->amp_vol[index][0] & 31; @@ -701,12 +816,12 @@ static int toggle_amp_mute(awacs_amp_t *amp, int index, int mute) } #endif -static void snd_pmac_awacs_update_automute(pmac_t *chip, int do_notify) +static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify) { if (chip->auto_mute) { #ifdef PMAC_AMP_AVAIL if (chip->mixer_data) { - awacs_amp_t *amp = chip->mixer_data; + struct awacs_amp *amp = chip->mixer_data; int changed; if (snd_pmac_awacs_detect_headphone(chip)) { changed = toggle_amp_mute(amp, AMP_CH_HD, 0); @@ -720,9 +835,21 @@ static void snd_pmac_awacs_update_automute(pmac_t *chip, int do_notify) } else #endif { - int reg = chip->awacs_reg[1] | (MASK_HDMUTE|MASK_SPKMUTE); + int reg = chip->awacs_reg[1] + | (MASK_HDMUTE | MASK_SPKMUTE); + if (imac1) { + reg &= ~MASK_SPKMUTE; + reg |= MASK_PAROUT1; + } else if (imac2) { + reg &= ~MASK_SPKMUTE; + reg &= ~MASK_PAROUT1; + } if (snd_pmac_awacs_detect_headphone(chip)) reg &= ~MASK_HDMUTE; + else if (imac1) + reg &= ~MASK_PAROUT1; + else if (imac2) + reg |= MASK_PAROUT1; else reg &= ~MASK_SPKMUTE; if (do_notify && reg == chip->awacs_reg[1]) @@ -745,11 +872,22 @@ static void snd_pmac_awacs_update_automute(pmac_t *chip, int do_notify) /* * initialize chip */ -int __init -snd_pmac_awacs_init(pmac_t *chip) +int +snd_pmac_awacs_init(struct snd_pmac *chip) { + int pm7500 = IS_PM7500; + int pm5500 = IS_PM5500; + int beige = IS_BEIGE; + int g4agp = IS_G4AGP; + int lombard = IS_LOMBARD; + int imac; int err, vol; + struct snd_kcontrol *vmaster_sw, *vmaster_vol; + struct snd_kcontrol *master_vol, *speaker_vol; + imac1 = IS_IMAC1; + imac2 = IS_IMAC2; + imac = imac1 || imac2; /* looks like MASK_GAINLINE triggers something, so we set here * as start-up */ @@ -757,7 +895,7 @@ snd_pmac_awacs_init(pmac_t *chip) chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE; /* FIXME: Only machines with external SRS module need MASK_PAROUT */ if (chip->has_iic || chip->device_id == 0x5 || - /*chip->_device_id == 0x8 || */ + /* chip->_device_id == 0x8 || */ chip->device_id == 0xb) chip->awacs_reg[1] |= MASK_PAROUT; /* get default volume from nvram */ @@ -768,8 +906,10 @@ snd_pmac_awacs_init(pmac_t *chip) chip->awacs_reg[2] = vol; chip->awacs_reg[4] = vol; if (chip->model == PMAC_SCREAMER) { - chip->awacs_reg[5] = vol; /* FIXME: screamer has loopthru vol control */ - chip->awacs_reg[6] = MASK_MIC_BOOST; /* FIXME: maybe should be vol << 3 for PCMCIA speaker */ + /* FIXME: screamer has loopthru vol control */ + chip->awacs_reg[5] = vol; + /* FIXME: maybe should be vol << 3 for PCMCIA speaker */ + chip->awacs_reg[6] = MASK_MIC_BOOST; chip->awacs_reg[7] = 0; } @@ -780,13 +920,13 @@ snd_pmac_awacs_init(pmac_t *chip) chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf; #ifdef PMAC_AMP_AVAIL if (chip->revision == 3 && chip->has_iic && CHECK_CUDA_AMP()) { - awacs_amp_t *amp = kmalloc(sizeof(*amp), GFP_KERNEL); + struct awacs_amp *amp = kzalloc(sizeof(*amp), GFP_KERNEL); if (! amp) return -ENOMEM; chip->mixer_data = amp; - memset(amp, 0, sizeof(*amp)); chip->mixer_free = awacs_amp_free; - awacs_amp_set_vol(amp, 0, 63, 63, 0); /* mute and zero vol */ + /* mute and zero vol */ + awacs_amp_set_vol(amp, 0, 63, 63, 0); awacs_amp_set_vol(amp, 1, 63, 63, 0); awacs_amp_set_tone(amp, 7, 7); /* 0 dB */ awacs_amp_set_master(amp, 79); /* 0 dB */ @@ -797,20 +937,25 @@ snd_pmac_awacs_init(pmac_t *chip) /* set headphone-jack detection bit */ switch (chip->model) { case PMAC_AWACS: - chip->hp_stat_mask = 0x04; + chip->hp_stat_mask = pm7500 || pm5500 ? MASK_HDPCONN + : MASK_LOCONN; break; case PMAC_SCREAMER: switch (chip->device_id) { case 0x08: - /* 1 = side jack, 2 = front jack */ - chip->hp_stat_mask = 0x03; + case 0x0B: + chip->hp_stat_mask = imac + ? MASK_LOCONN_IMAC | + MASK_HDPLCONN_IMAC | + MASK_HDPRCONN_IMAC + : MASK_HDPCONN; break; case 0x00: case 0x05: - chip->hp_stat_mask = 0x04; + chip->hp_stat_mask = MASK_LOCONN; break; default: - chip->hp_stat_mask = 0x08; + chip->hp_stat_mask = MASK_HDPCONN; break; } break; @@ -825,19 +970,65 @@ snd_pmac_awacs_init(pmac_t *chip) */ strcpy(chip->card->mixername, "PowerMac AWACS"); - if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers), - snd_pmac_awacs_mixers)) < 0) + err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers), + snd_pmac_awacs_mixers); + if (err < 0) return err; - if (chip->model == PMAC_SCREAMER) + if (beige || g4agp) + ; + else if (chip->model == PMAC_SCREAMER || pm5500) err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2), snd_pmac_screamer_mixers2); - else + else if (!pm7500) err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2), snd_pmac_awacs_mixers2); if (err < 0) return err; - chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_master_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + if (pm5500) { + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_awacs_mixers2_pmac5500), + snd_pmac_awacs_mixers2_pmac5500); + if (err < 0) + return err; + } + if (pm7500) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500), + snd_pmac_awacs_mixers_pmac7500); + else if (pm5500) + err = snd_ctl_add(chip->card, + (master_vol = snd_ctl_new1(snd_pmac_awacs_mixers_pmac5500, + chip))); + else if (beige) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mixers_beige), + snd_pmac_screamer_mixers_beige); + else if (imac || lombard) { + err = snd_ctl_add(chip->card, + (master_vol = snd_ctl_new1(snd_pmac_screamer_mixers_lo, + chip))); + if (err < 0) + return err; + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mixers_imac), + snd_pmac_screamer_mixers_imac); + } else if (g4agp) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mixers_g4agp), + snd_pmac_screamer_mixers_g4agp); + else + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_awacs_mixers_pmac), + snd_pmac_awacs_mixers_pmac); + if (err < 0) + return err; + chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac || g4agp || lombard) + ? &snd_pmac_awacs_master_sw_imac + : pm5500 + ? &snd_pmac_awacs_master_sw_pmac5500 + : &snd_pmac_awacs_master_sw, chip); + err = snd_ctl_add(chip->card, chip->master_sw_ctl); + if (err < 0) return err; #ifdef PMAC_AMP_AVAIL if (chip->mixer_data) { @@ -847,38 +1038,89 @@ snd_pmac_awacs_init(pmac_t *chip) * screamer registers. * in this case, it seems the route C is not used. */ - if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol), - snd_pmac_awacs_amp_vol)) < 0) + err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol), + snd_pmac_awacs_amp_vol); + if (err < 0) return err; /* overwrite */ - chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw, + chip); + err = snd_ctl_add(chip->card, chip->master_sw_ctl); + if (err < 0) return err; - chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) + chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw, + chip); + err = snd_ctl_add(chip->card, chip->speaker_sw_ctl); + if (err < 0) return err; } else #endif /* PMAC_AMP_AVAIL */ { /* route A = headphone, route C = speaker */ - if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol), - snd_pmac_awacs_speaker_vol)) < 0) + err = snd_ctl_add(chip->card, + (speaker_vol = snd_ctl_new1(snd_pmac_awacs_speaker_vol, + chip))); + if (err < 0) return err; - chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_speaker_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) + chip->speaker_sw_ctl = snd_ctl_new1(imac1 + ? &snd_pmac_awacs_speaker_sw_imac1 + : imac2 + ? &snd_pmac_awacs_speaker_sw_imac2 + : &snd_pmac_awacs_speaker_sw, chip); + err = snd_ctl_add(chip->card, chip->speaker_sw_ctl); + if (err < 0) return err; } - if (chip->model == PMAC_SCREAMER) { - if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mic_boost), - snd_pmac_screamer_mic_boost)) < 0) + if (pm5500 || imac || lombard) { + vmaster_sw = snd_ctl_make_virtual_master( + "Master Playback Switch", (unsigned int *) NULL); + err = snd_ctl_add_slave_uncached(vmaster_sw, + chip->master_sw_ctl); + if (err < 0) return err; - } else { - if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost), - snd_pmac_awacs_mic_boost)) < 0) + err = snd_ctl_add_slave_uncached(vmaster_sw, + chip->speaker_sw_ctl); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, vmaster_sw); + if (err < 0) + return err; + vmaster_vol = snd_ctl_make_virtual_master( + "Master Playback Volume", (unsigned int *) NULL); + err = snd_ctl_add_slave(vmaster_vol, master_vol); + if (err < 0) + return err; + err = snd_ctl_add_slave(vmaster_vol, speaker_vol); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, vmaster_vol); + if (err < 0) return err; } + if (beige || g4agp) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mic_boost_beige), + snd_pmac_screamer_mic_boost_beige); + else if (imac) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mic_boost_imac), + snd_pmac_screamer_mic_boost_imac); + else if (chip->model == PMAC_SCREAMER) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mic_boost), + snd_pmac_screamer_mic_boost); + else if (pm7500) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_awacs_mic_boost_pmac7500), + snd_pmac_awacs_mic_boost_pmac7500); + else + err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost), + snd_pmac_awacs_mic_boost); + if (err < 0) + return err; + /* * set lowlevel callbacks */ @@ -888,7 +1130,8 @@ snd_pmac_awacs_init(pmac_t *chip) chip->resume = snd_pmac_awacs_resume; #endif #ifdef PMAC_SUPPORT_AUTOMUTE - if ((err = snd_pmac_add_automute(chip)) < 0) + err = snd_pmac_add_automute(chip); + if (err < 0) return err; chip->detect_headphone = snd_pmac_awacs_detect_headphone; chip->update_automute = snd_pmac_awacs_update_automute; diff --git a/sound/ppc/awacs.h b/sound/ppc/awacs.h index 1b2cc44eda5..c33e6a531cf 100644 --- a/sound/ppc/awacs.h +++ b/sound/ppc/awacs.h @@ -116,6 +116,11 @@ struct awacs_regs { #define MASK_HDMUTE MASK_AMUTE #define SHIFT_HDMUTE 9 #define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */ +#define MASK_PAROUT0 (0x1 << 10) /* Parallel Out (???) */ +#define MASK_PAROUT1 (0x1 << 11) /* Parallel Out (enable speaker) */ +#define SHIFT_PAROUT 10 +#define SHIFT_PAROUT0 10 +#define SHIFT_PAROUT1 11 #define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */ #define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */ @@ -139,7 +144,7 @@ struct awacs_regs { #define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT) /* address 6 */ -#define MASK_MIC_BOOST (0x4) /* screamer mic boost */ +#define MASK_MIC_BOOST (0x4) /* screamer mic boost */ #define SHIFT_MIC_BOOST 2 /* Audio Codec Status Reg Bit Masks */ @@ -152,8 +157,15 @@ struct awacs_regs { #define MASK_REVISION (0xf << 12) /* Revision Number */ #define MASK_MFGID (0xf << 8) /* Mfg. ID */ #define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */ -#define MASK_INPPORT (0xf) /* Input Port */ -#define MASK_HDPCONN 8 /* headphone plugged in */ +#define MASK_INSENSE (0xf) /* port sense bits: */ +#define MASK_HDPCONN 8 /* headphone plugged in */ +#define MASK_LOCONN 4 /* line-out plugged in */ +#define MASK_LICONN 2 /* line-in plugged in */ +#define MASK_MICCONN 1 /* microphone plugged in */ +#define MASK_LICONN_IMAC 8 /* line-in plugged in */ +#define MASK_HDPRCONN_IMAC 4 /* headphone right plugged in */ +#define MASK_HDPLCONN_IMAC 2 /* headphone left plugged in */ +#define MASK_LOCONN_IMAC 1 /* line-out plugged in */ /* Clipping Count Reg Bit Masks */ /* -------- ----- --- --- ----- */ @@ -163,7 +175,8 @@ struct awacs_regs { /* DBDMA ChannelStatus Bit Masks */ /* ----- ------------- --- ----- */ #define MASK_CSERR (0x1 << 7) /* Error */ -#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */ +#define MASK_EOI (0x1 << 6) /* End of Input -- + only for Input Channel */ #define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */ #define MASK_WAIT (0x1) /* Wait */ diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index 31ea7a4c069..0040f048221 100644 --- a/sound/ppc/beep.c +++ b/sound/ppc/beep.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/init.h> @@ -30,23 +29,23 @@ #include <sound/control.h> #include "pmac.h" -struct snd_pmac_beep { - int running; /* boolean */ - int volume; /* mixer volume: 0-100 */ +struct pmac_beep { + int running; /* boolean */ + int volume; /* mixer volume: 0-100 */ int volume_play; /* currently playing volume */ int hz; int nsamples; short *buf; /* allocated wave buffer */ dma_addr_t addr; /* physical address of buffer */ - struct input_dev dev; + struct input_dev *dev; }; /* * stop beep if running */ -void snd_pmac_beep_stop(pmac_t *chip) +void snd_pmac_beep_stop(struct snd_pmac *chip) { - pmac_beep_t *beep = chip->beep; + struct pmac_beep *beep = chip->beep; if (beep && beep->running) { beep->running = 0; snd_pmac_beep_dma_stop(chip); @@ -97,10 +96,11 @@ static short beep_wform[256] = { #define BEEP_BUFLEN 512 #define BEEP_VOLUME 15 /* 0 - 100 */ -static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type, unsigned int code, int hz) +static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type, + unsigned int code, int hz) { - pmac_t *chip; - pmac_beep_t *beep; + struct snd_pmac *chip; + struct pmac_beep *beep; unsigned long flags; int beep_speed = 0; int srate; @@ -117,7 +117,7 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type, unsigne default: return -1; } - chip = dev->private; + chip = input_get_drvdata(dev); if (! chip || (beep = chip->beep) == NULL) return -1; @@ -171,9 +171,8 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type, unsigne * beep volume mixer */ -#define chip_t pmac_t - -static int snd_pmac_info_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_pmac_info_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; @@ -182,25 +181,32 @@ static int snd_pmac_info_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uin return 0; } -static int snd_pmac_get_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_get_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - snd_assert(chip->beep, return -ENXIO); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + if (snd_BUG_ON(!chip->beep)) + return -ENXIO; ucontrol->value.integer.value[0] = chip->beep->volume; return 0; } -static int snd_pmac_put_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - int oval; - snd_assert(chip->beep, return -ENXIO); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int oval, nval; + if (snd_BUG_ON(!chip->beep)) + return -ENXIO; oval = chip->beep->volume; - chip->beep->volume = ucontrol->value.integer.value[0]; + nval = ucontrol->value.integer.value[0]; + if (nval > 100) + return -EINVAL; + chip->beep->volume = nval; return oval != chip->beep->volume; } -static snd_kcontrol_new_t snd_pmac_beep_mixer = { +static struct snd_kcontrol_new snd_pmac_beep_mixer = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Beep Playback Volume", .info = snd_pmac_info_beep, @@ -209,50 +215,68 @@ static snd_kcontrol_new_t snd_pmac_beep_mixer = { }; /* Initialize beep stuff */ -int __init snd_pmac_attach_beep(pmac_t *chip) +int snd_pmac_attach_beep(struct snd_pmac *chip) { - pmac_beep_t *beep; - int err; + struct pmac_beep *beep; + struct input_dev *input_dev; + struct snd_kcontrol *beep_ctl; + void *dmabuf; + int err = -ENOMEM; - beep = kmalloc(sizeof(*beep), GFP_KERNEL); + beep = kzalloc(sizeof(*beep), GFP_KERNEL); if (! beep) return -ENOMEM; - - memset(beep, 0, sizeof(*beep)); - beep->buf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4, - &beep->addr, GFP_KERNEL); - - beep->dev.evbit[0] = BIT(EV_SND); - beep->dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); - beep->dev.event = snd_pmac_beep_event; - beep->dev.private = chip; + dmabuf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4, + &beep->addr, GFP_KERNEL); + input_dev = input_allocate_device(); + if (! dmabuf || ! input_dev) + goto fail1; /* FIXME: set more better values */ - beep->dev.name = "PowerMac Beep"; - beep->dev.phys = "powermac/beep"; - beep->dev.id.bustype = BUS_ADB; - beep->dev.id.vendor = 0x001f; - beep->dev.id.product = 0x0001; - beep->dev.id.version = 0x0100; + input_dev->name = "PowerMac Beep"; + input_dev->phys = "powermac/beep"; + input_dev->id.bustype = BUS_ADB; + input_dev->id.vendor = 0x001f; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + input_dev->evbit[0] = BIT_MASK(EV_SND); + input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + input_dev->event = snd_pmac_beep_event; + input_dev->dev.parent = &chip->pdev->dev; + input_set_drvdata(input_dev, chip); + beep->dev = input_dev; + beep->buf = dmabuf; beep->volume = BEEP_VOLUME; beep->running = 0; - if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_beep_mixer, chip))) < 0) { - kfree(beep->buf); - kfree(beep); - return err; - } + + beep_ctl = snd_ctl_new1(&snd_pmac_beep_mixer, chip); + err = snd_ctl_add(chip->card, beep_ctl); + if (err < 0) + goto fail1; chip->beep = beep; - input_register_device(&beep->dev); - return 0; + err = input_register_device(beep->dev); + if (err) + goto fail2; + + return 0; + + fail2: snd_ctl_remove(chip->card, beep_ctl); + fail1: input_free_device(input_dev); + if (dmabuf) + dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4, + dmabuf, beep->addr); + kfree(beep); + return err; } -void snd_pmac_detach_beep(pmac_t *chip) +void snd_pmac_detach_beep(struct snd_pmac *chip) { if (chip->beep) { - input_unregister_device(&chip->beep->dev); + input_unregister_device(chip->beep->dev); dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4, chip->beep->buf, chip->beep->addr); kfree(chip->beep); diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c index edbc0484e22..cb4f0a5e984 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -19,10 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/init.h> -#include <linux/slab.h> #include <linux/delay.h> #include <sound/core.h> #include "pmac.h" @@ -31,33 +29,33 @@ /* Waits for busy flag to clear */ static inline void -snd_pmac_burgundy_busy_wait(pmac_t *chip) +snd_pmac_burgundy_busy_wait(struct snd_pmac *chip) { int timeout = 50; while ((in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) && timeout--) udelay(1); - if (! timeout) + if (timeout < 0) printk(KERN_DEBUG "burgundy_busy_wait: timeout\n"); } static inline void -snd_pmac_burgundy_extend_wait(pmac_t *chip) +snd_pmac_burgundy_extend_wait(struct snd_pmac *chip) { int timeout; timeout = 50; while (!(in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--) udelay(1); - if (! timeout) + if (timeout < 0) printk(KERN_DEBUG "burgundy_extend_wait: timeout #1\n"); timeout = 50; while ((in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--) udelay(1); - if (! timeout) + if (timeout < 0) printk(KERN_DEBUG "burgundy_extend_wait: timeout #2\n"); } static void -snd_pmac_burgundy_wcw(pmac_t *chip, unsigned addr, unsigned val) +snd_pmac_burgundy_wcw(struct snd_pmac *chip, unsigned addr, unsigned val) { out_le32(&chip->awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); snd_pmac_burgundy_busy_wait(chip); @@ -70,7 +68,7 @@ snd_pmac_burgundy_wcw(pmac_t *chip, unsigned addr, unsigned val) } static unsigned -snd_pmac_burgundy_rcw(pmac_t *chip, unsigned addr) +snd_pmac_burgundy_rcw(struct snd_pmac *chip, unsigned addr) { unsigned val = 0; unsigned long flags; @@ -103,14 +101,15 @@ snd_pmac_burgundy_rcw(pmac_t *chip, unsigned addr) } static void -snd_pmac_burgundy_wcb(pmac_t *chip, unsigned int addr, unsigned int val) +snd_pmac_burgundy_wcb(struct snd_pmac *chip, unsigned int addr, + unsigned int val) { out_le32(&chip->awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); snd_pmac_burgundy_busy_wait(chip); } static unsigned -snd_pmac_burgundy_rcb(pmac_t *chip, unsigned int addr) +snd_pmac_burgundy_rcb(struct snd_pmac *chip, unsigned int addr) { unsigned val = 0; unsigned long flags; @@ -127,14 +126,21 @@ snd_pmac_burgundy_rcb(pmac_t *chip, unsigned int addr) return val; } +#define BASE2ADDR(base) ((base) << 12) +#define ADDR2BASE(addr) ((addr) >> 12) + /* - * Burgundy volume: 0 - 100, stereo + * Burgundy volume: 0 - 100, stereo, word reg */ static void -snd_pmac_burgundy_write_volume(pmac_t *chip, unsigned int address, long *volume, int shift) +snd_pmac_burgundy_write_volume(struct snd_pmac *chip, unsigned int address, + long *volume, int shift) { int hardvolume, lvolume, rvolume; + if (volume[0] < 0 || volume[0] > 100 || + volume[1] < 0 || volume[1] > 100) + return; /* -EINVAL */ lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; @@ -146,7 +152,8 @@ snd_pmac_burgundy_write_volume(pmac_t *chip, unsigned int address, long *volume, } static void -snd_pmac_burgundy_read_volume(pmac_t *chip, unsigned int address, long *volume, int shift) +snd_pmac_burgundy_read_volume(struct snd_pmac *chip, unsigned int address, + long *volume, int shift) { int wvolume; @@ -164,14 +171,8 @@ snd_pmac_burgundy_read_volume(pmac_t *chip, unsigned int address, long *volume, volume[1] = 0; } - -/* - */ - -#define BASE2ADDR(base) ((base) << 12) -#define ADDR2BASE(addr) ((addr) >> 12) - -static int snd_pmac_burgundy_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_pmac_burgundy_info_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; @@ -180,86 +181,120 @@ static int snd_pmac_burgundy_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_ return 0; } -static int snd_pmac_burgundy_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_burgundy_get_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); int shift = (kcontrol->private_value >> 8) & 0xff; - snd_pmac_burgundy_read_volume(chip, addr, ucontrol->value.integer.value, shift); + snd_pmac_burgundy_read_volume(chip, addr, + ucontrol->value.integer.value, shift); return 0; } -static int snd_pmac_burgundy_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_burgundy_put_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); int shift = (kcontrol->private_value >> 8) & 0xff; long nvoices[2]; - snd_pmac_burgundy_write_volume(chip, addr, ucontrol->value.integer.value, shift); + snd_pmac_burgundy_write_volume(chip, addr, + ucontrol->value.integer.value, shift); snd_pmac_burgundy_read_volume(chip, addr, nvoices, shift); return (nvoices[0] != ucontrol->value.integer.value[0] || nvoices[1] != ucontrol->value.integer.value[1]); } -#define BURGUNDY_VOLUME(xname, xindex, addr, shift) \ +#define BURGUNDY_VOLUME_W(xname, xindex, addr, shift) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ .info = snd_pmac_burgundy_info_volume,\ .get = snd_pmac_burgundy_get_volume,\ .put = snd_pmac_burgundy_put_volume,\ .private_value = ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) } -/* lineout/speaker */ +/* + * Burgundy volume: 0 - 100, stereo, 2-byte reg + */ +static void +snd_pmac_burgundy_write_volume_2b(struct snd_pmac *chip, unsigned int address, + long *volume, int off) +{ + int lvolume, rvolume; + + off |= off << 2; + lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; + rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; + + snd_pmac_burgundy_wcb(chip, address + off, lvolume); + snd_pmac_burgundy_wcb(chip, address + off + 0x500, rvolume); +} -static int snd_pmac_burgundy_info_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static void +snd_pmac_burgundy_read_volume_2b(struct snd_pmac *chip, unsigned int address, + long *volume, int off) { - int stereo = (kcontrol->private_value >> 24) & 1; - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = stereo + 1; + volume[0] = snd_pmac_burgundy_rcb(chip, address + off); + if (volume[0] >= BURGUNDY_VOLUME_OFFSET) + volume[0] -= BURGUNDY_VOLUME_OFFSET; + else + volume[0] = 0; + volume[1] = snd_pmac_burgundy_rcb(chip, address + off + 0x100); + if (volume[1] >= BURGUNDY_VOLUME_OFFSET) + volume[1] -= BURGUNDY_VOLUME_OFFSET; + else + volume[1] = 0; +} + +static int snd_pmac_burgundy_info_volume_2b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; + uinfo->value.integer.max = 100; return 0; } -static int snd_pmac_burgundy_get_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_burgundy_get_volume_2b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - int lmask = kcontrol->private_value & 0xff; - int rmask = (kcontrol->private_value >> 8) & 0xff; - int stereo = (kcontrol->private_value >> 24) & 1; - int val = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); - ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; - if (stereo) - ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int off = kcontrol->private_value & 0x300; + snd_pmac_burgundy_read_volume_2b(chip, addr, + ucontrol->value.integer.value, off); return 0; } -static int snd_pmac_burgundy_put_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_burgundy_put_volume_2b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - int lmask = kcontrol->private_value & 0xff; - int rmask = (kcontrol->private_value >> 8) & 0xff; - int stereo = (kcontrol->private_value >> 24) & 1; - int val, oval; - oval = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); - val = oval & ~(lmask | rmask); - if (ucontrol->value.integer.value[0]) - val |= lmask; - if (stereo && ucontrol->value.integer.value[1]) - val |= rmask; - snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, val); - return val != oval; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int off = kcontrol->private_value & 0x300; + long nvoices[2]; + + snd_pmac_burgundy_write_volume_2b(chip, addr, + ucontrol->value.integer.value, off); + snd_pmac_burgundy_read_volume_2b(chip, addr, nvoices, off); + return (nvoices[0] != ucontrol->value.integer.value[0] || + nvoices[1] != ucontrol->value.integer.value[1]); } -#define BURGUNDY_OUTPUT_SWITCH(xname, xindex, lmask, rmask, stereo) \ +#define BURGUNDY_VOLUME_2B(xname, xindex, addr, off) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ - .info = snd_pmac_burgundy_info_switch_out,\ - .get = snd_pmac_burgundy_get_switch_out,\ - .put = snd_pmac_burgundy_put_switch_out,\ - .private_value = ((lmask) | ((rmask) << 8) | ((stereo) << 24)) } + .info = snd_pmac_burgundy_info_volume_2b,\ + .get = snd_pmac_burgundy_get_volume_2b,\ + .put = snd_pmac_burgundy_put_volume_2b,\ + .private_value = ((ADDR2BASE(addr) & 0xff) | ((off) << 8)) } -/* line/speaker output volume */ -static int snd_pmac_burgundy_info_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +/* + * Burgundy gain/attenuation: 0 - 15, mono/stereo, byte reg + */ +static int snd_pmac_burgundy_info_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { int stereo = (kcontrol->private_value >> 24) & 1; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; @@ -269,82 +304,303 @@ static int snd_pmac_burgundy_info_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_e return 0; } -static int snd_pmac_burgundy_get_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_burgundy_get_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); int stereo = (kcontrol->private_value >> 24) & 1; + int atten = (kcontrol->private_value >> 25) & 1; int oval; - oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; + oval = snd_pmac_burgundy_rcb(chip, addr); + if (atten) + oval = ~oval & 0xff; ucontrol->value.integer.value[0] = oval & 0xf; if (stereo) ucontrol->value.integer.value[1] = (oval >> 4) & 0xf; return 0; } -static int snd_pmac_burgundy_put_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_pmac_burgundy_put_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); int stereo = (kcontrol->private_value >> 24) & 1; + int atten = (kcontrol->private_value >> 25) & 1; int oval, val; - oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; + oval = snd_pmac_burgundy_rcb(chip, addr); + if (atten) + oval = ~oval & 0xff; val = ucontrol->value.integer.value[0]; if (stereo) val |= ucontrol->value.integer.value[1] << 4; else val |= ucontrol->value.integer.value[0] << 4; - val = ~val & 0xff; + if (atten) + val = ~val & 0xff; snd_pmac_burgundy_wcb(chip, addr, val); return val != oval; } -#define BURGUNDY_OUTPUT_VOLUME(xname, xindex, addr, stereo) \ +#define BURGUNDY_VOLUME_B(xname, xindex, addr, stereo, atten) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ - .info = snd_pmac_burgundy_info_volume_out,\ - .get = snd_pmac_burgundy_get_volume_out,\ - .put = snd_pmac_burgundy_put_volume_out,\ - .private_value = (ADDR2BASE(addr) | ((stereo) << 24)) } - -static snd_kcontrol_new_t snd_pmac_burgundy_mixers[] __initdata = { - BURGUNDY_VOLUME("Master Playback Volume", 0, MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8), - BURGUNDY_VOLUME("Line Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLLINE, 16), - BURGUNDY_VOLUME("CD Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLCD, 16), - BURGUNDY_VOLUME("Mic Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLMIC, 16), - BURGUNDY_OUTPUT_VOLUME("PC Speaker Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENHP, 0), - /*BURGUNDY_OUTPUT_VOLUME("PCM Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1),*/ - BURGUNDY_OUTPUT_VOLUME("Headphone Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1), -}; -static snd_kcontrol_new_t snd_pmac_burgundy_master_sw __initdata = -BURGUNDY_OUTPUT_SWITCH("Headphone Playback Switch", 0, BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); -static snd_kcontrol_new_t snd_pmac_burgundy_speaker_sw __initdata = -BURGUNDY_OUTPUT_SWITCH("PC Speaker Playback Switch", 0, BURGUNDY_OUTPUT_INTERN, 0, 0); + .info = snd_pmac_burgundy_info_gain,\ + .get = snd_pmac_burgundy_get_gain,\ + .put = snd_pmac_burgundy_put_gain,\ + .private_value = (ADDR2BASE(addr) | ((stereo) << 24) | ((atten) << 25)) } + +/* + * Burgundy switch: 0/1, mono/stereo, word reg + */ +static int snd_pmac_burgundy_info_switch_w(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int stereo = (kcontrol->private_value >> 24) & 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_pmac_burgundy_get_switch_w(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); + int lmask = 1 << (kcontrol->private_value & 0xff); + int rmask = 1 << ((kcontrol->private_value >> 8) & 0xff); + int stereo = (kcontrol->private_value >> 24) & 1; + int val = snd_pmac_burgundy_rcw(chip, addr); + ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; + if (stereo) + ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; + return 0; +} + +static int snd_pmac_burgundy_put_switch_w(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); + int lmask = 1 << (kcontrol->private_value & 0xff); + int rmask = 1 << ((kcontrol->private_value >> 8) & 0xff); + int stereo = (kcontrol->private_value >> 24) & 1; + int val, oval; + oval = snd_pmac_burgundy_rcw(chip, addr); + val = oval & ~(lmask | (stereo ? rmask : 0)); + if (ucontrol->value.integer.value[0]) + val |= lmask; + if (stereo && ucontrol->value.integer.value[1]) + val |= rmask; + snd_pmac_burgundy_wcw(chip, addr, val); + return val != oval; +} + +#define BURGUNDY_SWITCH_W(xname, xindex, addr, lbit, rbit, stereo) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ + .info = snd_pmac_burgundy_info_switch_w,\ + .get = snd_pmac_burgundy_get_switch_w,\ + .put = snd_pmac_burgundy_put_switch_w,\ + .private_value = ((lbit) | ((rbit) << 8)\ + | (ADDR2BASE(addr) << 16) | ((stereo) << 24)) } + +/* + * Burgundy switch: 0/1, mono/stereo, byte reg, bit mask + */ +static int snd_pmac_burgundy_info_switch_b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int stereo = (kcontrol->private_value >> 24) & 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_pmac_burgundy_get_switch_b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); + int lmask = kcontrol->private_value & 0xff; + int rmask = (kcontrol->private_value >> 8) & 0xff; + int stereo = (kcontrol->private_value >> 24) & 1; + int val = snd_pmac_burgundy_rcb(chip, addr); + ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; + if (stereo) + ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; + return 0; +} + +static int snd_pmac_burgundy_put_switch_b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); + int lmask = kcontrol->private_value & 0xff; + int rmask = (kcontrol->private_value >> 8) & 0xff; + int stereo = (kcontrol->private_value >> 24) & 1; + int val, oval; + oval = snd_pmac_burgundy_rcb(chip, addr); + val = oval & ~(lmask | rmask); + if (ucontrol->value.integer.value[0]) + val |= lmask; + if (stereo && ucontrol->value.integer.value[1]) + val |= rmask; + snd_pmac_burgundy_wcb(chip, addr, val); + return val != oval; +} + +#define BURGUNDY_SWITCH_B(xname, xindex, addr, lmask, rmask, stereo) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ + .info = snd_pmac_burgundy_info_switch_b,\ + .get = snd_pmac_burgundy_get_switch_b,\ + .put = snd_pmac_burgundy_put_switch_b,\ + .private_value = ((lmask) | ((rmask) << 8)\ + | (ADDR2BASE(addr) << 16) | ((stereo) << 24)) } + +/* + * Burgundy mixers + */ +static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] = { + BURGUNDY_VOLUME_W("Master Playback Volume", 0, + MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8), + BURGUNDY_VOLUME_W("CD Capture Volume", 0, + MASK_ADDR_BURGUNDY_VOLCD, 16), + BURGUNDY_VOLUME_2B("Input Capture Volume", 0, + MASK_ADDR_BURGUNDY_VOLMIX01, 2), + BURGUNDY_VOLUME_2B("Mixer Playback Volume", 0, + MASK_ADDR_BURGUNDY_VOLMIX23, 0), + BURGUNDY_VOLUME_B("CD Gain Capture Volume", 0, + MASK_ADDR_BURGUNDY_GAINCD, 1, 0), + BURGUNDY_SWITCH_W("Master Capture Switch", 0, + MASK_ADDR_BURGUNDY_OUTPUTENABLES, 24, 0, 0), + BURGUNDY_SWITCH_W("CD Capture Switch", 0, + MASK_ADDR_BURGUNDY_CAPTURESELECTS, 0, 16, 1), + BURGUNDY_SWITCH_W("CD Playback Switch", 0, + MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 0, 16, 1), +/* BURGUNDY_SWITCH_W("Loop Capture Switch", 0, + * MASK_ADDR_BURGUNDY_CAPTURESELECTS, 8, 24, 1), + * BURGUNDY_SWITCH_B("Mixer out Capture Switch", 0, + * MASK_ADDR_BURGUNDY_HOSTIFAD, 0x02, 0, 0), + * BURGUNDY_SWITCH_B("Mixer Capture Switch", 0, + * MASK_ADDR_BURGUNDY_HOSTIFAD, 0x01, 0, 0), + * BURGUNDY_SWITCH_B("PCM out Capture Switch", 0, + * MASK_ADDR_BURGUNDY_HOSTIFEH, 0x02, 0, 0), + */ BURGUNDY_SWITCH_B("PCM Capture Switch", 0, + MASK_ADDR_BURGUNDY_HOSTIFEH, 0x01, 0, 0) +}; +static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] = { + BURGUNDY_VOLUME_W("Line in Capture Volume", 0, + MASK_ADDR_BURGUNDY_VOLLINE, 16), + BURGUNDY_VOLUME_W("Mic Capture Volume", 0, + MASK_ADDR_BURGUNDY_VOLMIC, 16), + BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0, + MASK_ADDR_BURGUNDY_GAINLINE, 1, 0), + BURGUNDY_VOLUME_B("Mic Gain Capture Volume", 0, + MASK_ADDR_BURGUNDY_GAINMIC, 1, 0), + BURGUNDY_VOLUME_B("Speaker Playback Volume", 0, + MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1), + BURGUNDY_VOLUME_B("Line out Playback Volume", 0, + MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1, 1), + BURGUNDY_VOLUME_B("Headphone Playback Volume", 0, + MASK_ADDR_BURGUNDY_ATTENHP, 1, 1), + BURGUNDY_SWITCH_W("Line in Capture Switch", 0, + MASK_ADDR_BURGUNDY_CAPTURESELECTS, 1, 17, 1), + BURGUNDY_SWITCH_W("Mic Capture Switch", 0, + MASK_ADDR_BURGUNDY_CAPTURESELECTS, 2, 18, 1), + BURGUNDY_SWITCH_W("Line in Playback Switch", 0, + MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 1, 17, 1), + BURGUNDY_SWITCH_W("Mic Playback Switch", 0, + MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 2, 18, 1), + BURGUNDY_SWITCH_B("Mic Boost Capture Switch", 0, + MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) +}; +static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] = { + BURGUNDY_VOLUME_W("Line in Capture Volume", 0, + MASK_ADDR_BURGUNDY_VOLMIC, 16), + BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0, + MASK_ADDR_BURGUNDY_GAINMIC, 1, 0), + BURGUNDY_VOLUME_B("Speaker Playback Volume", 0, + MASK_ADDR_BURGUNDY_ATTENMONO, 0, 1), + BURGUNDY_VOLUME_B("Line out Playback Volume", 0, + MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1), + BURGUNDY_SWITCH_W("Line in Capture Switch", 0, + MASK_ADDR_BURGUNDY_CAPTURESELECTS, 2, 18, 1), + BURGUNDY_SWITCH_W("Line in Playback Switch", 0, + MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 2, 18, 1), +/* BURGUNDY_SWITCH_B("Line in Boost Capture Switch", 0, + * MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) */ +}; +static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac = +BURGUNDY_SWITCH_B("Master Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_OUTPUT_LEFT | BURGUNDY_LINEOUT_LEFT | BURGUNDY_HP_LEFT, + BURGUNDY_OUTPUT_RIGHT | BURGUNDY_LINEOUT_RIGHT | BURGUNDY_HP_RIGHT, 1); +static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac = +BURGUNDY_SWITCH_B("Master Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_OUTPUT_INTERN + | BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); +static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac = +BURGUNDY_SWITCH_B("Speaker Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); +static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac = +BURGUNDY_SWITCH_B("Speaker Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_OUTPUT_INTERN, 0, 0); +static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac = +BURGUNDY_SWITCH_B("Line out Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_LINEOUT_LEFT, BURGUNDY_LINEOUT_RIGHT, 1); +static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac = +BURGUNDY_SWITCH_B("Line out Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); +static struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac = +BURGUNDY_SWITCH_B("Headphone Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_HP_LEFT, BURGUNDY_HP_RIGHT, 1); #ifdef PMAC_SUPPORT_AUTOMUTE /* * auto-mute stuffs */ -static int snd_pmac_burgundy_detect_headphone(pmac_t *chip) +static int snd_pmac_burgundy_detect_headphone(struct snd_pmac *chip) { return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0; } -static void snd_pmac_burgundy_update_automute(pmac_t *chip, int do_notify) +static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_notify) { if (chip->auto_mute) { + int imac = of_machine_is_compatible("iMac"); int reg, oreg; - reg = oreg = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); - reg &= ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT | BURGUNDY_OUTPUT_INTERN); + reg = oreg = snd_pmac_burgundy_rcb(chip, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + reg &= imac ? ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT + | BURGUNDY_HP_LEFT | BURGUNDY_HP_RIGHT) + : ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT + | BURGUNDY_OUTPUT_INTERN); if (snd_pmac_burgundy_detect_headphone(chip)) - reg |= BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT; + reg |= imac ? (BURGUNDY_HP_LEFT | BURGUNDY_HP_RIGHT) + : (BURGUNDY_OUTPUT_LEFT + | BURGUNDY_OUTPUT_RIGHT); else - reg |= BURGUNDY_OUTPUT_INTERN; + reg |= imac ? (BURGUNDY_OUTPUT_LEFT + | BURGUNDY_OUTPUT_RIGHT) + : (BURGUNDY_OUTPUT_INTERN); if (do_notify && reg == oreg) return; - snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, reg); + snd_pmac_burgundy_wcb(chip, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, reg); if (do_notify) { snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_sw_ctl->id); @@ -361,8 +617,9 @@ static void snd_pmac_burgundy_update_automute(pmac_t *chip, int do_notify) /* * initialize burgundy */ -int __init snd_pmac_burgundy_init(pmac_t *chip) +int snd_pmac_burgundy_init(struct snd_pmac *chip) { + int imac = of_machine_is_compatible("iMac"); int i, err; /* Checks to see the chip is alive and kicking */ @@ -371,7 +628,7 @@ int __init snd_pmac_burgundy_init(pmac_t *chip) return 1; } - snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES, + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES, DEF_BURGUNDY_OUTPUTENABLES); snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, DEF_BURGUNDY_MORE_OUTPUTENABLES); @@ -381,7 +638,8 @@ int __init snd_pmac_burgundy_init(pmac_t *chip) snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL21, DEF_BURGUNDY_INPSEL21); snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL3, - DEF_BURGUNDY_INPSEL3); + imac ? DEF_BURGUNDY_INPSEL3_IMAC + : DEF_BURGUNDY_INPSEL3_PMAC); snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINCD, DEF_BURGUNDY_GAINCD); snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINLINE, @@ -407,27 +665,62 @@ int __init snd_pmac_burgundy_init(pmac_t *chip) snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLMIC, DEF_BURGUNDY_VOLMIC); - if (chip->hp_stat_mask == 0) + if (chip->hp_stat_mask == 0) { /* set headphone-jack detection bit */ - chip->hp_stat_mask = 0x04; - + if (imac) + chip->hp_stat_mask = BURGUNDY_HPDETECT_IMAC_UPPER + | BURGUNDY_HPDETECT_IMAC_LOWER + | BURGUNDY_HPDETECT_IMAC_SIDE; + else + chip->hp_stat_mask = BURGUNDY_HPDETECT_PMAC_BACK; + } /* * build burgundy mixers */ strcpy(chip->card->mixername, "PowerMac Burgundy"); for (i = 0; i < ARRAY_SIZE(snd_pmac_burgundy_mixers); i++) { - if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip))) < 0) + err = snd_ctl_add(chip->card, + snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip)); + if (err < 0) + return err; + } + for (i = 0; i < (imac ? ARRAY_SIZE(snd_pmac_burgundy_mixers_imac) + : ARRAY_SIZE(snd_pmac_burgundy_mixers_pmac)); i++) { + err = snd_ctl_add(chip->card, + snd_ctl_new1(imac ? &snd_pmac_burgundy_mixers_imac[i] + : &snd_pmac_burgundy_mixers_pmac[i], chip)); + if (err < 0) return err; } - chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_burgundy_master_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + chip->master_sw_ctl = snd_ctl_new1(imac + ? &snd_pmac_burgundy_master_sw_imac + : &snd_pmac_burgundy_master_sw_pmac, chip); + err = snd_ctl_add(chip->card, chip->master_sw_ctl); + if (err < 0) + return err; + chip->master_sw_ctl = snd_ctl_new1(imac + ? &snd_pmac_burgundy_line_sw_imac + : &snd_pmac_burgundy_line_sw_pmac, chip); + err = snd_ctl_add(chip->card, chip->master_sw_ctl); + if (err < 0) return err; - chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_burgundy_speaker_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) + if (imac) { + chip->master_sw_ctl = snd_ctl_new1( + &snd_pmac_burgundy_hp_sw_imac, chip); + err = snd_ctl_add(chip->card, chip->master_sw_ctl); + if (err < 0) + return err; + } + chip->speaker_sw_ctl = snd_ctl_new1(imac + ? &snd_pmac_burgundy_speaker_sw_imac + : &snd_pmac_burgundy_speaker_sw_pmac, chip); + err = snd_ctl_add(chip->card, chip->speaker_sw_ctl); + if (err < 0) return err; #ifdef PMAC_SUPPORT_AUTOMUTE - if ((err = snd_pmac_add_automute(chip)) < 0) + err = snd_pmac_add_automute(chip); + if (err < 0) return err; chip->detect_headphone = snd_pmac_burgundy_detect_headphone; diff --git a/sound/ppc/burgundy.h b/sound/ppc/burgundy.h index ebb457a8342..7a7f9cf3d29 100644 --- a/sound/ppc/burgundy.h +++ b/sound/ppc/burgundy.h @@ -22,6 +22,7 @@ #ifndef __BURGUNDY_H #define __BURGUNDY_H +#define MASK_ADDR_BURGUNDY_INPBOOST (0x10 << 12) #define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12) #define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12) @@ -35,7 +36,10 @@ #define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12) #define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12) +#define MASK_ADDR_BURGUNDY_CAPTURESELECTS (0x2A << 12) #define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12) +#define MASK_ADDR_BURGUNDY_VOLMIX01 (0x2D << 12) +#define MASK_ADDR_BURGUNDY_VOLMIX23 (0x2E << 12) #define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12) #define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12) @@ -45,6 +49,10 @@ #define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12) #define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12) #define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12) +#define MASK_ADDR_BURGUNDY_ATTENMONO (0x65 << 12) + +#define MASK_ADDR_BURGUNDY_HOSTIFAD (0x78 << 12) +#define MASK_ADDR_BURGUNDY_HOSTIFEH (0x79 << 12) #define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1) #define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2) @@ -59,21 +67,22 @@ /* These are all default values for the burgundy */ #define DEF_BURGUNDY_INPSEL21 (0xAA) -#define DEF_BURGUNDY_INPSEL3 (0x0A) +#define DEF_BURGUNDY_INPSEL3_IMAC (0x0A) +#define DEF_BURGUNDY_INPSEL3_PMAC (0x05) #define DEF_BURGUNDY_GAINCD (0x33) #define DEF_BURGUNDY_GAINLINE (0x44) #define DEF_BURGUNDY_GAINMIC (0x44) #define DEF_BURGUNDY_GAINMODEM (0x06) -/* Remember: lowest volume here is 0x9b */ +/* Remember: lowest volume here is 0x9B (155) */ #define DEF_BURGUNDY_VOLCD (0xCCCCCCCC) #define DEF_BURGUNDY_VOLLINE (0x00000000) #define DEF_BURGUNDY_VOLMIC (0x00000000) #define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC) -#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f) -#define DEF_BURGUNDY_OUTPUTENABLES (0x0A) +#define DEF_BURGUNDY_OUTPUTSELECTS (0x010F010F) +#define DEF_BURGUNDY_OUTPUTENABLES (0x0100000A) /* #define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) */ /* too loud */ #define DEF_BURGUNDY_MASTER_VOLUME (0xDDDDDDDD) @@ -84,12 +93,22 @@ #define DEF_BURGUNDY_ATTENLINEOUT (0xCC) #define DEF_BURGUNDY_ATTENHP (0xCC) -/* OUTPUTENABLES bits */ +/* MORE_OUTPUTENABLES bits */ #define BURGUNDY_OUTPUT_LEFT 0x02 #define BURGUNDY_OUTPUT_RIGHT 0x04 +#define BURGUNDY_LINEOUT_LEFT 0x08 +#define BURGUNDY_LINEOUT_RIGHT 0x10 +#define BURGUNDY_HP_LEFT 0x20 +#define BURGUNDY_HP_RIGHT 0x40 #define BURGUNDY_OUTPUT_INTERN 0x80 -/* volume offset */ +/* Headphone detection bits */ +#define BURGUNDY_HPDETECT_PMAC_BACK 0x04 +#define BURGUNDY_HPDETECT_IMAC_SIDE 0x04 +#define BURGUNDY_HPDETECT_IMAC_UPPER 0x08 +#define BURGUNDY_HPDETECT_IMAC_LOWER 0x01 + +/* Volume offset */ #define BURGUNDY_VOLUME_OFFSET 155 #endif /* __BURGUNDY_H */ diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c index a737f298e77..b86526223e4 100644 --- a/sound/ppc/daca.c +++ b/sound/ppc/daca.c @@ -19,10 +19,8 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/i2c.h> -#include <linux/i2c-dev.h> #include <linux/kmod.h> #include <linux/slab.h> #include <sound/core.h> @@ -40,18 +38,18 @@ #define DACA_VOL_MAX 0x38 -typedef struct pmac_daca_t { - pmac_keywest_t i2c; +struct pmac_daca { + struct pmac_keywest i2c; int left_vol, right_vol; unsigned int deemphasis : 1; unsigned int amp_on : 1; -} pmac_daca_t; +}; /* * initialize / detect DACA */ -static int daca_init_client(pmac_keywest_t *i2c) +static int daca_init_client(struct pmac_keywest *i2c) { unsigned short wdata = 0x00; /* SR: no swap, 1bit delay, 32-48kHz */ @@ -66,7 +64,7 @@ static int daca_init_client(pmac_keywest_t *i2c) /* * update volume */ -static int daca_set_volume(pmac_daca_t *mix) +static int daca_set_volume(struct pmac_daca *mix) { unsigned char data[2]; @@ -84,7 +82,7 @@ static int daca_set_volume(pmac_daca_t *mix) data[1] |= mix->deemphasis ? 0x40 : 0; if (i2c_smbus_write_block_data(mix->i2c.client, DACA_REG_AVOL, 2, data) < 0) { - snd_printk("failed to set volume \n"); + snd_printk(KERN_ERR "failed to set volume \n"); return -EINVAL; } return 0; @@ -92,43 +90,39 @@ static int daca_set_volume(pmac_daca_t *mix) /* deemphasis switch */ -static int daca_info_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define daca_info_deemphasis snd_ctl_boolean_mono_info -static int daca_get_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int daca_get_deemphasis(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_daca_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_daca *mix; if (! (mix = chip->mixer_data)) return -ENODEV; ucontrol->value.integer.value[0] = mix->deemphasis ? 1 : 0; return 0; } -static int daca_put_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int daca_put_deemphasis(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_daca_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_daca *mix; int change; if (! (mix = chip->mixer_data)) return -ENODEV; change = mix->deemphasis != ucontrol->value.integer.value[0]; if (change) { - mix->deemphasis = ucontrol->value.integer.value[0]; + mix->deemphasis = !!ucontrol->value.integer.value[0]; daca_set_volume(mix); } return change; } /* output volume */ -static int daca_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int daca_info_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; @@ -137,10 +131,11 @@ static int daca_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo return 0; } -static int daca_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int daca_get_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_daca_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_daca *mix; if (! (mix = chip->mixer_data)) return -ENODEV; ucontrol->value.integer.value[0] = mix->left_vol; @@ -148,19 +143,25 @@ static int daca_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucont return 0; } -static int daca_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int daca_put_volume(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_daca_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_daca *mix; + unsigned int vol[2]; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->left_vol != ucontrol->value.integer.value[0] || - mix->right_vol != ucontrol->value.integer.value[1]; + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] > DACA_VOL_MAX || vol[1] > DACA_VOL_MAX) + return -EINVAL; + change = mix->left_vol != vol[0] || + mix->right_vol != vol[1]; if (change) { - mix->left_vol = ucontrol->value.integer.value[0]; - mix->right_vol = ucontrol->value.integer.value[1]; + mix->left_vol = vol[0]; + mix->right_vol = vol[1]; daca_set_volume(mix); } return change; @@ -169,34 +170,36 @@ static int daca_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucont /* amplifier switch */ #define daca_info_amp daca_info_deemphasis -static int daca_get_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int daca_get_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_daca_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_daca *mix; if (! (mix = chip->mixer_data)) return -ENODEV; ucontrol->value.integer.value[0] = mix->amp_on ? 1 : 0; return 0; } -static int daca_put_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int daca_put_amp(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); - pmac_daca_t *mix; + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + struct pmac_daca *mix; int change; if (! (mix = chip->mixer_data)) return -ENODEV; change = mix->amp_on != ucontrol->value.integer.value[0]; if (change) { - mix->amp_on = ucontrol->value.integer.value[0]; + mix->amp_on = !!ucontrol->value.integer.value[0]; i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG, mix->amp_on ? 0x05 : 0x04); } return change; } -static snd_kcontrol_new_t daca_mixers[] = { +static struct snd_kcontrol_new daca_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Deemphasis Switch", .info = daca_info_deemphasis, @@ -219,9 +222,9 @@ static snd_kcontrol_new_t daca_mixers[] = { #ifdef CONFIG_PM -static void daca_resume(pmac_t *chip) +static void daca_resume(struct snd_pmac *chip) { - pmac_daca_t *mix = chip->mixer_data; + struct pmac_daca *mix = chip->mixer_data; i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_SR, 0x08); i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG, mix->amp_on ? 0x05 : 0x04); @@ -230,9 +233,9 @@ static void daca_resume(pmac_t *chip) #endif /* CONFIG_PM */ -static void daca_cleanup(pmac_t *chip) +static void daca_cleanup(struct snd_pmac *chip) { - pmac_daca_t *mix = chip->mixer_data; + struct pmac_daca *mix = chip->mixer_data; if (! mix) return; snd_pmac_keywest_cleanup(&mix->i2c); @@ -241,20 +244,16 @@ static void daca_cleanup(pmac_t *chip) } /* exported */ -int __init snd_pmac_daca_init(pmac_t *chip) +int snd_pmac_daca_init(struct snd_pmac *chip) { int i, err; - pmac_daca_t *mix; + struct pmac_daca *mix; -#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)); chip->mixer_data = mix; chip->mixer_free = daca_cleanup; mix->amp_on = 1; /* default on */ diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c index df073a05b5d..0d1c27e911b 100644 --- a/sound/ppc/keywest.c +++ b/sound/ppc/keywest.c @@ -19,12 +19,9 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/delay.h> -#include <linux/i2c-dev.h> -#include <linux/slab.h> #include <sound/core.h> #include "pmac.h" @@ -32,81 +29,85 @@ * we have to keep a static variable here since i2c attach_adapter * callback cannot pass a private data. */ -static pmac_keywest_t *keywest_ctx; +static struct pmac_keywest *keywest_ctx; -#define I2C_DRIVERID_KEYWEST 0xFEBA - -static int keywest_attach_adapter(struct i2c_adapter *adapter); -static int keywest_detach_client(struct i2c_client *client); - -struct i2c_driver keywest_driver = { - .name = "PMac Keywest Audio", - .id = I2C_DRIVERID_KEYWEST, - .flags = I2C_DF_NOTIFY, - .attach_adapter = &keywest_attach_adapter, - .detach_client = &keywest_detach_client, -}; - - -#ifndef i2c_device_name -#define i2c_device_name(x) ((x)->name) -#endif +static int keywest_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + i2c_set_clientdata(client, keywest_ctx); + return 0; +} +/* + * This is kind of a hack, best would be to turn powermac to fixed i2c + * bus numbers and declare the sound device as part of platform + * initialization + */ static int keywest_attach_adapter(struct i2c_adapter *adapter) { - int err; - struct i2c_client *new_client; + struct i2c_board_info info; if (! keywest_ctx) return -EINVAL; - if (strncmp(i2c_device_name(adapter), "mac-io", 6)) + if (strncmp(adapter->name, "mac-io", 6)) return 0; /* ignored */ - new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (! new_client) - return -ENOMEM; - - memset(new_client, 0, sizeof(*new_client)); - new_client->addr = keywest_ctx->addr; - i2c_set_clientdata(new_client, keywest_ctx); - new_client->adapter = adapter; - new_client->driver = &keywest_driver; - new_client->flags = 0; - - strcpy(i2c_device_name(new_client), keywest_ctx->name); - keywest_ctx->client = new_client; - - /* Tell the i2c layer a new client has arrived */ - if (i2c_attach_client(new_client)) { - snd_printk(KERN_ERR "tumbler: cannot attach i2c client\n"); - err = -ENODEV; - goto __err; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "keywest", I2C_NAME_SIZE); + info.addr = keywest_ctx->addr; + keywest_ctx->client = i2c_new_device(adapter, &info); + if (!keywest_ctx->client) + return -ENODEV; + /* + * We know the driver is already loaded, so the device should be + * already bound. If not it means binding failed, and then there + * is no point in keeping the device instantiated. + */ + if (!keywest_ctx->client->dev.driver) { + i2c_unregister_device(keywest_ctx->client); + keywest_ctx->client = NULL; + return -ENODEV; } - + + /* + * Let i2c-core delete that device on driver removal. + * This is safe because i2c-core holds the core_lock mutex for us. + */ + list_add_tail(&keywest_ctx->client->detected, + &to_i2c_driver(keywest_ctx->client->dev.driver)->clients); return 0; - - __err: - kfree(new_client); - keywest_ctx->client = NULL; - return err; } -static int keywest_detach_client(struct i2c_client *client) +static int keywest_remove(struct i2c_client *client) { if (! keywest_ctx) return 0; if (client == keywest_ctx->client) keywest_ctx->client = NULL; - i2c_detach_client(client); - kfree(client); return 0; } + +static const struct i2c_device_id keywest_i2c_id[] = { + { "keywest", 0 }, + { } +}; + +static struct i2c_driver keywest_driver = { + .driver = { + .name = "PMac Keywest Audio", + }, + .attach_adapter = keywest_attach_adapter, + .probe = keywest_probe, + .remove = keywest_remove, + .id_table = keywest_i2c_id, +}; + /* exported */ -void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c) +void snd_pmac_keywest_cleanup(struct pmac_keywest *i2c) { if (keywest_ctx && keywest_ctx == i2c) { i2c_del_driver(&keywest_driver); @@ -114,10 +115,13 @@ void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c) } } -int __init snd_pmac_tumbler_post_init(void) +int snd_pmac_tumbler_post_init(void) { int err; + if (!keywest_ctx || !keywest_ctx->client) + return -ENXIO; + if ((err = keywest_ctx->init_client(keywest_ctx)) < 0) { snd_printk(KERN_ERR "tumbler: %i :cannot initialize the MCS\n", err); return err; @@ -126,7 +130,7 @@ int __init snd_pmac_tumbler_post_init(void) } /* exported */ -int __init snd_pmac_keywest_init(pmac_keywest_t *i2c) +int snd_pmac_keywest_init(struct pmac_keywest *i2c) { int err; diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 844d76152ea..7a43c0c3831 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -20,7 +20,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/init.h> @@ -29,6 +28,8 @@ #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/dma-mapping.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <sound/core.h> #include "pmac.h" #include <sound/pcm_params.h> @@ -36,14 +37,6 @@ #include <asm/pci-bridge.h> -#ifdef CONFIG_PM -static int snd_pmac_register_sleep_notifier(pmac_t *chip); -static int snd_pmac_unregister_sleep_notifier(pmac_t *chip); -static int snd_pmac_suspend(snd_card_t *card, pm_message_t state); -static int snd_pmac_resume(snd_card_t *card); -#endif - - /* fixed frequency table for awacs, screamer, burgundy, DACA (44100 max) */ static int awacs_freqs[8] = { 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 @@ -53,10 +46,22 @@ static int tumbler_freqs[1] = { 44100 }; + +/* + * we will allocate a single 'emergency' dbdma cmd block to use if the + * tx status comes up "DEAD". This happens on some PowerComputing Pmac + * clones, either owing to a bug in dbdma or some interaction between + * IDE and sound. However, this measure would deal with DEAD status if + * it appeared elsewhere. + */ +static struct pmac_dbdma emergency_dbdma; +static int emergency_in_use; + + /* * allocate DBDMA command arrays */ -static int snd_pmac_dbdma_alloc(pmac_t *chip, pmac_dbdma_t *rec, int size) +static int snd_pmac_dbdma_alloc(struct snd_pmac *chip, struct pmac_dbdma *rec, int size) { unsigned int rsize = sizeof(struct dbdma_cmd) * (size + 1); @@ -72,9 +77,9 @@ static int snd_pmac_dbdma_alloc(pmac_t *chip, pmac_dbdma_t *rec, int size) return 0; } -static void snd_pmac_dbdma_free(pmac_t *chip, pmac_dbdma_t *rec) +static void snd_pmac_dbdma_free(struct snd_pmac *chip, struct pmac_dbdma *rec) { - if (rec) { + if (rec->space) { unsigned int rsize = sizeof(struct dbdma_cmd) * (rec->size + 1); dma_free_coherent(&chip->pdev->dev, rsize, rec->space, rec->dma_base); @@ -90,7 +95,7 @@ static void snd_pmac_dbdma_free(pmac_t *chip, pmac_dbdma_t *rec) * look up frequency table */ -unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate) +unsigned int snd_pmac_rate_index(struct snd_pmac *chip, struct pmac_stream *rec, unsigned int rate) { int i, ok, found; @@ -119,8 +124,8 @@ static inline int another_stream(int stream) /* * allocate buffers */ -static int snd_pmac_pcm_hw_params(snd_pcm_substream_t *subs, - snd_pcm_hw_params_t *hw_params) +static int snd_pmac_pcm_hw_params(struct snd_pcm_substream *subs, + struct snd_pcm_hw_params *hw_params) { return snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw_params)); } @@ -128,7 +133,7 @@ static int snd_pmac_pcm_hw_params(snd_pcm_substream_t *subs, /* * release buffers */ -static int snd_pmac_pcm_hw_free(snd_pcm_substream_t *subs) +static int snd_pmac_pcm_hw_free(struct snd_pcm_substream *subs) { snd_pcm_lib_free_pages(subs); return 0; @@ -137,7 +142,7 @@ static int snd_pmac_pcm_hw_free(snd_pcm_substream_t *subs) /* * get a stream of the opposite direction */ -static pmac_stream_t *snd_pmac_get_stream(pmac_t *chip, int stream) +static struct pmac_stream *snd_pmac_get_stream(struct snd_pmac *chip, int stream) { switch (stream) { case SNDRV_PCM_STREAM_PLAYBACK: @@ -154,7 +159,7 @@ static pmac_stream_t *snd_pmac_get_stream(pmac_t *chip, int stream) * wait while run status is on */ static inline void -snd_pmac_wait_ack(pmac_stream_t *rec) +snd_pmac_wait_ack(struct pmac_stream *rec) { int timeout = 50000; while ((in_le32(&rec->dma->status) & RUN) && timeout-- > 0) @@ -165,7 +170,7 @@ snd_pmac_wait_ack(pmac_stream_t *rec) * set the format and rate to the chip. * call the lowlevel function if defined (e.g. for AWACS). */ -static void snd_pmac_pcm_set_format(pmac_t *chip) +static void snd_pmac_pcm_set_format(struct snd_pmac *chip) { /* set up frequency and format */ out_le32(&chip->awacs->control, chip->control_mask | (chip->rate_index << 8)); @@ -177,7 +182,7 @@ static void snd_pmac_pcm_set_format(pmac_t *chip) /* * stop the DMA transfer */ -static inline void snd_pmac_dma_stop(pmac_stream_t *rec) +static inline void snd_pmac_dma_stop(struct pmac_stream *rec) { out_le32(&rec->dma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); snd_pmac_wait_ack(rec); @@ -186,7 +191,7 @@ static inline void snd_pmac_dma_stop(pmac_stream_t *rec) /* * set the command pointer address */ -static inline void snd_pmac_dma_set_command(pmac_stream_t *rec, pmac_dbdma_t *cmd) +static inline void snd_pmac_dma_set_command(struct pmac_stream *rec, struct pmac_dbdma *cmd) { out_le32(&rec->dma->cmdptr, cmd->addr); } @@ -194,7 +199,7 @@ static inline void snd_pmac_dma_set_command(pmac_stream_t *rec, pmac_dbdma_t *cm /* * start the DMA */ -static inline void snd_pmac_dma_run(pmac_stream_t *rec, int status) +static inline void snd_pmac_dma_run(struct pmac_stream *rec, int status) { out_le32(&rec->dma->control, status | (status << 16)); } @@ -203,15 +208,15 @@ static inline void snd_pmac_dma_run(pmac_stream_t *rec, int status) /* * prepare playback/capture stream */ -static int snd_pmac_pcm_prepare(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec, struct snd_pcm_substream *subs) { int i; volatile struct dbdma_cmd __iomem *cp; - snd_pcm_runtime_t *runtime = subs->runtime; + struct snd_pcm_runtime *runtime = subs->runtime; int rate_index; long offset; - pmac_stream_t *astr; - + struct pmac_stream *astr; + rec->dma_size = snd_pcm_lib_buffer_bytes(subs); rec->period_size = snd_pcm_lib_period_bytes(subs); rec->nperiods = rec->dma_size / rec->period_size; @@ -220,7 +225,8 @@ static int snd_pmac_pcm_prepare(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substr /* set up constraints */ astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); - snd_runtime_check(astr, return -EINVAL); + if (! astr) + return -EINVAL; astr->cur_freqs = 1 << rate_index; astr->cur_formats = 1 << runtime->format; chip->rate_index = rate_index; @@ -266,8 +272,8 @@ static int snd_pmac_pcm_prepare(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substr /* * PCM trigger/stop */ -static int snd_pmac_pcm_trigger(pmac_t *chip, pmac_stream_t *rec, - snd_pcm_substream_t *subs, int cmd) +static int snd_pmac_pcm_trigger(struct snd_pmac *chip, struct pmac_stream *rec, + struct snd_pcm_substream *subs, int cmd) { volatile struct dbdma_cmd __iomem *cp; int i, command; @@ -295,7 +301,7 @@ static int snd_pmac_pcm_trigger(pmac_t *chip, pmac_stream_t *rec, case SNDRV_PCM_TRIGGER_SUSPEND: spin_lock(&chip->reg_lock); rec->running = 0; - /*printk("stopped!!\n");*/ + /*printk(KERN_DEBUG "stopped!!\n");*/ snd_pmac_dma_stop(rec); for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) out_le16(&cp->command, DBDMA_STOP); @@ -313,8 +319,9 @@ static int snd_pmac_pcm_trigger(pmac_t *chip, pmac_stream_t *rec, * return the current pointer */ inline -static snd_pcm_uframes_t snd_pmac_pcm_pointer(pmac_t *chip, pmac_stream_t *rec, - snd_pcm_substream_t *subs) +static snd_pcm_uframes_t snd_pmac_pcm_pointer(struct snd_pmac *chip, + struct pmac_stream *rec, + struct snd_pcm_substream *subs) { int count = 0; @@ -329,7 +336,7 @@ static snd_pcm_uframes_t snd_pmac_pcm_pointer(pmac_t *chip, pmac_stream_t *rec, } #endif count += rec->cur_period * rec->period_size; - /*printk("pointer=%d\n", count);*/ + /*printk(KERN_DEBUG "pointer=%d\n", count);*/ return bytes_to_frames(subs->runtime, count); } @@ -337,22 +344,22 @@ static snd_pcm_uframes_t snd_pmac_pcm_pointer(pmac_t *chip, pmac_stream_t *rec, * playback */ -static int snd_pmac_playback_prepare(snd_pcm_substream_t *subs) +static int snd_pmac_playback_prepare(struct snd_pcm_substream *subs) { - pmac_t *chip = snd_pcm_substream_chip(subs); + struct snd_pmac *chip = snd_pcm_substream_chip(subs); return snd_pmac_pcm_prepare(chip, &chip->playback, subs); } -static int snd_pmac_playback_trigger(snd_pcm_substream_t *subs, +static int snd_pmac_playback_trigger(struct snd_pcm_substream *subs, int cmd) { - pmac_t *chip = snd_pcm_substream_chip(subs); + struct snd_pmac *chip = snd_pcm_substream_chip(subs); return snd_pmac_pcm_trigger(chip, &chip->playback, subs, cmd); } -static snd_pcm_uframes_t snd_pmac_playback_pointer(snd_pcm_substream_t *subs) +static snd_pcm_uframes_t snd_pmac_playback_pointer(struct snd_pcm_substream *subs) { - pmac_t *chip = snd_pcm_substream_chip(subs); + struct snd_pmac *chip = snd_pcm_substream_chip(subs); return snd_pmac_pcm_pointer(chip, &chip->playback, subs); } @@ -361,30 +368,99 @@ static snd_pcm_uframes_t snd_pmac_playback_pointer(snd_pcm_substream_t *subs) * capture */ -static int snd_pmac_capture_prepare(snd_pcm_substream_t *subs) +static int snd_pmac_capture_prepare(struct snd_pcm_substream *subs) { - pmac_t *chip = snd_pcm_substream_chip(subs); + struct snd_pmac *chip = snd_pcm_substream_chip(subs); return snd_pmac_pcm_prepare(chip, &chip->capture, subs); } -static int snd_pmac_capture_trigger(snd_pcm_substream_t *subs, +static int snd_pmac_capture_trigger(struct snd_pcm_substream *subs, int cmd) { - pmac_t *chip = snd_pcm_substream_chip(subs); + struct snd_pmac *chip = snd_pcm_substream_chip(subs); return snd_pmac_pcm_trigger(chip, &chip->capture, subs, cmd); } -static snd_pcm_uframes_t snd_pmac_capture_pointer(snd_pcm_substream_t *subs) +static snd_pcm_uframes_t snd_pmac_capture_pointer(struct snd_pcm_substream *subs) { - pmac_t *chip = snd_pcm_substream_chip(subs); + struct snd_pmac *chip = snd_pcm_substream_chip(subs); return snd_pmac_pcm_pointer(chip, &chip->capture, subs); } /* + * Handle DEAD DMA transfers: + * if the TX status comes up "DEAD" - reported on some Power Computing machines + * we need to re-start the dbdma - but from a different physical start address + * and with a different transfer length. It would get very messy to do this + * with the normal dbdma_cmd blocks - we would have to re-write the buffer start + * addresses each time. So, we will keep a single dbdma_cmd block which can be + * fiddled with. + * When DEAD status is first reported the content of the faulted dbdma block is + * copied into the emergency buffer and we note that the buffer is in use. + * we then bump the start physical address by the amount that was successfully + * output before it died. + * On any subsequent DEAD result we just do the bump-ups (we know that we are + * already using the emergency dbdma_cmd). + * CHECK: this just tries to "do it". It is possible that we should abandon + * xfers when the number of residual bytes gets below a certain value - I can + * see that this might cause a loop-forever if a too small transfer causes + * DEAD status. However this is a TODO for now - we'll see what gets reported. + * When we get a successful transfer result with the emergency buffer we just + * pretend that it completed using the original dmdma_cmd and carry on. The + * 'next_cmd' field will already point back to the original loop of blocks. + */ +static inline void snd_pmac_pcm_dead_xfer(struct pmac_stream *rec, + volatile struct dbdma_cmd __iomem *cp) +{ + unsigned short req, res ; + unsigned int phy ; + + /* printk(KERN_WARNING "snd-powermac: DMA died - patching it up!\n"); */ + + /* to clear DEAD status we must first clear RUN + set it to quiescent to be on the safe side */ + (void)in_le32(&rec->dma->status); + out_le32(&rec->dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + + if (!emergency_in_use) { /* new problem */ + memcpy((void *)emergency_dbdma.cmds, (void *)cp, + sizeof(struct dbdma_cmd)); + emergency_in_use = 1; + st_le16(&cp->xfer_status, 0); + st_le16(&cp->req_count, rec->period_size); + cp = emergency_dbdma.cmds; + } + + /* now bump the values to reflect the amount + we haven't yet shifted */ + req = ld_le16(&cp->req_count); + res = ld_le16(&cp->res_count); + phy = ld_le32(&cp->phy_addr); + phy += (req - res); + st_le16(&cp->req_count, res); + st_le16(&cp->res_count, 0); + st_le16(&cp->xfer_status, 0); + st_le32(&cp->phy_addr, phy); + + st_le32(&cp->cmd_dep, rec->cmd.addr + + sizeof(struct dbdma_cmd)*((rec->cur_period+1)%rec->nperiods)); + + st_le16(&cp->command, OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS); + + /* point at our patched up command block */ + out_le32(&rec->dma->cmdptr, emergency_dbdma.addr); + + /* we must re-start the controller */ + (void)in_le32(&rec->dma->status); + /* should complete clearing the DEAD status */ + out_le32(&rec->dma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); +} + +/* * update playback/capture pointer from interrupts */ -static void snd_pmac_pcm_update(pmac_t *chip, pmac_stream_t *rec) +static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) { volatile struct dbdma_cmd __iomem *cp; int c; @@ -392,21 +468,35 @@ static void snd_pmac_pcm_update(pmac_t *chip, pmac_stream_t *rec) spin_lock(&chip->reg_lock); if (rec->running) { - cp = &rec->cmd.cmds[rec->cur_period]; for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */ + + if (emergency_in_use) /* already using DEAD xfer? */ + cp = emergency_dbdma.cmds; + else + cp = &rec->cmd.cmds[rec->cur_period]; + stat = ld_le16(&cp->xfer_status); + + if (stat & DEAD) { + snd_pmac_pcm_dead_xfer(rec, cp); + break; /* this block is still going */ + } + + if (emergency_in_use) + emergency_in_use = 0 ; /* done that */ + if (! (stat & ACTIVE)) break; - /*printk("update frag %d\n", rec->cur_period);*/ + + /*printk(KERN_DEBUG "update frag %d\n", rec->cur_period);*/ st_le16(&cp->xfer_status, 0); st_le16(&cp->req_count, rec->period_size); /*st_le16(&cp->res_count, 0);*/ rec->cur_period++; if (rec->cur_period >= rec->nperiods) { rec->cur_period = 0; - cp = rec->cmd.cmds; - } else - cp++; + } + spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(rec->substream); spin_lock(&chip->reg_lock); @@ -420,7 +510,7 @@ static void snd_pmac_pcm_update(pmac_t *chip, pmac_stream_t *rec) * hw info */ -static snd_pcm_hardware_t snd_pmac_playback = +static struct snd_pcm_hardware snd_pmac_playback = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | @@ -439,7 +529,7 @@ static snd_pcm_hardware_t snd_pmac_playback = .periods_max = PMAC_MAX_FRAGS, }; -static snd_pcm_hardware_t snd_pmac_capture = +static struct snd_pcm_hardware snd_pmac_capture = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | @@ -460,14 +550,15 @@ static snd_pcm_hardware_t snd_pmac_capture = #if 0 // NYI -static int snd_pmac_hw_rule_rate(snd_pcm_hw_params_t *params, - snd_pcm_hw_rule_t *rule) +static int snd_pmac_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) { - pmac_t *chip = rule->private; - pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]); + struct snd_pmac *chip = rule->private; + struct pmac_stream *rec = snd_pmac_get_stream(chip, rule->deps[0]); int i, freq_table[8], num_freqs; - snd_runtime_check(rec, return -EINVAL); + if (! rec) + return -EINVAL; num_freqs = 0; for (i = chip->num_freqs - 1; i >= 0; i--) { if (rec->cur_freqs & (1 << i)) @@ -478,50 +569,31 @@ static int snd_pmac_hw_rule_rate(snd_pcm_hw_params_t *params, num_freqs, freq_table, 0); } -static int snd_pmac_hw_rule_format(snd_pcm_hw_params_t *params, - snd_pcm_hw_rule_t *rule) +static int snd_pmac_hw_rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) { - pmac_t *chip = rule->private; - pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]); + struct snd_pmac *chip = rule->private; + struct pmac_stream *rec = snd_pmac_get_stream(chip, rule->deps[0]); - snd_runtime_check(rec, return -EINVAL); + if (! rec) + return -EINVAL; return snd_mask_refine_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), rec->cur_formats); } #endif // NYI -static int snd_pmac_pcm_open(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +static int snd_pmac_pcm_open(struct snd_pmac *chip, struct pmac_stream *rec, + struct snd_pcm_substream *subs) { - snd_pcm_runtime_t *runtime = subs->runtime; - int i, j, fflags; - static int typical_freqs[] = { - 44100, - 22050, - 11025, - 0, - }; - static int typical_freq_flags[] = { - SNDRV_PCM_RATE_44100, - SNDRV_PCM_RATE_22050, - SNDRV_PCM_RATE_11025, - 0, - }; + struct snd_pcm_runtime *runtime = subs->runtime; + int i; /* look up frequency table and fill bit mask */ runtime->hw.rates = 0; - fflags = chip->freqs_ok; - for (i = 0; typical_freqs[i]; i++) { - for (j = 0; j < chip->num_freqs; j++) { - if ((chip->freqs_ok & (1 << j)) && - chip->freq_table[j] == typical_freqs[i]) { - runtime->hw.rates |= typical_freq_flags[i]; - fflags &= ~(1 << j); - break; - } - } - } - if (fflags) /* rest */ - runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + for (i = 0; i < chip->num_freqs; i++) + if (chip->freqs_ok & (1 << i)) + runtime->hw.rates |= + snd_pcm_rate_to_rate_bit(chip->freq_table[i]); /* check for minimum and maximum rates */ for (i = 0; i < chip->num_freqs; i++) { @@ -554,56 +626,55 @@ static int snd_pmac_pcm_open(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream runtime->hw.periods_max = rec->cmd.size - 1; - if (chip->can_duplex) - snd_pcm_set_sync(subs); - /* constraints to fix choppy sound */ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); return 0; } -static int snd_pmac_pcm_close(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) +static int snd_pmac_pcm_close(struct snd_pmac *chip, struct pmac_stream *rec, + struct snd_pcm_substream *subs) { - pmac_stream_t *astr; + struct pmac_stream *astr; snd_pmac_dma_stop(rec); astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); - snd_runtime_check(astr, return -EINVAL); + if (! astr) + return -EINVAL; /* reset constraints */ astr->cur_freqs = chip->freqs_ok; astr->cur_formats = chip->formats_ok; - + return 0; } -static int snd_pmac_playback_open(snd_pcm_substream_t *subs) +static int snd_pmac_playback_open(struct snd_pcm_substream *subs) { - pmac_t *chip = snd_pcm_substream_chip(subs); + struct snd_pmac *chip = snd_pcm_substream_chip(subs); subs->runtime->hw = snd_pmac_playback; return snd_pmac_pcm_open(chip, &chip->playback, subs); } -static int snd_pmac_capture_open(snd_pcm_substream_t *subs) +static int snd_pmac_capture_open(struct snd_pcm_substream *subs) { - pmac_t *chip = snd_pcm_substream_chip(subs); + struct snd_pmac *chip = snd_pcm_substream_chip(subs); subs->runtime->hw = snd_pmac_capture; return snd_pmac_pcm_open(chip, &chip->capture, subs); } -static int snd_pmac_playback_close(snd_pcm_substream_t *subs) +static int snd_pmac_playback_close(struct snd_pcm_substream *subs) { - pmac_t *chip = snd_pcm_substream_chip(subs); + struct snd_pmac *chip = snd_pcm_substream_chip(subs); return snd_pmac_pcm_close(chip, &chip->playback, subs); } -static int snd_pmac_capture_close(snd_pcm_substream_t *subs) +static int snd_pmac_capture_close(struct snd_pcm_substream *subs) { - pmac_t *chip = snd_pcm_substream_chip(subs); + struct snd_pmac *chip = snd_pcm_substream_chip(subs); return snd_pmac_pcm_close(chip, &chip->capture, subs); } @@ -611,7 +682,7 @@ static int snd_pmac_capture_close(snd_pcm_substream_t *subs) /* */ -static snd_pcm_ops_t snd_pmac_playback_ops = { +static struct snd_pcm_ops snd_pmac_playback_ops = { .open = snd_pmac_playback_open, .close = snd_pmac_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -622,7 +693,7 @@ static snd_pcm_ops_t snd_pmac_playback_ops = { .pointer = snd_pmac_playback_pointer, }; -static snd_pcm_ops_t snd_pmac_capture_ops = { +static struct snd_pcm_ops snd_pmac_capture_ops = { .open = snd_pmac_capture_open, .close = snd_pmac_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -633,14 +704,9 @@ static snd_pcm_ops_t snd_pmac_capture_ops = { .pointer = snd_pmac_capture_pointer, }; -static void pmac_pcm_free(snd_pcm_t *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - -int __init snd_pmac_pcm_new(pmac_t *chip) +int snd_pmac_pcm_new(struct snd_pmac *chip) { - snd_pcm_t *pcm; + struct snd_pcm *pcm; int err; int num_captures = 1; @@ -655,7 +721,6 @@ int __init snd_pmac_pcm_new(pmac_t *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pmac_capture_ops); pcm->private_data = chip; - pcm->private_free = pmac_pcm_free; pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; strcpy(pcm->name, chip->card->shortname); chip->pcm = pcm; @@ -678,7 +743,7 @@ int __init snd_pmac_pcm_new(pmac_t *chip) } -static void snd_pmac_dbdma_reset(pmac_t *chip) +static void snd_pmac_dbdma_reset(struct snd_pmac *chip) { out_le32(&chip->playback.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); snd_pmac_wait_ack(&chip->playback); @@ -690,9 +755,9 @@ static void snd_pmac_dbdma_reset(pmac_t *chip) /* * handling beep */ -void snd_pmac_beep_dma_start(pmac_t *chip, int bytes, unsigned long addr, int speed) +void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long addr, int speed) { - pmac_stream_t *rec = &chip->playback; + struct pmac_stream *rec = &chip->playback; snd_pmac_dma_stop(rec); st_le16(&chip->extra_dma.cmds->req_count, bytes); @@ -708,7 +773,7 @@ void snd_pmac_beep_dma_start(pmac_t *chip, int bytes, unsigned long addr, int sp snd_pmac_dma_run(rec, RUN); } -void snd_pmac_beep_dma_stop(pmac_t *chip) +void snd_pmac_beep_dma_stop(struct snd_pmac *chip) { snd_pmac_dma_stop(&chip->playback); st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); @@ -720,30 +785,30 @@ void snd_pmac_beep_dma_stop(pmac_t *chip) * interrupt handlers */ static irqreturn_t -snd_pmac_tx_intr(int irq, void *devid, struct pt_regs *regs) +snd_pmac_tx_intr(int irq, void *devid) { - pmac_t *chip = devid; + struct snd_pmac *chip = devid; snd_pmac_pcm_update(chip, &chip->playback); return IRQ_HANDLED; } static irqreturn_t -snd_pmac_rx_intr(int irq, void *devid, struct pt_regs *regs) +snd_pmac_rx_intr(int irq, void *devid) { - pmac_t *chip = devid; + struct snd_pmac *chip = devid; snd_pmac_pcm_update(chip, &chip->capture); return IRQ_HANDLED; } static irqreturn_t -snd_pmac_ctrl_intr(int irq, void *devid, struct pt_regs *regs) +snd_pmac_ctrl_intr(int irq, void *devid) { - pmac_t *chip = devid; + struct snd_pmac *chip = devid; int ctrl = in_le32(&chip->awacs->control); - /*printk("pmac: control interrupt.. 0x%x\n", ctrl);*/ + /*printk(KERN_DEBUG "pmac: control interrupt.. 0x%x\n", ctrl);*/ if (ctrl & MASK_PORTCHG) { /* do something when headphone is plugged/unplugged? */ if (chip->update_automute) @@ -763,16 +828,17 @@ snd_pmac_ctrl_intr(int irq, void *devid, struct pt_regs *regs) /* * a wrapper to feature call for compatibility */ -static void snd_pmac_sound_feature(pmac_t *chip, int enable) +static void snd_pmac_sound_feature(struct snd_pmac *chip, int enable) { - ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable); + if (ppc_md.feature_call) + ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable); } /* * release resources */ -static int snd_pmac_free(pmac_t *chip) +static int snd_pmac_free(struct snd_pmac *chip) { /* stop sounds */ if (chip->initialized) { @@ -781,10 +847,8 @@ static int snd_pmac_free(pmac_t *chip) out_le32(&chip->awacs->control, in_le32(&chip->awacs->control) & 0xfff); } - snd_pmac_sound_feature(chip, 0); -#ifdef CONFIG_PM - snd_pmac_unregister_sleep_notifier(chip); -#endif + if (chip->node) + snd_pmac_sound_feature(chip, 0); /* clean up mixer if any */ if (chip->mixer_free) @@ -802,6 +866,7 @@ static int snd_pmac_free(pmac_t *chip) snd_pmac_dbdma_free(chip, &chip->playback.cmd); snd_pmac_dbdma_free(chip, &chip->capture.cmd); snd_pmac_dbdma_free(chip, &chip->extra_dma); + snd_pmac_dbdma_free(chip, &emergency_dbdma); if (chip->macio_base) iounmap(chip->macio_base); if (chip->latch_base) @@ -812,23 +877,19 @@ static int snd_pmac_free(pmac_t *chip) iounmap(chip->playback.dma); if (chip->capture.dma) iounmap(chip->capture.dma); -#ifndef CONFIG_PPC64 + if (chip->node) { int i; - for (i = 0; i < 3; i++) { - if (chip->of_requested & (1 << i)) { - if (chip->is_k2) - release_OF_resource(chip->node->parent, - i); - else - release_OF_resource(chip->node, i); - } + if (chip->requested & (1 << i)) + release_mem_region(chip->rsrc[i].start, + resource_size(&chip->rsrc[i])); } } -#endif /* CONFIG_PPC64 */ + if (chip->pdev) pci_dev_put(chip->pdev); + of_node_put(chip->node); kfree(chip); return 0; } @@ -837,9 +898,9 @@ static int snd_pmac_free(pmac_t *chip) /* * free the device */ -static int snd_pmac_dev_free(snd_device_t *device) +static int snd_pmac_dev_free(struct snd_device *device) { - pmac_t *chip = device->device_data; + struct snd_pmac *chip = device->device_data; return snd_pmac_free(chip); } @@ -848,25 +909,25 @@ static int snd_pmac_dev_free(snd_device_t *device) * check the machine support byteswap (little-endian) */ -static void __init detect_byte_swap(pmac_t *chip) +static void detect_byte_swap(struct snd_pmac *chip) { struct device_node *mio; /* if seems that Keylargo can't byte-swap */ for (mio = chip->node->parent; mio; mio = mio->parent) { if (strcmp(mio->name, "mac-io") == 0) { - if (device_is_compatible(mio, "Keylargo")) + if (of_device_is_compatible(mio, "Keylargo")) chip->can_byte_swap = 0; break; } } /* it seems the Pismo & iBook can't byte-swap in hardware. */ - if (machine_is_compatible("PowerBook3,1") || - machine_is_compatible("PowerBook2,1")) + if (of_machine_is_compatible("PowerBook3,1") || + of_machine_is_compatible("PowerBook2,1")) chip->can_byte_swap = 0 ; - if (machine_is_compatible("PowerBook2,1")) + if (of_machine_is_compatible("PowerBook2,1")) chip->can_duplex = 0; } @@ -874,15 +935,15 @@ static void __init detect_byte_swap(pmac_t *chip) /* * detect a sound chip */ -static int __init snd_pmac_detect(pmac_t *chip) +static int snd_pmac_detect(struct snd_pmac *chip) { - struct device_node *sound = NULL; - unsigned int *prop, l; + struct device_node *sound; + struct device_node *dn; + const unsigned int *prop; + unsigned int l; struct macio_chip* macio; - u32 layout_id = 0; - - if (_machine != _MACH_Pmac) + if (!machine_is(powermac)) return -ENODEV; chip->subframe = 0; @@ -894,35 +955,35 @@ static int __init snd_pmac_detect(pmac_t *chip) chip->can_capture = 1; chip->num_freqs = ARRAY_SIZE(awacs_freqs); chip->freq_table = awacs_freqs; + chip->pdev = NULL; chip->control_mask = MASK_IEPC | MASK_IEE | 0x11; /* default */ /* check machine type */ - if (machine_is_compatible("AAPL,3400/2400") - || machine_is_compatible("AAPL,3500")) + if (of_machine_is_compatible("AAPL,3400/2400") + || of_machine_is_compatible("AAPL,3500")) chip->is_pbook_3400 = 1; - else if (machine_is_compatible("PowerBook1,1") - || machine_is_compatible("AAPL,PowerBook1998")) + else if (of_machine_is_compatible("PowerBook1,1") + || of_machine_is_compatible("AAPL,PowerBook1998")) chip->is_pbook_G3 = 1; - chip->node = find_devices("awacs"); - if (chip->node) - sound = chip->node; + chip->node = of_find_node_by_name(NULL, "awacs"); + sound = of_node_get(chip->node); /* * powermac G3 models have a node called "davbus" * with a child called "sound". */ if (!chip->node) - chip->node = find_devices("davbus"); + chip->node = of_find_node_by_name(NULL, "davbus"); /* * if we didn't find a davbus device, try 'i2s-a' since * this seems to be what iBooks have */ if (! chip->node) { - chip->node = find_devices("i2s-a"); + chip->node = of_find_node_by_name(NULL, "i2s-a"); if (chip->node && chip->node->parent && chip->node->parent->parent) { - if (device_is_compatible(chip->node->parent->parent, + if (of_device_is_compatible(chip->node->parent->parent, "K2-Keylargo")) chip->is_k2 = 1; } @@ -931,79 +992,73 @@ static int __init snd_pmac_detect(pmac_t *chip) return -ENODEV; if (!sound) { - sound = find_devices("sound"); + sound = of_find_node_by_name(NULL, "sound"); while (sound && sound->parent != chip->node) - sound = sound->next; + sound = of_find_node_by_name(sound, "sound"); } - if (! sound) + if (! sound) { + of_node_put(chip->node); + chip->node = NULL; return -ENODEV; - prop = (unsigned int *) get_property(sound, "sub-frame", NULL); + } + prop = of_get_property(sound, "sub-frame", NULL); if (prop && *prop < 16) chip->subframe = *prop; - prop = (unsigned int *) get_property(sound, "layout-id", NULL); - if (prop) - layout_id = *prop; + prop = of_get_property(sound, "layout-id", NULL); + if (prop) { + /* partly deprecate snd-powermac, for those machines + * that have a layout-id property for now */ + printk(KERN_INFO "snd-powermac no longer handles any " + "machines with a layout-id property " + "in the device-tree, use snd-aoa.\n"); + of_node_put(sound); + of_node_put(chip->node); + chip->node = NULL; + return -ENODEV; + } /* This should be verified on older screamers */ - if (device_is_compatible(sound, "screamer")) { + if (of_device_is_compatible(sound, "screamer")) { chip->model = PMAC_SCREAMER; // chip->can_byte_swap = 0; /* FIXME: check this */ } - if (device_is_compatible(sound, "burgundy")) { + if (of_device_is_compatible(sound, "burgundy")) { chip->model = PMAC_BURGUNDY; chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ } - if (device_is_compatible(sound, "daca")) { + if (of_device_is_compatible(sound, "daca")) { chip->model = PMAC_DACA; chip->can_capture = 0; /* no capture */ chip->can_duplex = 0; // chip->can_byte_swap = 0; /* FIXME: check this */ chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ } - if (device_is_compatible(sound, "tumbler")) { + if (of_device_is_compatible(sound, "tumbler")) { chip->model = PMAC_TUMBLER; - chip->can_capture = 0; /* no capture */ + chip->can_capture = of_machine_is_compatible("PowerMac4,2") + || of_machine_is_compatible("PowerBook3,2") + || of_machine_is_compatible("PowerBook3,3") + || of_machine_is_compatible("PowerBook4,1") + || of_machine_is_compatible("PowerBook4,2") + || of_machine_is_compatible("PowerBook4,3"); chip->can_duplex = 0; // chip->can_byte_swap = 0; /* FIXME: check this */ chip->num_freqs = ARRAY_SIZE(tumbler_freqs); chip->freq_table = tumbler_freqs; chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ } - if (device_is_compatible(sound, "snapper")) { + if (of_device_is_compatible(sound, "snapper")) { chip->model = PMAC_SNAPPER; // chip->can_byte_swap = 0; /* FIXME: check this */ chip->num_freqs = ARRAY_SIZE(tumbler_freqs); chip->freq_table = tumbler_freqs; chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ } - if (device_is_compatible(sound, "AOAKeylargo") || - device_is_compatible(sound, "AOAbase") || - device_is_compatible(sound, "AOAK2")) { - /* For now, only support very basic TAS3004 based machines with - * single frequency until proper i2s control is implemented - */ - switch(layout_id) { - case 0x48: - case 0x46: - case 0x33: - case 0x29: - case 0x24: - chip->num_freqs = ARRAY_SIZE(tumbler_freqs); - chip->model = PMAC_SNAPPER; - chip->can_byte_swap = 0; /* FIXME: check this */ - chip->control_mask = MASK_IEPC | 0x11;/* disable IEE */ - break; - case 0x3a: - chip->num_freqs = ARRAY_SIZE(tumbler_freqs); - chip->model = PMAC_TOONIE; - chip->can_byte_swap = 0; /* FIXME: check this */ - chip->control_mask = MASK_IEPC | 0x11;/* disable IEE */ - break; - } - } - prop = (unsigned int *)get_property(sound, "device-id", NULL); + prop = of_get_property(sound, "device-id", NULL); if (prop) chip->device_id = *prop; - chip->has_iic = (find_devices("perch") != NULL); + dn = of_find_node_by_name(NULL, "perch"); + chip->has_iic = (dn != NULL); + of_node_put(dn); /* We need the PCI device for DMA allocations, let's use a crude method * for now ... @@ -1030,10 +1085,9 @@ static int __init snd_pmac_detect(pmac_t *chip) /* look for a property saying what sample rates are available */ - prop = (unsigned int *) get_property(sound, "sample-rates", &l); + prop = of_get_property(sound, "sample-rates", &l); if (! prop) - prop = (unsigned int *) get_property(sound, - "output-frame-rates", &l); + prop = of_get_property(sound, "output-frame-rates", &l); if (prop) { int i; chip->freqs_ok = 0; @@ -1054,29 +1108,7 @@ static int __init snd_pmac_detect(pmac_t *chip) chip->freqs_ok = 1; } - return 0; -} - -/* - * exported - boolean info callbacks for ease of programming - */ -int snd_pmac_boolean_stereo_info(snd_kcontrol_t *kcontrol, - snd_ctl_elem_info_t *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - -int snd_pmac_boolean_mono_info(snd_kcontrol_t *kcontrol, - snd_ctl_elem_info_t *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; + of_node_put(sound); return 0; } @@ -1084,18 +1116,20 @@ int snd_pmac_boolean_mono_info(snd_kcontrol_t *kcontrol, /* * auto-mute */ -static int pmac_auto_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int pmac_auto_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = chip->auto_mute; return 0; } -static int pmac_auto_mute_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int pmac_auto_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); if (ucontrol->value.integer.value[0] != chip->auto_mute) { - chip->auto_mute = ucontrol->value.integer.value[0]; + chip->auto_mute = !!ucontrol->value.integer.value[0]; if (chip->update_automute) chip->update_automute(chip, 1); return 1; @@ -1103,9 +1137,10 @@ static int pmac_auto_mute_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uc return 0; } -static int pmac_hp_detect_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int pmac_hp_detect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - pmac_t *chip = snd_kcontrol_chip(kcontrol); + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); if (chip->detect_headphone) ucontrol->value.integer.value[0] = chip->detect_headphone(chip); else @@ -1113,7 +1148,7 @@ static int pmac_hp_detect_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uc return 0; } -static snd_kcontrol_new_t auto_mute_controls[] __initdata = { +static struct snd_kcontrol_new auto_mute_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Auto Mute Switch", .info = snd_pmac_boolean_mono_info, @@ -1128,7 +1163,7 @@ static snd_kcontrol_new_t auto_mute_controls[] __initdata = { }, }; -int __init snd_pmac_add_automute(pmac_t *chip) +int snd_pmac_add_automute(struct snd_pmac *chip) { int err; chip->auto_mute = 1; @@ -1145,20 +1180,20 @@ int __init snd_pmac_add_automute(pmac_t *chip) /* * create and detect a pmac chip record */ -int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return) +int snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) { - pmac_t *chip; + struct snd_pmac *chip; struct device_node *np; int i, err; + unsigned int irq; unsigned long ctrl_addr, txdma_addr, rxdma_addr; - static snd_device_ops_t ops = { + static struct snd_device_ops ops = { .dev_free = snd_pmac_dev_free, }; - snd_runtime_check(chip_return, return -EINVAL); *chip_return = NULL; - chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); + chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; chip->card = card; @@ -1174,86 +1209,100 @@ int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return) if (snd_pmac_dbdma_alloc(chip, &chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 || snd_pmac_dbdma_alloc(chip, &chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 || - snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0) { + snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0 || + snd_pmac_dbdma_alloc(chip, &emergency_dbdma, 2) < 0) { err = -ENOMEM; goto __error; } np = chip->node; + chip->requested = 0; if (chip->is_k2) { - if (np->parent->n_addrs < 2 || np->n_intrs < 3) { - err = -ENODEV; - goto __error; - } - for (i = 0; i < 2; i++) { -#ifndef CONFIG_PPC64 - static char *name[2] = { "- Control", "- DMA" }; - if (! request_OF_resource(np->parent, i, name[i])) { - snd_printk(KERN_ERR "pmac: can't request resource %d!\n", i); + static char *rnames[] = { + "Sound Control", "Sound DMA" }; + for (i = 0; i < 2; i ++) { + if (of_address_to_resource(np->parent, i, + &chip->rsrc[i])) { + printk(KERN_ERR "snd: can't translate rsrc " + " %d (%s)\n", i, rnames[i]); + err = -ENODEV; + goto __error; + } + if (request_mem_region(chip->rsrc[i].start, + resource_size(&chip->rsrc[i]), + rnames[i]) == NULL) { + printk(KERN_ERR "snd: can't request rsrc " + " %d (%s: %pR)\n", + i, rnames[i], &chip->rsrc[i]); err = -ENODEV; goto __error; } - chip->of_requested |= (1 << i); -#endif /* CONFIG_PPC64 */ - ctrl_addr = np->parent->addrs[0].address; - txdma_addr = np->parent->addrs[1].address; - rxdma_addr = txdma_addr + 0x100; + chip->requested |= (1 << i); } - + ctrl_addr = chip->rsrc[0].start; + txdma_addr = chip->rsrc[1].start; + rxdma_addr = txdma_addr + 0x100; } else { - if (np->n_addrs < 3 || np->n_intrs < 3) { - err = -ENODEV; - goto __error; - } - - for (i = 0; i < 3; i++) { -#ifndef CONFIG_PPC64 - static char *name[3] = { "- Control", "- Tx DMA", "- Rx DMA" }; - if (! request_OF_resource(np, i, name[i])) { - snd_printk(KERN_ERR "pmac: can't request resource %d!\n", i); + static char *rnames[] = { + "Sound Control", "Sound Tx DMA", "Sound Rx DMA" }; + for (i = 0; i < 3; i ++) { + if (of_address_to_resource(np, i, + &chip->rsrc[i])) { + printk(KERN_ERR "snd: can't translate rsrc " + " %d (%s)\n", i, rnames[i]); err = -ENODEV; goto __error; } - chip->of_requested |= (1 << i); -#endif /* CONFIG_PPC64 */ - ctrl_addr = np->addrs[0].address; - txdma_addr = np->addrs[1].address; - rxdma_addr = np->addrs[2].address; + if (request_mem_region(chip->rsrc[i].start, + resource_size(&chip->rsrc[i]), + rnames[i]) == NULL) { + printk(KERN_ERR "snd: can't request rsrc " + " %d (%s: %pR)\n", + i, rnames[i], &chip->rsrc[i]); + err = -ENODEV; + goto __error; + } + chip->requested |= (1 << i); } + ctrl_addr = chip->rsrc[0].start; + txdma_addr = chip->rsrc[1].start; + rxdma_addr = chip->rsrc[2].start; } chip->awacs = ioremap(ctrl_addr, 0x1000); chip->playback.dma = ioremap(txdma_addr, 0x100); chip->capture.dma = ioremap(rxdma_addr, 0x100); if (chip->model <= PMAC_BURGUNDY) { - if (request_irq(np->intrs[0].line, snd_pmac_ctrl_intr, 0, + irq = irq_of_parse_and_map(np, 0); + if (request_irq(irq, snd_pmac_ctrl_intr, 0, "PMac", (void*)chip)) { - snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[0].line); + snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", + irq); err = -EBUSY; goto __error; } - chip->irq = np->intrs[0].line; + chip->irq = irq; } - if (request_irq(np->intrs[1].line, snd_pmac_tx_intr, 0, - "PMac Output", (void*)chip)) { - snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[1].line); + irq = irq_of_parse_and_map(np, 1); + if (request_irq(irq, snd_pmac_tx_intr, 0, "PMac Output", (void*)chip)){ + snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", irq); err = -EBUSY; goto __error; } - chip->tx_irq = np->intrs[1].line; - if (request_irq(np->intrs[2].line, snd_pmac_rx_intr, 0, - "PMac Input", (void*)chip)) { - snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[2].line); + chip->tx_irq = irq; + irq = irq_of_parse_and_map(np, 2); + if (request_irq(irq, snd_pmac_rx_intr, 0, "PMac Input", (void*)chip)) { + snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", irq); err = -EBUSY; goto __error; } - chip->rx_irq = np->intrs[2].line; + chip->rx_irq = irq; snd_pmac_sound_feature(chip, 1); - /* reset */ - if (chip->model == PMAC_AWACS) - out_le32(&chip->awacs->control, 0x11); + /* reset & enable interrupts */ + if (chip->model <= PMAC_BURGUNDY) + out_le32(&chip->awacs->control, chip->control_mask); /* Powerbooks have odd ways of enabling inputs such as an expansion-bay CD or sound from an internal modem @@ -1271,9 +1320,11 @@ int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return) } else if (chip->is_pbook_G3) { struct device_node* mio; for (mio = chip->node->parent; mio; mio = mio->parent) { - if (strcmp(mio->name, "mac-io") == 0 - && mio->n_addrs > 0) { - chip->macio_base = ioremap(mio->addrs[0].address, 0x40); + if (strcmp(mio->name, "mac-io") == 0) { + struct resource r; + if (of_address_to_resource(mio, 0, &r) == 0) + chip->macio_base = + ioremap(r.start, 0x40); break; } } @@ -1292,12 +1343,6 @@ int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return) /* Reset dbdma channels */ snd_pmac_dbdma_reset(chip); -#ifdef CONFIG_PM - /* add sleep notifier */ - if (! snd_pmac_register_sleep_notifier(chip)) - snd_card_set_pm_callback(chip->card, snd_pmac_suspend, snd_pmac_resume, chip); -#endif - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) goto __error; @@ -1305,8 +1350,6 @@ int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return) return 0; __error: - if (chip->pdev) - pci_dev_put(chip->pdev); snd_pmac_free(chip); return err; } @@ -1322,11 +1365,11 @@ int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return) * Save state when going to sleep, restore it afterwards. */ -static int snd_pmac_suspend(snd_card_t *card, pm_message_t state) +void snd_pmac_suspend(struct snd_pmac *chip) { - pmac_t *chip = card->pm_private_data; unsigned long flags; + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); if (chip->suspend) chip->suspend(chip); snd_pcm_suspend_all(chip->pcm); @@ -1340,22 +1383,18 @@ static int snd_pmac_suspend(snd_card_t *card, pm_message_t state) if (chip->rx_irq >= 0) disable_irq(chip->rx_irq); snd_pmac_sound_feature(chip, 0); - return 0; } -static int snd_pmac_resume(snd_card_t *card) +void snd_pmac_resume(struct snd_pmac *chip) { - pmac_t *chip = card->pm_private_data; - snd_pmac_sound_feature(chip, 1); if (chip->resume) chip->resume(chip); /* enable CD sound input */ - if (chip->macio_base && chip->is_pbook_G3) { + if (chip->macio_base && chip->is_pbook_G3) out_8(chip->macio_base + 0x37, 3); - } else if (chip->is_pbook_3400) { + else if (chip->is_pbook_3400) in_8(chip->latch_base + 0x190); - } snd_pmac_pcm_set_format(chip); @@ -1366,52 +1405,7 @@ static int snd_pmac_resume(snd_card_t *card) if (chip->rx_irq >= 0) enable_irq(chip->rx_irq); - return 0; -} - -/* the chip is stored statically by snd_pmac_register_sleep_notifier - * because we can't have any private data for notify callback. - */ -static pmac_t *sleeping_pmac = NULL; - -static int snd_pmac_sleep_notify(struct pmu_sleep_notifier *self, int when) -{ - pmac_t *chip; - - chip = sleeping_pmac; - snd_runtime_check(chip, return 0); - - switch (when) { - case PBOOK_SLEEP_NOW: - snd_pmac_suspend(chip->card, PMSG_SUSPEND); - break; - case PBOOK_WAKE: - snd_pmac_resume(chip->card); - break; - } - return PBOOK_SLEEP_OK; -} - -static struct pmu_sleep_notifier snd_pmac_sleep_notifier = { - snd_pmac_sleep_notify, SLEEP_LEVEL_SOUND, -}; - -static int __init snd_pmac_register_sleep_notifier(pmac_t *chip) -{ - /* should be protected here.. */ - snd_assert(! sleeping_pmac, return -EBUSY); - sleeping_pmac = chip; - pmu_register_sleep_notifier(&snd_pmac_sleep_notifier); - return 0; -} - -static int snd_pmac_unregister_sleep_notifier(pmac_t *chip) -{ - /* should be protected here.. */ - snd_assert(sleeping_pmac == chip, return -ENODEV); - pmu_unregister_sleep_notifier(&snd_pmac_sleep_notifier); - sleeping_pmac = NULL; - return 0; + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); } #endif /* CONFIG_PM */ diff --git a/sound/ppc/pmac.h b/sound/ppc/pmac.h index ae3bb6c6edf..25c512c2d74 100644 --- a/sound/ppc/pmac.h +++ b/sound/ppc/pmac.h @@ -22,7 +22,6 @@ #ifndef __PMAC_H #define __PMAC_H -#include <linux/version.h> #include <sound/control.h> #include <sound/pcm.h> #include "awacs.h" @@ -48,18 +47,9 @@ #define PMAC_SUPPORT_AUTOMUTE /* - * typedefs - */ -typedef struct snd_pmac pmac_t; -typedef struct snd_pmac_stream pmac_stream_t; -typedef struct snd_pmac_beep pmac_beep_t; -typedef struct snd_pmac_dbdma pmac_dbdma_t; - - -/* * DBDMA space */ -struct snd_pmac_dbdma { +struct pmac_dbdma { dma_addr_t dma_base; dma_addr_t addr; struct dbdma_cmd __iomem *cmds; @@ -70,7 +60,7 @@ struct snd_pmac_dbdma { /* * playback/capture stream */ -struct snd_pmac_stream { +struct pmac_stream { int running; /* boolean */ int stream; /* PLAYBACK/CAPTURE */ @@ -80,10 +70,10 @@ struct snd_pmac_stream { int buffer_size; /* in kbytes */ int nperiods, cur_period; - pmac_dbdma_t cmd; + struct pmac_dbdma cmd; volatile struct dbdma_regs __iomem *dma; - snd_pcm_substream_t *substream; + struct snd_pcm_substream *substream; unsigned int cur_freqs; /* currently available frequencies */ unsigned int cur_formats; /* currently available formats */ @@ -95,11 +85,11 @@ struct snd_pmac_stream { enum snd_pmac_model { PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER, - PMAC_SNAPPER, PMAC_TOONIE + PMAC_SNAPPER }; struct snd_pmac { - snd_card_t *card; + struct snd_card *card; /* h/w info */ struct device_node *node; @@ -123,7 +113,8 @@ struct snd_pmac { unsigned int initialized : 1; unsigned int feature_is_set : 1; - unsigned int of_requested; + unsigned int requested; + struct resource rsrc[3]; int num_freqs; int *freq_table; @@ -141,75 +132,79 @@ struct snd_pmac { unsigned char __iomem *latch_base; unsigned char __iomem *macio_base; - pmac_stream_t playback; - pmac_stream_t capture; + struct pmac_stream playback; + struct pmac_stream capture; - pmac_dbdma_t extra_dma; + struct pmac_dbdma extra_dma; int irq, tx_irq, rx_irq; - snd_pcm_t *pcm; + struct snd_pcm *pcm; - pmac_beep_t *beep; + struct pmac_beep *beep; unsigned int control_mask; /* control mask */ /* mixer stuffs */ void *mixer_data; - void (*mixer_free)(pmac_t *); - snd_kcontrol_t *master_sw_ctl; - snd_kcontrol_t *speaker_sw_ctl; - snd_kcontrol_t *drc_sw_ctl; /* only used for tumbler -ReneR */ - snd_kcontrol_t *hp_detect_ctl; - snd_kcontrol_t *lineout_sw_ctl; + void (*mixer_free)(struct snd_pmac *); + struct snd_kcontrol *master_sw_ctl; + struct snd_kcontrol *speaker_sw_ctl; + struct snd_kcontrol *drc_sw_ctl; /* only used for tumbler -ReneR */ + struct snd_kcontrol *hp_detect_ctl; + struct snd_kcontrol *lineout_sw_ctl; /* lowlevel callbacks */ - void (*set_format)(pmac_t *chip); - void (*update_automute)(pmac_t *chip, int do_notify); - int (*detect_headphone)(pmac_t *chip); + void (*set_format)(struct snd_pmac *chip); + void (*update_automute)(struct snd_pmac *chip, int do_notify); + int (*detect_headphone)(struct snd_pmac *chip); #ifdef CONFIG_PM - void (*suspend)(pmac_t *chip); - void (*resume)(pmac_t *chip); + void (*suspend)(struct snd_pmac *chip); + void (*resume)(struct snd_pmac *chip); #endif }; /* exported functions */ -int snd_pmac_new(snd_card_t *card, pmac_t **chip_return); -int snd_pmac_pcm_new(pmac_t *chip); -int snd_pmac_attach_beep(pmac_t *chip); -void snd_pmac_detach_beep(pmac_t *chip); -void snd_pmac_beep_stop(pmac_t *chip); -unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate); +int snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return); +int snd_pmac_pcm_new(struct snd_pmac *chip); +int snd_pmac_attach_beep(struct snd_pmac *chip); +void snd_pmac_detach_beep(struct snd_pmac *chip); +void snd_pmac_beep_stop(struct snd_pmac *chip); +unsigned int snd_pmac_rate_index(struct snd_pmac *chip, struct pmac_stream *rec, unsigned int rate); -void snd_pmac_beep_dma_start(pmac_t *chip, int bytes, unsigned long addr, int speed); -void snd_pmac_beep_dma_stop(pmac_t *chip); +void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long addr, int speed); +void snd_pmac_beep_dma_stop(struct snd_pmac *chip); + +#ifdef CONFIG_PM +void snd_pmac_suspend(struct snd_pmac *chip); +void snd_pmac_resume(struct snd_pmac *chip); +#endif /* initialize mixer */ -int snd_pmac_awacs_init(pmac_t *chip); -int snd_pmac_burgundy_init(pmac_t *chip); -int snd_pmac_daca_init(pmac_t *chip); -int snd_pmac_tumbler_init(pmac_t *chip); +int snd_pmac_awacs_init(struct snd_pmac *chip); +int snd_pmac_burgundy_init(struct snd_pmac *chip); +int snd_pmac_daca_init(struct snd_pmac *chip); +int snd_pmac_tumbler_init(struct snd_pmac *chip); int snd_pmac_tumbler_post_init(void); -int snd_pmac_toonie_init(pmac_t *chip); /* i2c functions */ -typedef struct snd_pmac_keywest { +struct pmac_keywest { int addr; struct i2c_client *client; int id; - int (*init_client)(struct snd_pmac_keywest *i2c); + int (*init_client)(struct pmac_keywest *i2c); char *name; -} pmac_keywest_t; +}; -int snd_pmac_keywest_init(pmac_keywest_t *i2c); -void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c); +int snd_pmac_keywest_init(struct pmac_keywest *i2c); +void snd_pmac_keywest_cleanup(struct pmac_keywest *i2c); /* misc */ -int snd_pmac_boolean_stereo_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo); -int snd_pmac_boolean_mono_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo); +#define snd_pmac_boolean_stereo_info snd_ctl_boolean_stereo_info +#define snd_pmac_boolean_mono_info snd_ctl_boolean_mono_info -int snd_pmac_add_automute(pmac_t *chip); +int snd_pmac_add_automute(struct snd_pmac *chip); #endif /* __PMAC_H */ diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c index 231f6432ea6..350a7c8f86d 100644 --- a/sound/ppc/powermac.c +++ b/sound/ppc/powermac.c @@ -18,9 +18,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> -#include <linux/moduleparam.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/module.h> #include <sound/core.h> #include <sound/initval.h> #include "pmac.h" @@ -35,7 +36,7 @@ MODULE_LICENSE("GPL"); static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ -static int enable_beep = 1; +static bool enable_beep = 1; module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for " CHIP_NAME " soundchip."); @@ -44,29 +45,26 @@ MODULE_PARM_DESC(id, "ID string for " CHIP_NAME " soundchip."); module_param(enable_beep, bool, 0444); MODULE_PARM_DESC(enable_beep, "Enable beep using PCM."); +static struct platform_device *device; -/* - * card entry - */ - -static snd_card_t *snd_pmac_card = NULL; /* */ -static int __init snd_pmac_probe(void) +static int snd_pmac_probe(struct platform_device *devptr) { - snd_card_t *card; - pmac_t *chip; + struct snd_card *card; + struct snd_pmac *chip; char *name_ext; int err; - card = snd_card_new(index, id, THIS_MODULE, 0); - if (card == NULL) - return -ENOMEM; + err = snd_card_new(&devptr->dev, index, id, THIS_MODULE, 0, &card); + if (err < 0) + return err; if ((err = snd_pmac_new(card, &chip)) < 0) goto __error; + card->private_data = chip; switch (chip->model) { case PMAC_BURGUNDY: @@ -95,13 +93,6 @@ static int __init snd_pmac_probe(void) if ( snd_pmac_tumbler_init(chip) < 0 || snd_pmac_tumbler_post_init() < 0) goto __error; break; - case PMAC_TOONIE: - strcpy(card->driver, "PMac Toonie"); - strcpy(card->shortname, "PowerMac Toonie"); - strcpy(card->longname, card->shortname); - if ((err = snd_pmac_toonie_init(chip)) < 0) - goto __error; - break; case PMAC_AWACS: case PMAC_SCREAMER: name_ext = chip->model == PMAC_SCREAMER ? "Screamer" : "AWACS"; @@ -119,7 +110,7 @@ static int __init snd_pmac_probe(void) goto __error; break; default: - snd_printk("unsupported hardware %d\n", chip->model); + snd_printk(KERN_ERR "unsupported hardware %d\n", chip->model); err = -EINVAL; goto __error; } @@ -134,7 +125,7 @@ static int __init snd_pmac_probe(void) if ((err = snd_card_register(card)) < 0) goto __error; - snd_pmac_card = card; + platform_set_drvdata(devptr, card); return 0; __error: @@ -143,23 +134,61 @@ __error: } -/* - * MODULE stuff - */ +static int snd_pmac_remove(struct platform_device *devptr) +{ + snd_card_free(platform_get_drvdata(devptr)); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int snd_pmac_driver_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + snd_pmac_suspend(card->private_data); + return 0; +} + +static int snd_pmac_driver_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + snd_pmac_resume(card->private_data); + return 0; +} + +static SIMPLE_DEV_PM_OPS(snd_pmac_pm, snd_pmac_driver_suspend, snd_pmac_driver_resume); +#define SND_PMAC_PM_OPS &snd_pmac_pm +#else +#define SND_PMAC_PM_OPS NULL +#endif + +#define SND_PMAC_DRIVER "snd_powermac" + +static struct platform_driver snd_pmac_driver = { + .probe = snd_pmac_probe, + .remove = snd_pmac_remove, + .driver = { + .name = SND_PMAC_DRIVER, + .owner = THIS_MODULE, + .pm = SND_PMAC_PM_OPS, + }, +}; static int __init alsa_card_pmac_init(void) { int err; - if ((err = snd_pmac_probe()) < 0) + + if ((err = platform_driver_register(&snd_pmac_driver)) < 0) return err; + device = platform_device_register_simple(SND_PMAC_DRIVER, -1, NULL, 0); return 0; } static void __exit alsa_card_pmac_exit(void) { - if (snd_pmac_card) - snd_card_free(snd_pmac_card); + if (!IS_ERR(device)) + platform_device_unregister(device); + platform_driver_unregister(&snd_pmac_driver); } module_init(alsa_card_pmac_init) diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c new file mode 100644 index 00000000000..58f292a87f9 --- /dev/null +++ b/sound/ppc/snd_ps3.c @@ -0,0 +1,1163 @@ +/* + * Audio support for PS3 + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * All rights reserved. + * Copyright 2006, 2007 Sony Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 of the Licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/gfp.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> + +#include <sound/asound.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/memalloc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include <asm/dma.h> +#include <asm/firmware.h> +#include <asm/lv1call.h> +#include <asm/ps3.h> +#include <asm/ps3av.h> + +#include "snd_ps3.h" +#include "snd_ps3_reg.h" + + +/* + * global + */ +static struct snd_ps3_card_info the_card; + +static int snd_ps3_start_delay = CONFIG_SND_PS3_DEFAULT_START_DELAY; + +module_param_named(start_delay, snd_ps3_start_delay, uint, 0644); +MODULE_PARM_DESC(start_delay, "time to insert silent data in ms"); + +static int index = SNDRV_DEFAULT_IDX1; +static char *id = SNDRV_DEFAULT_STR1; + +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for PS3 soundchip."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for PS3 soundchip."); + + +/* + * PS3 audio register access + */ +static inline u32 read_reg(unsigned int reg) +{ + return in_be32(the_card.mapped_mmio_vaddr + reg); +} +static inline void write_reg(unsigned int reg, u32 val) +{ + out_be32(the_card.mapped_mmio_vaddr + reg, val); +} +static inline void update_reg(unsigned int reg, u32 or_val) +{ + u32 newval = read_reg(reg) | or_val; + write_reg(reg, newval); +} +static inline void update_mask_reg(unsigned int reg, u32 mask, u32 or_val) +{ + u32 newval = (read_reg(reg) & mask) | or_val; + write_reg(reg, newval); +} + +/* + * ALSA defs + */ +static const struct snd_pcm_hardware snd_ps3_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE), + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .rate_min = 44100, + .rate_max = 96000, + + .channels_min = 2, /* stereo only */ + .channels_max = 2, + + .buffer_bytes_max = PS3_AUDIO_FIFO_SIZE * 64, + + /* interrupt by four stages */ + .period_bytes_min = PS3_AUDIO_FIFO_STAGE_SIZE * 4, + .period_bytes_max = PS3_AUDIO_FIFO_STAGE_SIZE * 4, + + .periods_min = 16, + .periods_max = 32, /* buffer_size_max/ period_bytes_max */ + + .fifo_size = PS3_AUDIO_FIFO_SIZE +}; + +static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card, + int count, int force_stop) +{ + int dma_ch, done, retries, stop_forced = 0; + uint32_t status; + + for (dma_ch = 0; dma_ch < 8; dma_ch++) { + retries = count; + do { + status = read_reg(PS3_AUDIO_KICK(dma_ch)) & + PS3_AUDIO_KICK_STATUS_MASK; + switch (status) { + case PS3_AUDIO_KICK_STATUS_DONE: + case PS3_AUDIO_KICK_STATUS_NOTIFY: + case PS3_AUDIO_KICK_STATUS_CLEAR: + case PS3_AUDIO_KICK_STATUS_ERROR: + done = 1; + break; + default: + done = 0; + udelay(10); + } + } while (!done && --retries); + if (!retries && force_stop) { + pr_info("%s: DMA ch %d is not stopped.", + __func__, dma_ch); + /* last resort. force to stop dma. + * NOTE: this cause DMA done interrupts + */ + update_reg(PS3_AUDIO_CONFIG, PS3_AUDIO_CONFIG_CLEAR); + stop_forced = 1; + } + } + return stop_forced; +} + +/* + * wait for all dma is done. + * NOTE: caller should reset card->running before call. + * If not, the interrupt handler will re-start DMA, + * then DMA is never stopped. + */ +static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card) +{ + int stop_forced; + /* + * wait for the last dma is done + */ + + /* + * expected maximum DMA done time is 5.7ms + something (DMA itself). + * 5.7ms is from 16bit/sample 2ch 44.1Khz; the time next + * DMA kick event would occur. + */ + stop_forced = snd_ps3_verify_dma_stop(card, 700, 1); + + /* + * clear outstanding interrupts. + */ + update_reg(PS3_AUDIO_INTR_0, 0); + update_reg(PS3_AUDIO_AX_IS, 0); + + /* + *revert CLEAR bit since it will not reset automatically after DMA stop + */ + if (stop_forced) + update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0); + /* ensure the hardware sees changes */ + wmb(); +} + +static void snd_ps3_kick_dma(struct snd_ps3_card_info *card) +{ + + update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST); + /* ensure the hardware sees the change */ + wmb(); +} + +/* + * convert virtual addr to ioif bus addr. + */ +static dma_addr_t v_to_bus(struct snd_ps3_card_info *card, void *paddr, int ch) +{ + return card->dma_start_bus_addr[ch] + + (paddr - card->dma_start_vaddr[ch]); +}; + + +/* + * increment ring buffer pointer. + * NOTE: caller must hold write spinlock + */ +static void snd_ps3_bump_buffer(struct snd_ps3_card_info *card, + enum snd_ps3_ch ch, size_t byte_count, + int stage) +{ + if (!stage) + card->dma_last_transfer_vaddr[ch] = + card->dma_next_transfer_vaddr[ch]; + card->dma_next_transfer_vaddr[ch] += byte_count; + if ((card->dma_start_vaddr[ch] + (card->dma_buffer_size / 2)) <= + card->dma_next_transfer_vaddr[ch]) { + card->dma_next_transfer_vaddr[ch] = card->dma_start_vaddr[ch]; + } +} +/* + * setup dmac to send data to audio and attenuate samples on the ring buffer + */ +static int snd_ps3_program_dma(struct snd_ps3_card_info *card, + enum snd_ps3_dma_filltype filltype) +{ + /* this dmac does not support over 4G */ + uint32_t dma_addr; + int fill_stages, dma_ch, stage; + enum snd_ps3_ch ch; + uint32_t ch0_kick_event = 0; /* initialize to mute gcc */ + void *start_vaddr; + unsigned long irqsave; + int silent = 0; + + switch (filltype) { + case SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL: + silent = 1; + /* intentionally fall thru */ + case SND_PS3_DMA_FILLTYPE_FIRSTFILL: + ch0_kick_event = PS3_AUDIO_KICK_EVENT_ALWAYS; + break; + + case SND_PS3_DMA_FILLTYPE_SILENT_RUNNING: + silent = 1; + /* intentionally fall thru */ + case SND_PS3_DMA_FILLTYPE_RUNNING: + ch0_kick_event = PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY; + break; + } + + snd_ps3_verify_dma_stop(card, 700, 0); + fill_stages = 4; + spin_lock_irqsave(&card->dma_lock, irqsave); + for (ch = 0; ch < 2; ch++) { + start_vaddr = card->dma_next_transfer_vaddr[0]; + for (stage = 0; stage < fill_stages; stage++) { + dma_ch = stage * 2 + ch; + if (silent) + dma_addr = card->null_buffer_start_dma_addr; + else + dma_addr = + v_to_bus(card, + card->dma_next_transfer_vaddr[ch], + ch); + + write_reg(PS3_AUDIO_SOURCE(dma_ch), + (PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY | + dma_addr)); + + /* dst: fixed to 3wire#0 */ + if (ch == 0) + write_reg(PS3_AUDIO_DEST(dma_ch), + (PS3_AUDIO_DEST_TARGET_AUDIOFIFO | + PS3_AUDIO_AO_3W_LDATA(0))); + else + write_reg(PS3_AUDIO_DEST(dma_ch), + (PS3_AUDIO_DEST_TARGET_AUDIOFIFO | + PS3_AUDIO_AO_3W_RDATA(0))); + + /* count always 1 DMA block (1/2 stage = 128 bytes) */ + write_reg(PS3_AUDIO_DMASIZE(dma_ch), 0); + /* bump pointer if needed */ + if (!silent) + snd_ps3_bump_buffer(card, ch, + PS3_AUDIO_DMAC_BLOCK_SIZE, + stage); + + /* kick event */ + if (dma_ch == 0) + write_reg(PS3_AUDIO_KICK(dma_ch), + ch0_kick_event); + else + write_reg(PS3_AUDIO_KICK(dma_ch), + PS3_AUDIO_KICK_EVENT_AUDIO_DMA(dma_ch + - 1) | + PS3_AUDIO_KICK_REQUEST); + } + } + /* ensure the hardware sees the change */ + wmb(); + spin_unlock_irqrestore(&card->dma_lock, irqsave); + + return 0; +} + +/* + * Interrupt handler + */ +static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id) +{ + + uint32_t port_intr; + int underflow_occured = 0; + struct snd_ps3_card_info *card = dev_id; + + if (!card->running) { + update_reg(PS3_AUDIO_AX_IS, 0); + update_reg(PS3_AUDIO_INTR_0, 0); + return IRQ_HANDLED; + } + + port_intr = read_reg(PS3_AUDIO_AX_IS); + /* + *serial buffer empty detected (every 4 times), + *program next dma and kick it + */ + if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) { + write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0)); + if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { + write_reg(PS3_AUDIO_AX_IS, port_intr); + underflow_occured = 1; + } + if (card->silent) { + /* we are still in silent time */ + snd_ps3_program_dma(card, + (underflow_occured) ? + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL : + SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); + snd_ps3_kick_dma(card); + card->silent--; + } else { + snd_ps3_program_dma(card, + (underflow_occured) ? + SND_PS3_DMA_FILLTYPE_FIRSTFILL : + SND_PS3_DMA_FILLTYPE_RUNNING); + snd_ps3_kick_dma(card); + snd_pcm_period_elapsed(card->substream); + } + } else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { + write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0)); + /* + * serial out underflow, but buffer empty not detected. + * in this case, fill fifo with 0 to recover. After + * filling dummy data, serial automatically start to + * consume them and then will generate normal buffer + * empty interrupts. + * If both buffer underflow and buffer empty are occurred, + * it is better to do nomal data transfer than empty one + */ + snd_ps3_program_dma(card, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + snd_ps3_program_dma(card, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + } + /* clear interrupt cause */ + return IRQ_HANDLED; +}; + +/* + * audio mute on/off + * mute_on : 0 output enabled + * 1 mute + */ +static int snd_ps3_mute(int mute_on) +{ + return ps3av_audio_mute(mute_on); +} + +/* + * av setting + * NOTE: calling this function may generate audio interrupt. + */ +static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card) +{ + int ret, retries, i; + pr_debug("%s: start\n", __func__); + + ret = ps3av_set_audio_mode(card->avs.avs_audio_ch, + card->avs.avs_audio_rate, + card->avs.avs_audio_width, + card->avs.avs_audio_format, + card->avs.avs_audio_source); + /* + * Reset the following unwanted settings: + */ + + /* disable all 3wire buffers */ + update_mask_reg(PS3_AUDIO_AO_3WMCTRL, + ~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) | + PS3_AUDIO_AO_3WMCTRL_ASOEN(1) | + PS3_AUDIO_AO_3WMCTRL_ASOEN(2) | + PS3_AUDIO_AO_3WMCTRL_ASOEN(3)), + 0); + wmb(); /* ensure the hardware sees the change */ + /* wait for actually stopped */ + retries = 1000; + while ((read_reg(PS3_AUDIO_AO_3WMCTRL) & + (PS3_AUDIO_AO_3WMCTRL_ASORUN(0) | + PS3_AUDIO_AO_3WMCTRL_ASORUN(1) | + PS3_AUDIO_AO_3WMCTRL_ASORUN(2) | + PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) && + --retries) { + udelay(1); + } + + /* reset buffer pointer */ + for (i = 0; i < 4; i++) { + update_reg(PS3_AUDIO_AO_3WCTRL(i), + PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET); + udelay(10); + } + wmb(); /* ensure the hardware actually start resetting */ + + /* enable 3wire#0 buffer */ + update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0)); + + + /* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */ + update_mask_reg(PS3_AUDIO_AO_3WCTRL(0), + ~PS3_AUDIO_AO_3WCTRL_ASODF, + PS3_AUDIO_AO_3WCTRL_ASODF_LSB); + update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0), + ~PS3_AUDIO_AO_SPDCTRL_SPODF, + PS3_AUDIO_AO_SPDCTRL_SPODF_LSB); + /* ensure all the setting above is written back to register */ + wmb(); + /* avsetting driver altered AX_IE, caller must reset it if you want */ + pr_debug("%s: end\n", __func__); + return ret; +} + +/* + * set sampling rate according to the substream + */ +static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream) +{ + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + struct snd_ps3_avsetting_info avs; + int ret; + + avs = card->avs; + + pr_debug("%s: called freq=%d width=%d\n", __func__, + substream->runtime->rate, + snd_pcm_format_width(substream->runtime->format)); + + pr_debug("%s: before freq=%d width=%d\n", __func__, + card->avs.avs_audio_rate, card->avs.avs_audio_width); + + /* sample rate */ + switch (substream->runtime->rate) { + case 44100: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K; + break; + case 48000: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; + break; + case 88200: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K; + break; + case 96000: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K; + break; + default: + pr_info("%s: invalid rate %d\n", __func__, + substream->runtime->rate); + return 1; + } + + /* width */ + switch (snd_pcm_format_width(substream->runtime->format)) { + case 16: + avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; + break; + case 24: + avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24; + break; + default: + pr_info("%s: invalid width %d\n", __func__, + snd_pcm_format_width(substream->runtime->format)); + return 1; + } + + memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8); + + if (memcmp(&card->avs, &avs, sizeof(avs))) { + pr_debug("%s: after freq=%d width=%d\n", __func__, + card->avs.avs_audio_rate, card->avs.avs_audio_width); + + card->avs = avs; + snd_ps3_change_avsetting(card); + ret = 0; + } else + ret = 1; + + /* check CS non-audio bit and mute accordingly */ + if (avs.avs_cs_info[0] & 0x02) + ps3av_audio_mute_analog(1); /* mute if non-audio */ + else + ps3av_audio_mute_analog(0); + + return ret; +} + +/* + * PCM operators + */ +static int snd_ps3_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + int pcm_index; + + pcm_index = substream->pcm->device; + /* to retrieve substream/runtime in interrupt handler */ + card->substream = substream; + + runtime->hw = snd_ps3_pcm_hw; + + card->start_delay = snd_ps3_start_delay; + + /* mute off */ + snd_ps3_mute(0); /* this function sleep */ + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + PS3_AUDIO_FIFO_STAGE_SIZE * 4 * 2); + return 0; +}; + +static int snd_ps3_pcm_close(struct snd_pcm_substream *substream) +{ + /* mute on */ + snd_ps3_mute(1); + return 0; +}; + +static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + size_t size; + + /* alloc transport buffer */ + size = params_buffer_bytes(hw_params); + snd_pcm_lib_malloc_pages(substream, size); + return 0; +}; + +static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream) +{ + int ret; + ret = snd_pcm_lib_free_pages(substream); + return ret; +}; + +static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream, + unsigned int delay_ms) +{ + int ret; + int rate ; + + rate = substream->runtime->rate; + ret = snd_pcm_format_size(substream->runtime->format, + rate * delay_ms / 1000) + * substream->runtime->channels; + + pr_debug("%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n", + __func__, + delay_ms, + rate, + snd_pcm_format_size(substream->runtime->format, rate), + rate * delay_ms / 1000, + ret); + + return ret; +}; + +static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + unsigned long irqsave; + + if (!snd_ps3_set_avsetting(substream)) { + /* some parameter changed */ + write_reg(PS3_AUDIO_AX_IE, + PS3_AUDIO_AX_IE_ASOBEIE(0) | + PS3_AUDIO_AX_IE_ASOBUIE(0)); + /* + * let SPDIF device re-lock with SPDIF signal, + * start with some silence + */ + card->silent = snd_ps3_delay_to_bytes(substream, + card->start_delay) / + (PS3_AUDIO_FIFO_STAGE_SIZE * 4); /* every 4 times */ + } + + /* restart ring buffer pointer */ + spin_lock_irqsave(&card->dma_lock, irqsave); + { + card->dma_buffer_size = runtime->dma_bytes; + + card->dma_last_transfer_vaddr[SND_PS3_CH_L] = + card->dma_next_transfer_vaddr[SND_PS3_CH_L] = + card->dma_start_vaddr[SND_PS3_CH_L] = + runtime->dma_area; + card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr; + + card->dma_last_transfer_vaddr[SND_PS3_CH_R] = + card->dma_next_transfer_vaddr[SND_PS3_CH_R] = + card->dma_start_vaddr[SND_PS3_CH_R] = + runtime->dma_area + (runtime->dma_bytes / 2); + card->dma_start_bus_addr[SND_PS3_CH_R] = + runtime->dma_addr + (runtime->dma_bytes / 2); + + pr_debug("%s: vaddr=%p bus=%#llx\n", __func__, + card->dma_start_vaddr[SND_PS3_CH_L], + card->dma_start_bus_addr[SND_PS3_CH_L]); + + } + spin_unlock_irqrestore(&card->dma_lock, irqsave); + + /* ensure the hardware sees the change */ + mb(); + + return 0; +}; + +static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* clear outstanding interrupts */ + update_reg(PS3_AUDIO_AX_IS, 0); + + spin_lock(&card->dma_lock); + { + card->running = 1; + } + spin_unlock(&card->dma_lock); + + snd_ps3_program_dma(card, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + while (read_reg(PS3_AUDIO_KICK(7)) & + PS3_AUDIO_KICK_STATUS_MASK) { + udelay(1); + } + snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); + snd_ps3_kick_dma(card); + break; + + case SNDRV_PCM_TRIGGER_STOP: + spin_lock(&card->dma_lock); + { + card->running = 0; + } + spin_unlock(&card->dma_lock); + snd_ps3_wait_for_dma_stop(card); + break; + default: + break; + + } + + return ret; +}; + +/* + * report current pointer + */ +static snd_pcm_uframes_t snd_ps3_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + size_t bytes; + snd_pcm_uframes_t ret; + + spin_lock(&card->dma_lock); + { + bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] - + card->dma_start_vaddr[SND_PS3_CH_L]); + } + spin_unlock(&card->dma_lock); + + ret = bytes_to_frames(substream->runtime, bytes * 2); + + return ret; +}; + +/* + * SPDIF status bits controls + */ +static int snd_ps3_spdif_mask_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +/* FIXME: ps3av_set_audio_mode() assumes only consumer mode */ +static int snd_ps3_spdif_cmask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + memset(ucontrol->value.iec958.status, 0xff, 8); + return 0; +} + +static int snd_ps3_spdif_pmask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int snd_ps3_spdif_default_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + memcpy(ucontrol->value.iec958.status, ps3av_mode_cs_info, 8); + return 0; +} + +static int snd_ps3_spdif_default_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (memcmp(ps3av_mode_cs_info, ucontrol->value.iec958.status, 8)) { + memcpy(ps3av_mode_cs_info, ucontrol->value.iec958.status, 8); + return 1; + } + return 0; +} + +static struct snd_kcontrol_new spdif_ctls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), + .info = snd_ps3_spdif_mask_info, + .get = snd_ps3_spdif_cmask_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK), + .info = snd_ps3_spdif_mask_info, + .get = snd_ps3_spdif_pmask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = snd_ps3_spdif_mask_info, + .get = snd_ps3_spdif_default_get, + .put = snd_ps3_spdif_default_put, + }, +}; + +static struct snd_pcm_ops snd_ps3_pcm_spdif_ops = { + .open = snd_ps3_pcm_open, + .close = snd_ps3_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ps3_pcm_hw_params, + .hw_free = snd_ps3_pcm_hw_free, + .prepare = snd_ps3_pcm_prepare, + .trigger = snd_ps3_pcm_trigger, + .pointer = snd_ps3_pcm_pointer, +}; + + +static int snd_ps3_map_mmio(void) +{ + the_card.mapped_mmio_vaddr = + ioremap(the_card.ps3_dev->m_region->bus_addr, + the_card.ps3_dev->m_region->len); + + if (!the_card.mapped_mmio_vaddr) { + pr_info("%s: ioremap 0 failed p=%#lx l=%#lx \n", + __func__, the_card.ps3_dev->m_region->lpar_addr, + the_card.ps3_dev->m_region->len); + return -ENXIO; + } + + return 0; +}; + +static void snd_ps3_unmap_mmio(void) +{ + iounmap(the_card.mapped_mmio_vaddr); + the_card.mapped_mmio_vaddr = NULL; +} + +static int snd_ps3_allocate_irq(void) +{ + int ret; + u64 lpar_addr, lpar_size; + u64 __iomem *mapped; + + /* FIXME: move this to device_init (H/W probe) */ + + /* get irq outlet */ + ret = lv1_gpu_device_map(1, &lpar_addr, &lpar_size); + if (ret) { + pr_info("%s: device map 1 failed %d\n", __func__, + ret); + return -ENXIO; + } + + mapped = ioremap(lpar_addr, lpar_size); + if (!mapped) { + pr_info("%s: ioremap 1 failed \n", __func__); + return -ENXIO; + } + + the_card.audio_irq_outlet = in_be64(mapped); + + iounmap(mapped); + ret = lv1_gpu_device_unmap(1); + if (ret) + pr_info("%s: unmap 1 failed\n", __func__); + + /* irq */ + ret = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, + the_card.audio_irq_outlet, + &the_card.irq_no); + if (ret) { + pr_info("%s:ps3_alloc_irq failed (%d)\n", __func__, ret); + return ret; + } + + ret = request_irq(the_card.irq_no, snd_ps3_interrupt, 0, + SND_PS3_DRIVER_NAME, &the_card); + if (ret) { + pr_info("%s: request_irq failed (%d)\n", __func__, ret); + goto cleanup_irq; + } + + return 0; + + cleanup_irq: + ps3_irq_plug_destroy(the_card.irq_no); + return ret; +}; + +static void snd_ps3_free_irq(void) +{ + free_irq(the_card.irq_no, &the_card); + ps3_irq_plug_destroy(the_card.irq_no); +} + +static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start) +{ + uint64_t val; + int ret; + + val = (ioaddr_start & (0x0fUL << 32)) >> (32 - 20) | + (0x03UL << 24) | + (0x0fUL << 12) | + (PS3_AUDIO_IOID); + + ret = lv1_gpu_attribute(0x100, 0x007, val); + if (ret) + pr_info("%s: gpu_attribute failed %d\n", __func__, + ret); +} + +static void snd_ps3_audio_fixup(struct snd_ps3_card_info *card) +{ + /* + * avsetting driver seems to never change the followings + * so, init them here once + */ + + /* no dma interrupt needed */ + write_reg(PS3_AUDIO_INTR_EN_0, 0); + + /* use every 4 buffer empty interrupt */ + update_mask_reg(PS3_AUDIO_AX_IC, + PS3_AUDIO_AX_IC_AASOIMD_MASK, + PS3_AUDIO_AX_IC_AASOIMD_EVERY4); + + /* enable 3wire clocks */ + update_mask_reg(PS3_AUDIO_AO_3WMCTRL, + ~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED | + PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED), + 0); + update_reg(PS3_AUDIO_AO_3WMCTRL, + PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT); +} + +static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card) +{ + int ret; + pr_debug("%s: start\n", __func__); + card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2; + card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; + card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; + card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM; + card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL; + memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8); + + ret = snd_ps3_change_avsetting(card); + + snd_ps3_audio_fixup(card); + + /* to start to generate SPDIF signal, fill data */ + snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + pr_debug("%s: end\n", __func__); + return ret; +} + +static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) +{ + int i, ret; + u64 lpar_addr, lpar_size; + + if (WARN_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1))) + return -ENODEV; + if (WARN_ON(dev->match_id != PS3_MATCH_ID_SOUND)) + return -ENODEV; + + the_card.ps3_dev = dev; + + ret = ps3_open_hv_device(dev); + + if (ret) + return -ENXIO; + + /* setup MMIO */ + ret = lv1_gpu_device_map(2, &lpar_addr, &lpar_size); + if (ret) { + pr_info("%s: device map 2 failed %d\n", __func__, ret); + goto clean_open; + } + ps3_mmio_region_init(dev, dev->m_region, lpar_addr, lpar_size, + PAGE_SHIFT); + + ret = snd_ps3_map_mmio(); + if (ret) + goto clean_dev_map; + + /* setup DMA area */ + ps3_dma_region_init(dev, dev->d_region, + PAGE_SHIFT, /* use system page size */ + 0, /* dma type; not used */ + NULL, + _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE)); + dev->d_region->ioid = PS3_AUDIO_IOID; + + ret = ps3_dma_region_create(dev->d_region); + if (ret) { + pr_info("%s: region_create\n", __func__); + goto clean_mmio; + } + + snd_ps3_audio_set_base_addr(dev->d_region->bus_addr); + + /* CONFIG_SND_PS3_DEFAULT_START_DELAY */ + the_card.start_delay = snd_ps3_start_delay; + + /* irq */ + if (snd_ps3_allocate_irq()) { + ret = -ENXIO; + goto clean_dma_region; + } + + /* create card instance */ + ret = snd_card_new(&dev->core, index, id, THIS_MODULE, + 0, &the_card.card); + if (ret < 0) + goto clean_irq; + + strcpy(the_card.card->driver, "PS3"); + strcpy(the_card.card->shortname, "PS3"); + strcpy(the_card.card->longname, "PS3 sound"); + + /* create control elements */ + for (i = 0; i < ARRAY_SIZE(spdif_ctls); i++) { + ret = snd_ctl_add(the_card.card, + snd_ctl_new1(&spdif_ctls[i], &the_card)); + if (ret < 0) + goto clean_card; + } + + /* create PCM devices instance */ + /* NOTE:this driver works assuming pcm:substream = 1:1 */ + ret = snd_pcm_new(the_card.card, + "SPDIF", + 0, /* instance index, will be stored pcm.device*/ + 1, /* output substream */ + 0, /* input substream */ + &(the_card.pcm)); + if (ret) + goto clean_card; + + the_card.pcm->private_data = &the_card; + strcpy(the_card.pcm->name, "SPDIF"); + + /* set pcm ops */ + snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_ps3_pcm_spdif_ops); + + the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED; + /* pre-alloc PCM DMA buffer*/ + ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm, + SNDRV_DMA_TYPE_DEV, + &dev->core, + SND_PS3_PCM_PREALLOC_SIZE, + SND_PS3_PCM_PREALLOC_SIZE); + if (ret < 0) { + pr_info("%s: prealloc failed\n", __func__); + goto clean_card; + } + + /* + * allocate null buffer + * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2 + * PAGE_SIZE is enogh + */ + the_card.null_buffer_start_vaddr = + dma_alloc_coherent(&the_card.ps3_dev->core, + PAGE_SIZE, + &the_card.null_buffer_start_dma_addr, + GFP_KERNEL); + if (!the_card.null_buffer_start_vaddr) { + pr_info("%s: nullbuffer alloc failed\n", __func__); + ret = -ENOMEM; + goto clean_preallocate; + } + pr_debug("%s: null vaddr=%p dma=%#llx\n", __func__, + the_card.null_buffer_start_vaddr, + the_card.null_buffer_start_dma_addr); + /* set default sample rate/word width */ + snd_ps3_init_avsetting(&the_card); + + /* register the card */ + ret = snd_card_register(the_card.card); + if (ret < 0) + goto clean_dma_map; + + pr_info("%s started. start_delay=%dms\n", + the_card.card->longname, the_card.start_delay); + return 0; + +clean_dma_map: + dma_free_coherent(&the_card.ps3_dev->core, + PAGE_SIZE, + the_card.null_buffer_start_vaddr, + the_card.null_buffer_start_dma_addr); +clean_preallocate: + snd_pcm_lib_preallocate_free_for_all(the_card.pcm); +clean_card: + snd_card_free(the_card.card); +clean_irq: + snd_ps3_free_irq(); +clean_dma_region: + ps3_dma_region_free(dev->d_region); +clean_mmio: + snd_ps3_unmap_mmio(); +clean_dev_map: + lv1_gpu_device_unmap(2); +clean_open: + ps3_close_hv_device(dev); + /* + * there is no destructor function to pcm. + * midlayer automatically releases if the card removed + */ + return ret; +}; /* snd_ps3_probe */ + +/* called when module removal */ +static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev) +{ + int ret; + pr_info("%s:start id=%d\n", __func__, dev->match_id); + if (dev->match_id != PS3_MATCH_ID_SOUND) + return -ENXIO; + + /* + * ctl and preallocate buffer will be freed in + * snd_card_free + */ + ret = snd_card_free(the_card.card); + if (ret) + pr_info("%s: ctl freecard=%d\n", __func__, ret); + + dma_free_coherent(&dev->core, + PAGE_SIZE, + the_card.null_buffer_start_vaddr, + the_card.null_buffer_start_dma_addr); + + ps3_dma_region_free(dev->d_region); + + snd_ps3_free_irq(); + snd_ps3_unmap_mmio(); + + lv1_gpu_device_unmap(2); + ps3_close_hv_device(dev); + pr_info("%s:end id=%d\n", __func__, dev->match_id); + return 0; +} /* snd_ps3_remove */ + +static struct ps3_system_bus_driver snd_ps3_bus_driver_info = { + .match_id = PS3_MATCH_ID_SOUND, + .probe = snd_ps3_driver_probe, + .remove = snd_ps3_driver_remove, + .shutdown = snd_ps3_driver_remove, + .core = { + .name = SND_PS3_DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + + +/* + * module/subsystem initialize/terminate + */ +static int __init snd_ps3_init(void) +{ + int ret; + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENXIO; + + memset(&the_card, 0, sizeof(the_card)); + spin_lock_init(&the_card.dma_lock); + + /* register systembus DRIVER, this calls our probe() func */ + ret = ps3_system_bus_driver_register(&snd_ps3_bus_driver_info); + + return ret; +} +module_init(snd_ps3_init); + +static void __exit snd_ps3_exit(void) +{ + ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info); +} +module_exit(snd_ps3_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PS3 sound driver"); +MODULE_AUTHOR("Sony Computer Entertainment Inc."); +MODULE_ALIAS(PS3_MODULE_ALIAS_SOUND); diff --git a/sound/ppc/snd_ps3.h b/sound/ppc/snd_ps3.h new file mode 100644 index 00000000000..326fb29e82d --- /dev/null +++ b/sound/ppc/snd_ps3.h @@ -0,0 +1,136 @@ +/* + * Audio support for PS3 + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * All rights reserved. + * Copyright 2006, 2007 Sony Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 of the Licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(_SND_PS3_H_) +#define _SND_PS3_H_ + +#include <linux/irqreturn.h> + +#define SND_PS3_DRIVER_NAME "snd_ps3" + +enum snd_ps3_out_channel { + SND_PS3_OUT_SPDIF_0, + SND_PS3_OUT_SPDIF_1, + SND_PS3_OUT_SERIAL_0, + SND_PS3_OUT_DEVS +}; + +enum snd_ps3_dma_filltype { + SND_PS3_DMA_FILLTYPE_FIRSTFILL, + SND_PS3_DMA_FILLTYPE_RUNNING, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL, + SND_PS3_DMA_FILLTYPE_SILENT_RUNNING +}; + +enum snd_ps3_ch { + SND_PS3_CH_L = 0, + SND_PS3_CH_R = 1, + SND_PS3_CH_MAX = 2 +}; + +struct snd_ps3_avsetting_info { + uint32_t avs_audio_ch; /* fixed */ + uint32_t avs_audio_rate; + uint32_t avs_audio_width; + uint32_t avs_audio_format; /* fixed */ + uint32_t avs_audio_source; /* fixed */ + unsigned char avs_cs_info[8]; +}; +/* + * PS3 audio 'card' instance + * there should be only ONE hardware. + */ +struct snd_ps3_card_info { + struct ps3_system_bus_device *ps3_dev; + struct snd_card *card; + + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + + /* hvc info */ + u64 audio_lpar_addr; + u64 audio_lpar_size; + + /* registers */ + void __iomem *mapped_mmio_vaddr; + + /* irq */ + u64 audio_irq_outlet; + unsigned int irq_no; + + /* remember avsetting */ + struct snd_ps3_avsetting_info avs; + + /* dma buffer management */ + spinlock_t dma_lock; + /* dma_lock start */ + void * dma_start_vaddr[2]; /* 0 for L, 1 for R */ + dma_addr_t dma_start_bus_addr[2]; + size_t dma_buffer_size; + void * dma_last_transfer_vaddr[2]; + void * dma_next_transfer_vaddr[2]; + int silent; + /* dma_lock end */ + + int running; + + /* null buffer */ + void *null_buffer_start_vaddr; + dma_addr_t null_buffer_start_dma_addr; + + /* start delay */ + unsigned int start_delay; + +}; + + +/* PS3 audio DMAC block size in bytes */ +#define PS3_AUDIO_DMAC_BLOCK_SIZE (128) +/* one stage (stereo) of audio FIFO in bytes */ +#define PS3_AUDIO_FIFO_STAGE_SIZE (256) +/* how many stages the fifo have */ +#define PS3_AUDIO_FIFO_STAGE_COUNT (8) +/* fifo size 128 bytes * 8 stages * stereo (2ch) */ +#define PS3_AUDIO_FIFO_SIZE \ + (PS3_AUDIO_FIFO_STAGE_SIZE * PS3_AUDIO_FIFO_STAGE_COUNT) + +/* PS3 audio DMAC max block count in one dma shot = 128 (0x80) blocks*/ +#define PS3_AUDIO_DMAC_MAX_BLOCKS (PS3_AUDIO_DMASIZE_BLOCKS_MASK + 1) + +#define PS3_AUDIO_NORMAL_DMA_START_CH (0) +#define PS3_AUDIO_NORMAL_DMA_COUNT (8) +#define PS3_AUDIO_NULL_DMA_START_CH \ + (PS3_AUDIO_NORMAL_DMA_START_CH + PS3_AUDIO_NORMAL_DMA_COUNT) +#define PS3_AUDIO_NULL_DMA_COUNT (2) + +#define SND_PS3_MAX_VOL (0x0F) +#define SND_PS3_MIN_VOL (0x00) +#define SND_PS3_MIN_ATT SND_PS3_MIN_VOL +#define SND_PS3_MAX_ATT SND_PS3_MAX_VOL + +#define SND_PS3_PCM_PREALLOC_SIZE \ + (PS3_AUDIO_DMAC_BLOCK_SIZE * PS3_AUDIO_DMAC_MAX_BLOCKS * 4) + +#define SND_PS3_DMA_REGION_SIZE \ + (SND_PS3_PCM_PREALLOC_SIZE + PAGE_SIZE) + +#define PS3_AUDIO_IOID (1UL) + +#endif /* _SND_PS3_H_ */ diff --git a/sound/ppc/snd_ps3_reg.h b/sound/ppc/snd_ps3_reg.h new file mode 100644 index 00000000000..2e630207956 --- /dev/null +++ b/sound/ppc/snd_ps3_reg.h @@ -0,0 +1,891 @@ +/* + * Audio support for PS3 + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * interrupt / configure registers + */ + +#define PS3_AUDIO_INTR_0 (0x00000100) +#define PS3_AUDIO_INTR_EN_0 (0x00000140) +#define PS3_AUDIO_CONFIG (0x00000200) + +/* + * DMAC registers + * n:0..9 + */ +#define PS3_AUDIO_DMAC_REGBASE(x) (0x0000210 + 0x20 * (x)) + +#define PS3_AUDIO_KICK(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x00) +#define PS3_AUDIO_SOURCE(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x04) +#define PS3_AUDIO_DEST(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x08) +#define PS3_AUDIO_DMASIZE(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x0C) + +/* + * mute control + */ +#define PS3_AUDIO_AX_MCTRL (0x00004000) +#define PS3_AUDIO_AX_ISBP (0x00004004) +#define PS3_AUDIO_AX_AOBP (0x00004008) +#define PS3_AUDIO_AX_IC (0x00004010) +#define PS3_AUDIO_AX_IE (0x00004014) +#define PS3_AUDIO_AX_IS (0x00004018) + +/* + * three wire serial + * n:0..3 + */ +#define PS3_AUDIO_AO_MCTRL (0x00006000) +#define PS3_AUDIO_AO_3WMCTRL (0x00006004) + +#define PS3_AUDIO_AO_3WCTRL(n) (0x00006200 + 0x200 * (n)) + +/* + * S/PDIF + * n:0..1 + * x:0..11 + * y:0..5 + */ +#define PS3_AUDIO_AO_SPD_REGBASE(n) (0x00007200 + 0x200 * (n)) + +#define PS3_AUDIO_AO_SPDCTRL(n) \ + (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x00) +#define PS3_AUDIO_AO_SPDUB(n, x) \ + (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x04 + 0x04 * (x)) +#define PS3_AUDIO_AO_SPDCS(n, y) \ + (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x34 + 0x04 * (y)) + + +/* + PS3_AUDIO_INTR_0 register tells an interrupt handler which audio + DMA channel triggered the interrupt. The interrupt status for a channel + can be cleared by writing a '1' to the corresponding bit. A new interrupt + cannot be generated until the previous interrupt has been cleared. + + Note that the status reported by PS3_AUDIO_INTR_0 is independent of the + value of PS3_AUDIO_INTR_EN_0. + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +#define PS3_AUDIO_INTR_0_CHAN(n) (1 << ((n) * 2)) +#define PS3_AUDIO_INTR_0_CHAN9 PS3_AUDIO_INTR_0_CHAN(9) +#define PS3_AUDIO_INTR_0_CHAN8 PS3_AUDIO_INTR_0_CHAN(8) +#define PS3_AUDIO_INTR_0_CHAN7 PS3_AUDIO_INTR_0_CHAN(7) +#define PS3_AUDIO_INTR_0_CHAN6 PS3_AUDIO_INTR_0_CHAN(6) +#define PS3_AUDIO_INTR_0_CHAN5 PS3_AUDIO_INTR_0_CHAN(5) +#define PS3_AUDIO_INTR_0_CHAN4 PS3_AUDIO_INTR_0_CHAN(4) +#define PS3_AUDIO_INTR_0_CHAN3 PS3_AUDIO_INTR_0_CHAN(3) +#define PS3_AUDIO_INTR_0_CHAN2 PS3_AUDIO_INTR_0_CHAN(2) +#define PS3_AUDIO_INTR_0_CHAN1 PS3_AUDIO_INTR_0_CHAN(1) +#define PS3_AUDIO_INTR_0_CHAN0 PS3_AUDIO_INTR_0_CHAN(0) + +/* + The PS3_AUDIO_INTR_EN_0 register specifies which DMA channels can generate + an interrupt to the PU. Each bit of PS3_AUDIO_INTR_EN_0 is ANDed with the + corresponding bit in PS3_AUDIO_INTR_0. The resulting bits are OR'd together + to generate the Audio interrupt. + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_EN_0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + + Bit assignments are same as PS3_AUDIO_INTR_0 +*/ + +/* + PS3_AUDIO_CONFIG + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 C|0 0 0 0 0 0 0 0| CONFIG + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ + +/* The CLEAR field cancels all pending transfers, and stops any running DMA + transfers. Any interrupts associated with the canceled transfers + will occur as if the transfer had finished. + Since this bit is designed to recover from DMA related issues + which are caused by unpredictable situations, it is preferred to wait + for normal DMA transfer end without using this bit. +*/ +#define PS3_AUDIO_CONFIG_CLEAR (1 << 8) /* RWIVF */ + +/* + PS3_AUDIO_AX_MCTRL: Audio Port Mute Control Register + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|A|A|0 0 0 0 0 0 0|S|S|A|A|A|A| AX_MCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* 3 Wire Audio Serial Output Channel Mutes (0..3) */ +#define PS3_AUDIO_AX_MCTRL_ASOMT(n) (1 << (3 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO3MT (1 << 0) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO2MT (1 << 1) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO1MT (1 << 2) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO0MT (1 << 3) /* RWIVF */ + +/* S/PDIF mutes (0,1)*/ +#define PS3_AUDIO_AX_MCTRL_SPOMT(n) (1 << (5 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_SPO1MT (1 << 4) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_SPO0MT (1 << 5) /* RWIVF */ + +/* All 3 Wire Serial Outputs Mute */ +#define PS3_AUDIO_AX_MCTRL_AASOMT (1 << 13) /* RWIVF */ + +/* All S/PDIF Mute */ +#define PS3_AUDIO_AX_MCTRL_ASPOMT (1 << 14) /* RWIVF */ + +/* All Audio Outputs Mute */ +#define PS3_AUDIO_AX_MCTRL_AAOMT (1 << 15) /* RWIVF */ + +/* + S/PDIF Outputs Buffer Read/Write Pointer Register + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B|0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B| AX_ISBP + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ +/* + S/PDIF Output Channel Read Buffer Numbers + Buffer number is value of field. + Indicates current read access buffer ID from Audio Data + Transfer controller of S/PDIF Output +*/ + +#define PS3_AUDIO_AX_ISBP_SPOBRN_MASK(n) (0x7 << 4 * (1 - (n))) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO1BRN_MASK (0x7 << 0) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO0BRN_MASK (0x7 << 4) /* R-IUF */ + +/* +S/PDIF Output Channel Buffer Write Numbers +Indicates current write access buffer ID from bus master. +*/ +#define PS3_AUDIO_AX_ISBP_SPOBWN_MASK(n) (0x7 << 4 * (5 - (n))) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO1BWN_MASK (0x7 << 16) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO0BWN_MASK (0x7 << 20) /* R-IUF */ + +/* + 3 Wire Audio Serial Outputs Buffer Read/Write + Pointer Register + Buffer number is value of field + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B|0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B| AX_AOBP + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +3 Wire Audio Serial Output Channel Buffer Read Numbers +Indicates current read access buffer Id from Audio Data Transfer +Controller of 3 Wire Audio Serial Output Channels +*/ +#define PS3_AUDIO_AX_AOBP_ASOBRN_MASK(n) (0x7 << 4 * (3 - (n))) /* R-IUF */ + +#define PS3_AUDIO_AX_AOBP_ASO3BRN_MASK (0x7 << 0) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO2BRN_MASK (0x7 << 4) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO1BRN_MASK (0x7 << 8) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO0BRN_MASK (0x7 << 12) /* R-IUF */ + +/* +3 Wire Audio Serial Output Channel Buffer Write Numbers +Indicates current write access buffer ID from bus master. +*/ +#define PS3_AUDIO_AX_AOBP_ASOBWN_MASK(n) (0x7 << 4 * (7 - (n))) /* R-IUF */ + +#define PS3_AUDIO_AX_AOBP_ASO3BWN_MASK (0x7 << 16) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO2BWN_MASK (0x7 << 20) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO1BWN_MASK (0x7 << 24) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO0BWN_MASK (0x7 << 28) /* R-IUF */ + + + +/* +Audio Port Interrupt Condition Register +For the fields in this register, the following values apply: +0 = Interrupt is generated every interrupt event. +1 = Interrupt is generated every 2 interrupt events. +2 = Interrupt is generated every 4 interrupt events. +3 = Reserved + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|0 0|SPO|0 0|SPO|0 0|AAS|0 0 0 0 0 0 0 0 0 0 0 0| AX_IC + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/* +All 3-Wire Audio Serial Outputs Interrupt Mode +Configures the Interrupt and Signal Notification +condition of all 3-wire Audio Serial Outputs. +*/ +#define PS3_AUDIO_AX_IC_AASOIMD_MASK (0x3 << 12) /* RWIVF */ +#define PS3_AUDIO_AX_IC_AASOIMD_EVERY1 (0x0 << 12) /* RWI-V */ +#define PS3_AUDIO_AX_IC_AASOIMD_EVERY2 (0x1 << 12) /* RW--V */ +#define PS3_AUDIO_AX_IC_AASOIMD_EVERY4 (0x2 << 12) /* RW--V */ + +/* +S/PDIF Output Channel Interrupt Modes +Configures the Interrupt and signal Notification +conditions of S/PDIF output channels. +*/ +#define PS3_AUDIO_AX_IC_SPO1IMD_MASK (0x3 << 16) /* RWIVF */ +#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY1 (0x0 << 16) /* RWI-V */ +#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY2 (0x1 << 16) /* RW--V */ +#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY4 (0x2 << 16) /* RW--V */ + +#define PS3_AUDIO_AX_IC_SPO0IMD_MASK (0x3 << 20) /* RWIVF */ +#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY1 (0x0 << 20) /* RWI-V */ +#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY2 (0x1 << 20) /* RW--V */ +#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY4 (0x2 << 20) /* RW--V */ + +/* +Audio Port interrupt Enable Register +Configures whether to enable or disable each Interrupt Generation. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IE + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ + +/* +3 Wire Audio Serial Output Channel Buffer Underflow +Interrupt Enables +Select enable/disable of Buffer Underflow Interrupts for +3-Wire Audio Serial Output Channels +DISABLED=Interrupt generation disabled. +*/ +#define PS3_AUDIO_AX_IE_ASOBUIE(n) (1 << (3 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO3BUIE (1 << 0) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO2BUIE (1 << 1) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO1BUIE (1 << 2) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO0BUIE (1 << 3) /* RWIVF */ + +/* S/PDIF Output Channel Buffer Underflow Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_SPOBUIE(n) (1 << (7 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO1BUIE (1 << 6) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO0BUIE (1 << 7) /* RWIVF */ + +/* S/PDIF Output Channel One Block Transfer Completion Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_SPOBTCIE(n) (1 << (11 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO1BTCIE (1 << 10) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO0BTCIE (1 << 11) /* RWIVF */ + +/* 3-Wire Audio Serial Output Channel Buffer Empty Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_ASOBEIE(n) (1 << (19 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO3BEIE (1 << 16) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO2BEIE (1 << 17) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO1BEIE (1 << 18) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO0BEIE (1 << 19) /* RWIVF */ + +/* S/PDIF Output Channel Buffer Empty Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_SPOBEIE(n) (1 << (23 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO1BEIE (1 << 22) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO0BEIE (1 << 23) /* RWIVF */ + +/* +Audio Port Interrupt Status Register +Indicates Interrupt status, which interrupt has occurred, and can clear +each interrupt in this register. +Writing 1b to a field containing 1b clears field and de-asserts interrupt. +Writing 0b to a field has no effect. +Field vaules are the following: +0 - Interrupt hasn't occurred. +1 - Interrupt has occurred. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IS + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + + Bit assignment are same as AX_IE +*/ + +/* +Audio Output Master Control Register +Configures Master Clock and other master Audio Output Settings + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0|SCKSE|0|SCKSE| MR0 | MR1 |MCL|MCL|0 0 0 0|0 0 0 0 0 0 0 0| AO_MCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +MCLK Output Control +Controls mclko[1] output. +0 - Disable output (fixed at High) +1 - Output clock produced by clock selected +with scksel1 by mr1 +2 - Reserved +3 - Reserved +*/ + +#define PS3_AUDIO_AO_MCTRL_MCLKC1_MASK (0x3 << 12) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_DISABLED (0x0 << 12) /* RWI-V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_ENABLED (0x1 << 12) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD2 (0x2 << 12) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD3 (0x3 << 12) /* RW--V */ + +/* +MCLK Output Control +Controls mclko[0] output. +0 - Disable output (fixed at High) +1 - Output clock produced by clock selected +with SCKSEL0 by MR0 +2 - Reserved +3 - Reserved +*/ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_MASK (0x3 << 14) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_DISABLED (0x0 << 14) /* RWI-V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_ENABLED (0x1 << 14) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD2 (0x2 << 14) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD3 (0x3 << 14) /* RW--V */ +/* +Master Clock Rate 1 +Sets the divide ration of Master Clock1 (clock output from +mclko[1] for the input clock selected by scksel1. +*/ +#define PS3_AUDIO_AO_MCTRL_MR1_MASK (0xf << 16) +#define PS3_AUDIO_AO_MCTRL_MR1_DEFAULT (0x0 << 16) /* RWI-V */ +/* +Master Clock Rate 0 +Sets the divide ratio of Master Clock0 (clock output from +mclko[0] for the input clock selected by scksel0). +*/ +#define PS3_AUDIO_AO_MCTRL_MR0_MASK (0xf << 20) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_MR0_DEFAULT (0x0 << 20) /* RWI-V */ +/* +System Clock Select 0/1 +Selects the system clock to be used as Master Clock 0/1 +Input the system clock that is appropriate for the sampling +rate. +*/ +#define PS3_AUDIO_AO_MCTRL_SCKSEL1_MASK (0x7 << 24) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_SCKSEL1_DEFAULT (0x2 << 24) /* RWI-V */ + +#define PS3_AUDIO_AO_MCTRL_SCKSEL0_MASK (0x7 << 28) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_SCKSEL0_DEFAULT (0x2 << 28) /* RWI-V */ + + +/* +3-Wire Audio Output Master Control Register +Configures clock, 3-Wire Audio Serial Output Enable, and +other 3-Wire Audio Serial Output Master Settings + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |A|A|A|A|0 0 0|A| ASOSR |0 0 0 0|A|A|A|A|A|A|0|1|0 0 0 0 0 0 0 0| AO_3WMCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + + +/* +LRCKO Polarity +0 - Reserved +1 - default +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK (1 << 8) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT (1 << 8) /* RW--V */ + +/* LRCK Output Disable */ + +#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD (1 << 10) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_ENABLED (0 << 10) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED (1 << 10) /* RWI-V */ + +/* Bit Clock Output Disable */ + +#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD (1 << 11) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_ENABLED (0 << 11) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED (1 << 11) /* RWI-V */ + +/* +3-Wire Audio Serial Output Channel 0-3 Operational +Status. Each bit becomes 1 after each 3-Wire Audio +Serial Output Channel N is in action by setting 1 to +asoen. +Each bit becomes 0 after each 3-Wire Audio Serial Output +Channel N is out of action by setting 0 to asoen. +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN(n) (1 << (15 - (n))) /* R-IVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(n) (0 << (15 - (n))) /* R-I-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(n) (1 << (15 - (n))) /* R---V */ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN0 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(0) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(0) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(0) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN1 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(1) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(1) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(1) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN2 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(2) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(2) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(2) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN3 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(3) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(3) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(3) + +/* +Sampling Rate +Specifies the divide ratio of the bit clock (clock output +from bclko) used by the 3-wire Audio Output Clock, which +is applied to the master clock selected by mcksel. +Data output is synchronized with this clock. +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_MASK (0xf << 20) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV2 (0x1 << 20) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV4 (0x2 << 20) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV8 (0x4 << 20) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV12 (0x6 << 20) /* RW--V */ + +/* +Master Clock Select +0 - Master Clock 0 +1 - Master Clock 1 +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL (1 << 24) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK0 (0 << 24) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK1 (1 << 24) /* RW--V */ + +/* +Enables and disables 4ch 3-Wire Audio Serial Output +operation. Each Bit from 0 to 3 corresponds to an +output channel, which means that each output channel +can be enabled or disabled individually. When +multiple channels are enabled at the same time, output +operations are performed in synchronization. +Bit 0 - Output Channel 0 (SDOUT[0]) +Bit 1 - Output Channel 1 (SDOUT[1]) +Bit 2 - Output Channel 2 (SDOUT[2]) +Bit 3 - Output Channel 3 (SDOUT[3]) +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN(n) (1 << (31 - (n))) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(n) (0 << (31 - (n))) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(n) (1 << (31 - (n))) /* RW--V */ + +#define PS3_AUDIO_AO_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(0) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(0) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(0) /* RW--V */ +#define PS3_AUDIO_A1_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(1) /* RWIVF */ +#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(1) /* RWI-V */ +#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(1) /* RW--V */ +#define PS3_AUDIO_A2_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(2) /* RWIVF */ +#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(2) /* RWI-V */ +#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(2) /* RW--V */ +#define PS3_AUDIO_A3_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(3) /* RWIVF */ +#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(3) /* RWI-V */ +#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(3) /* RW--V */ + +/* +3-Wire Audio Serial output Channel 0-3 Control Register +Configures settings for 3-Wire Serial Audio Output Channel 0-3 + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|0 0 0 0|A|0|ASO|0 0 0|0|0|0|0|0| AO_3WCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ +/* +Data Bit Mode +Specifies the number of data bits +0 - 16 bits +1 - reserved +2 - 20 bits +3 - 24 bits +*/ +#define PS3_AUDIO_AO_3WCTRL_ASODB_MASK (0x3 << 8) /* RWIVF */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_16BIT (0x0 << 8) /* RWI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_RESVD (0x1 << 8) /* RWI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_20BIT (0x2 << 8) /* RW--V */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_24BIT (0x3 << 8) /* RW--V */ +/* +Data Format Mode +Specifies the data format where (LSB side or MSB) the data(in 20 bit +or 24 bit resolution mode) is put in a 32 bit field. +0 - Data put on LSB side +1 - Data put on MSB side +*/ +#define PS3_AUDIO_AO_3WCTRL_ASODF (1 << 11) /* RWIVF */ +#define PS3_AUDIO_AO_3WCTRL_ASODF_LSB (0 << 11) /* RWI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASODF_MSB (1 << 11) /* RW--V */ +/* +Buffer Reset +Performs buffer reset. Writing 1 to this bit initializes the +corresponding 3-Wire Audio Output buffers(both L and R). +*/ +#define PS3_AUDIO_AO_3WCTRL_ASOBRST (1 << 16) /* CWIVF */ +#define PS3_AUDIO_AO_3WCTRL_ASOBRST_IDLE (0 << 16) /* -WI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET (1 << 16) /* -W--T */ + +/* +S/PDIF Audio Output Channel 0/1 Control Register +Configures settings for S/PDIF Audio Output Channel 0/1. + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |S|0 0 0|S|0 0|S| SPOSR |0 0|SPO|0 0 0 0|S|0|SPO|0 0 0 0 0 0 0|S| AO_SPDCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/* +Buffer reset. Writing 1 to this bit initializes the +corresponding S/PDIF output buffer pointer. +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOBRST (1 << 0) /* CWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_IDLE (0 << 0) /* -WI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_RESET (1 << 0) /* -W--T */ + +/* +Data Bit Mode +Specifies number of data bits +0 - 16 bits +1 - Reserved +2 - 20 bits +3 - 24 bits +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_MASK (0x3 << 8) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_16BIT (0x0 << 8) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_RESVD (0x1 << 8) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_20BIT (0x2 << 8) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_24BIT (0x3 << 8) /* RW--V */ +/* +Data format Mode +Specifies the data format, where (LSB side or MSB) +the data(in 20 or 24 bit resolution) is put in the +32 bit field. +0 - LSB Side +1 - MSB Side +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPODF (1 << 11) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPODF_LSB (0 << 11) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODF_MSB (1 << 11) /* RW--V */ +/* +Source Select +Specifies the source of the S/PDIF output. When 0, output +operation is controlled by 3wen[0] of AO_3WMCTRL register. +The SR must have the same setting as the a0_3wmctrl reg. +0 - 3-Wire Audio OUT Ch0 Buffer +1 - S/PDIF buffer +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOSS_MASK (0x3 << 16) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSS_3WEN (0x0 << 16) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSS_SPDIF (0x1 << 16) /* RW--V */ +/* +Sampling Rate +Specifies the divide ratio of the bit clock (clock output +from bclko) used by the S/PDIF Output Clock, which +is applied to the master clock selected by mcksel. +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR (0xf << 20) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV2 (0x1 << 20) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV4 (0x2 << 20) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV8 (0x4 << 20) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV12 (0x6 << 20) /* RW--V */ +/* +Master Clock Select +0 - Master Clock 0 +1 - Master Clock 1 +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL (1 << 24) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK0 (0 << 24) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK1 (1 << 24) /* RW--V */ + +/* +S/PDIF Output Channel Operational Status +This bit becomes 1 after S/PDIF Output Channel is in +action by setting 1 to spoen. This bit becomes 0 +after S/PDIF Output Channel is out of action by setting +0 to spoen. +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPORUN (1 << 27) /* R-IVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPORUN_STOPPED (0 << 27) /* R-I-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPORUN_RUNNING (1 << 27) /* R---V */ + +/* +S/PDIF Audio Output Channel Output Enable +Enables and disables output operation. This bit is used +only when sposs = 1 +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOEN (1 << 31) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOEN_DISABLED (0 << 31) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOEN_ENABLED (1 << 31) /* RW--V */ + +/* +S/PDIF Audio Output Channel Channel Status +Setting Registers. +Configures channel status bit settings for each block +(192 bits). +Output is performed from the MSB(AO_SPDCS0 register bit 31). +The same value is added for subframes within the same frame. + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | SPOCS | AO_SPDCS + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +S/PDIF Audio Output Channel User Bit Setting +Configures user bit settings for each block (384 bits). +Output is performed from the MSB(ao_spdub0 register bit 31). + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | SPOUB | AO_SPDUB + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/***************************************************************************** + * + * DMAC register + * + *****************************************************************************/ +/* +The PS3_AUDIO_KICK register is used to initiate a DMA transfer and monitor +its status + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0|STATU|0 0 0| EVENT |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|R| KICK + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/* +The REQUEST field is written to ACTIVE to initiate a DMA request when EVENT +occurs. +It will return to the DONE state when the request is completed. +The registers for a DMA channel should only be written if REQUEST is IDLE. +*/ + +#define PS3_AUDIO_KICK_REQUEST (1 << 0) /* RWIVF */ +#define PS3_AUDIO_KICK_REQUEST_IDLE (0 << 0) /* RWI-V */ +#define PS3_AUDIO_KICK_REQUEST_ACTIVE (1 << 0) /* -W--T */ + +/* + *The EVENT field is used to set the event in which + *the DMA request becomes active. + */ +#define PS3_AUDIO_KICK_EVENT_MASK (0x1f << 16) /* RWIVF */ +#define PS3_AUDIO_KICK_EVENT_ALWAYS (0x00 << 16) /* RWI-V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY (0x01 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_UNDERFLOW (0x02 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_EMPTY (0x03 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_UNDERFLOW (0x04 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_EMPTY (0x05 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_UNDERFLOW (0x06 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_EMPTY (0x07 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_UNDERFLOW (0x08 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF0_BLOCKTRANSFERCOMPLETE \ + (0x09 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF0_UNDERFLOW (0x0A << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF0_EMPTY (0x0B << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF1_BLOCKTRANSFERCOMPLETE \ + (0x0C << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF1_UNDERFLOW (0x0D << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF1_EMPTY (0x0E << 16) /* RW--V */ + +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA(n) \ + ((0x13 + (n)) << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA0 (0x13 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA1 (0x14 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA2 (0x15 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA3 (0x16 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA4 (0x17 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA5 (0x18 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA6 (0x19 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA7 (0x1A << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA8 (0x1B << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA9 (0x1C << 16) /* RW--V */ + +/* +The STATUS field can be used to monitor the progress of a DMA request. +DONE indicates the previous request has completed. +EVENT indicates that the DMA engine is waiting for the EVENT to occur. +PENDING indicates that the DMA engine has not started processing this +request, but the EVENT has occurred. +DMA indicates that the data transfer is in progress. +NOTIFY indicates that the notifier signalling end of transfer is being written. +CLEAR indicated that the previous transfer was cleared. +ERROR indicates the previous transfer requested an unsupported +source/destination combination. +*/ + +#define PS3_AUDIO_KICK_STATUS_MASK (0x7 << 24) /* R-IVF */ +#define PS3_AUDIO_KICK_STATUS_DONE (0x0 << 24) /* R-I-V */ +#define PS3_AUDIO_KICK_STATUS_EVENT (0x1 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_PENDING (0x2 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_DMA (0x3 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_NOTIFY (0x4 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_CLEAR (0x5 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_ERROR (0x6 << 24) /* R---V */ + +/* +The PS3_AUDIO_SOURCE register specifies the source address for transfers. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | START |0 0 0 0 0|TAR| SOURCE + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +The Audio DMA engine uses 128-byte transfers, thus the address must be aligned +to a 128 byte boundary. The low seven bits are assumed to be 0. +*/ + +#define PS3_AUDIO_SOURCE_START_MASK (0x01FFFFFF << 7) /* RWIUF */ + +/* +The TARGET field specifies the memory space containing the source address. +*/ + +#define PS3_AUDIO_SOURCE_TARGET_MASK (3 << 0) /* RWIVF */ +#define PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY (2 << 0) /* RW--V */ + +/* +The PS3_AUDIO_DEST register specifies the destination address for transfers. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | START |0 0 0 0 0|TAR| DEST + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +The Audio DMA engine uses 128-byte transfers, thus the address must be aligned +to a 128 byte boundary. The low seven bits are assumed to be 0. +*/ + +#define PS3_AUDIO_DEST_START_MASK (0x01FFFFFF << 7) /* RWIUF */ + +/* +The TARGET field specifies the memory space containing the destination address +AUDIOFIFO = Audio WriteData FIFO, +*/ + +#define PS3_AUDIO_DEST_TARGET_MASK (3 << 0) /* RWIVF */ +#define PS3_AUDIO_DEST_TARGET_AUDIOFIFO (1 << 0) /* RW--V */ + +/* +PS3_AUDIO_DMASIZE specifies the number of 128-byte blocks + 1 to transfer. +So a value of 0 means 128-bytes will get transferred. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0| BLOCKS | DMASIZE + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + + +#define PS3_AUDIO_DMASIZE_BLOCKS_MASK (0x7f << 0) /* RWIUF */ + +/* + * source/destination address for internal fifos + */ +#define PS3_AUDIO_AO_3W_LDATA(n) (0x1000 + (0x100 * (n))) +#define PS3_AUDIO_AO_3W_RDATA(n) (0x1080 + (0x100 * (n))) + +#define PS3_AUDIO_AO_SPD_DATA(n) (0x2000 + (0x400 * (n))) + + +/* + * field attiribute + * + * Read + * ' ' = Other Information + * '-' = Field is part of a write-only register + * 'C' = Value read is always the same, constant value line follows (C) + * 'R' = Value is read + * + * Write + * ' ' = Other Information + * '-' = Must not be written (D), value ignored when written (R,A,F) + * 'W' = Can be written + * + * Internal State + * ' ' = Other Information + * '-' = No internal state + * 'X' = Internal state, initial value is unknown + * 'I' = Internal state, initial value is known and follows (I) + * + * Declaration/Size + * ' ' = Other Information + * '-' = Does Not Apply + * 'V' = Type is void + * 'U' = Type is unsigned integer + * 'S' = Type is signed integer + * 'F' = Type is IEEE floating point + * '1' = Byte size (008) + * '2' = Short size (016) + * '3' = Three byte size (024) + * '4' = Word size (032) + * '8' = Double size (064) + * + * Define Indicator + * ' ' = Other Information + * 'D' = Device + * 'M' = Memory + * 'R' = Register + * 'A' = Array of Registers + * 'F' = Field + * 'V' = Value + * 'T' = Task + */ + diff --git a/sound/ppc/toonie.c b/sound/ppc/toonie.c deleted file mode 100644 index 082bc4babab..00000000000 --- a/sound/ppc/toonie.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Mac Mini "toonie" mixer control - * - * Copyright (c) 2005 by Benjamin Herrenschmidt <benh@kernel.crashing.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#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 <sound/core.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/machdep.h> -#include <asm/pmac_feature.h> -#include "pmac.h" - -#undef DEBUG - -#ifdef DEBUG -#define DBG(fmt...) printk(fmt) -#else -#define DBG(fmt...) -#endif - -struct pmac_gpio { - unsigned int addr; - u8 active_val; - u8 inactive_val; - u8 active_state; -}; - -struct pmac_toonie -{ - struct pmac_gpio hp_detect_gpio; - struct pmac_gpio hp_mute_gpio; - struct pmac_gpio amp_mute_gpio; - int hp_detect_irq; - int auto_mute_notify; - struct work_struct detect_work; -}; - - -/* - * gpio access - */ -#define do_gpio_write(gp, val) \ - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, (gp)->addr, val) -#define do_gpio_read(gp) \ - pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, (gp)->addr, 0) -#define tumbler_gpio_free(gp) /* NOP */ - -static void write_audio_gpio(struct pmac_gpio *gp, int active) -{ - if (! gp->addr) - return; - active = active ? gp->active_val : gp->inactive_val; - do_gpio_write(gp, active); - DBG("(I) gpio %x write %d\n", gp->addr, active); -} - -static int check_audio_gpio(struct pmac_gpio *gp) -{ - int ret; - - if (! gp->addr) - return 0; - - ret = do_gpio_read(gp); - - return (ret & 0xd) == (gp->active_val & 0xd); -} - -static int read_audio_gpio(struct pmac_gpio *gp) -{ - int ret; - if (! gp->addr) - return 0; - ret = ((do_gpio_read(gp) & 0x02) !=0); - return ret == gp->active_state; -} - - -enum { TOONIE_MUTE_HP, TOONIE_MUTE_AMP }; - -static int toonie_get_mute_switch(snd_kcontrol_t *kcontrol, - snd_ctl_elem_value_t *ucontrol) -{ - pmac_t *chip = snd_kcontrol_chip(kcontrol); - struct pmac_toonie *mix = chip->mixer_data; - struct pmac_gpio *gp; - - if (mix == NULL) - return -ENODEV; - switch(kcontrol->private_value) { - case TOONIE_MUTE_HP: - gp = &mix->hp_mute_gpio; - break; - case TOONIE_MUTE_AMP: - gp = &mix->amp_mute_gpio; - break; - default: - return -EINVAL;; - } - ucontrol->value.integer.value[0] = !check_audio_gpio(gp); - return 0; -} - -static int toonie_put_mute_switch(snd_kcontrol_t *kcontrol, - snd_ctl_elem_value_t *ucontrol) -{ - pmac_t *chip = snd_kcontrol_chip(kcontrol); - struct pmac_toonie *mix = chip->mixer_data; - struct pmac_gpio *gp; - int val; - - if (chip->update_automute && chip->auto_mute) - return 0; /* don't touch in the auto-mute mode */ - - if (mix == NULL) - return -ENODEV; - - switch(kcontrol->private_value) { - case TOONIE_MUTE_HP: - gp = &mix->hp_mute_gpio; - break; - case TOONIE_MUTE_AMP: - gp = &mix->amp_mute_gpio; - break; - default: - return -EINVAL;; - } - val = ! check_audio_gpio(gp); - if (val != ucontrol->value.integer.value[0]) { - write_audio_gpio(gp, ! ucontrol->value.integer.value[0]); - return 1; - } - return 0; -} - -static snd_kcontrol_new_t toonie_hp_sw __initdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Headphone Playback Switch", - .info = snd_pmac_boolean_mono_info, - .get = toonie_get_mute_switch, - .put = toonie_put_mute_switch, - .private_value = TOONIE_MUTE_HP, -}; -static snd_kcontrol_new_t toonie_speaker_sw __initdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PC Speaker Playback Switch", - .info = snd_pmac_boolean_mono_info, - .get = toonie_get_mute_switch, - .put = toonie_put_mute_switch, - .private_value = TOONIE_MUTE_AMP, -}; - -/* - * auto-mute stuffs - */ -static int toonie_detect_headphone(pmac_t *chip) -{ - struct pmac_toonie *mix = chip->mixer_data; - int detect = 0; - - if (mix->hp_detect_gpio.addr) - detect |= read_audio_gpio(&mix->hp_detect_gpio); - return detect; -} - -static void toonie_check_mute(pmac_t *chip, struct pmac_gpio *gp, int val, - int do_notify, snd_kcontrol_t *sw) -{ - if (check_audio_gpio(gp) != val) { - write_audio_gpio(gp, val); - if (do_notify) - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &sw->id); - } -} - -static void toonie_detect_handler(void *self) -{ - pmac_t *chip = (pmac_t*) self; - struct pmac_toonie *mix; - int headphone; - - if (!chip) - return; - - mix = chip->mixer_data; - snd_assert(mix, return); - - headphone = toonie_detect_headphone(chip); - - DBG("headphone: %d, lineout: %d\n", headphone, lineout); - - if (headphone) { - /* unmute headphone/lineout & mute speaker */ - toonie_check_mute(chip, &mix->hp_mute_gpio, 0, - mix->auto_mute_notify, chip->master_sw_ctl); - toonie_check_mute(chip, &mix->amp_mute_gpio, 1, - mix->auto_mute_notify, chip->speaker_sw_ctl); - } else { - /* unmute speaker, mute others */ - toonie_check_mute(chip, &mix->amp_mute_gpio, 0, - mix->auto_mute_notify, chip->speaker_sw_ctl); - toonie_check_mute(chip, &mix->hp_mute_gpio, 1, - mix->auto_mute_notify, chip->master_sw_ctl); - } - if (mix->auto_mute_notify) { - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->hp_detect_ctl->id); - } -} - -static void toonie_update_automute(pmac_t *chip, int do_notify) -{ - if (chip->auto_mute) { - struct pmac_toonie *mix; - mix = chip->mixer_data; - snd_assert(mix, return); - mix->auto_mute_notify = do_notify; - schedule_work(&mix->detect_work); - } -} - -/* interrupt - headphone plug changed */ -static irqreturn_t toonie_hp_intr(int irq, void *devid, struct pt_regs *regs) -{ - pmac_t *chip = devid; - - if (chip->update_automute && chip->initialized) { - chip->update_automute(chip, 1); - return IRQ_HANDLED; - } - return IRQ_NONE; -} - -/* look for audio gpio device */ -static int find_audio_gpio(const char *name, const char *platform, - struct pmac_gpio *gp) -{ - struct device_node *np; - u32 *base, addr; - - if (! (np = find_devices("gpio"))) - return -ENODEV; - - for (np = np->child; np; np = np->sibling) { - char *property = get_property(np, "audio-gpio", NULL); - if (property && strcmp(property, name) == 0) - break; - if (device_is_compatible(np, name)) - break; - } - if (np == NULL) - return -ENODEV; - - base = (u32 *)get_property(np, "AAPL,address", NULL); - if (! base) { - base = (u32 *)get_property(np, "reg", NULL); - if (!base) { - DBG("(E) cannot find address for device %s !\n", name); - return -ENODEV; - } - addr = *base; - if (addr < 0x50) - addr += 0x50; - } else - addr = *base; - - gp->addr = addr & 0x0000ffff; - - /* Try to find the active state, default to 0 ! */ - base = (u32 *)get_property(np, "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; - gp->active_val = 0x4; - gp->inactive_val = 0x5; - /* Here are some crude hacks to extract the GPIO polarity and - * open collector informations out of the do-platform script - * as we don't yet have an interpreter for these things - */ - if (platform) - prop = (u32 *)get_property(np, platform, NULL); - if (prop) { - if (prop[3] == 0x9 && prop[4] == 0x9) { - gp->active_val = 0xd; - gp->inactive_val = 0xc; - } - if (prop[3] == 0x1 && prop[4] == 0x1) { - gp->active_val = 0x5; - gp->inactive_val = 0x4; - } - } - } - - DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n", - name, gp->addr, gp->active_state); - - return (np->n_intrs > 0) ? np->intrs[0].line : 0; -} - -static void toonie_cleanup(pmac_t *chip) -{ - struct pmac_toonie *mix = chip->mixer_data; - if (! mix) - return; - if (mix->hp_detect_irq >= 0) - free_irq(mix->hp_detect_irq, chip); - kfree(mix); - chip->mixer_data = NULL; -} - -int snd_pmac_toonie_init(pmac_t *chip) -{ - struct pmac_toonie *mix; - - mix = kmalloc(sizeof(*mix), GFP_KERNEL); - if (! mix) - return -ENOMEM; - - chip->mixer_data = mix; - chip->mixer_free = toonie_cleanup; - - find_audio_gpio("headphone-mute", NULL, &mix->hp_mute_gpio); - find_audio_gpio("amp-mute", NULL, &mix->amp_mute_gpio); - mix->hp_detect_irq = find_audio_gpio("headphone-detect", - NULL, &mix->hp_detect_gpio); - - strcpy(chip->card->mixername, "PowerMac Toonie"); - - chip->master_sw_ctl = snd_ctl_new1(&toonie_hp_sw, chip); - snd_ctl_add(chip->card, chip->master_sw_ctl); - - chip->speaker_sw_ctl = snd_ctl_new1(&toonie_speaker_sw, chip); - snd_ctl_add(chip->card, chip->speaker_sw_ctl); - - INIT_WORK(&mix->detect_work, toonie_detect_handler, (void *)chip); - - if (mix->hp_detect_irq >= 0) { - snd_pmac_add_automute(chip); - - chip->detect_headphone = toonie_detect_headphone; - chip->update_automute = toonie_update_automute; - toonie_update_automute(chip, 0); - - if (request_irq(mix->hp_detect_irq, toonie_hp_intr, 0, - "Sound Headphone Detection", chip) < 0) - mix->hp_detect_irq = -1; - } - - return 0; -} - 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) |
