/*
* PMac AWACS lowlevel functions
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
* code based on dmasound.c.
*
* 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 <asm/io.h>
#include <asm/nvram.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "pmac.h"
#ifdef CONFIG_ADB_CUDA
#define PMAC_AMP_AVAIL
#endif
#ifdef PMAC_AMP_AVAIL
struct awacs_amp {
unsigned char amp_master;
unsigned char amp_vol[2][2];
unsigned char amp_tone[2];
};
#define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA)
#endif /* PMAC_AMP_AVAIL */
static void snd_pmac_screamer_wait(struct snd_pmac *chip)
{
long timeout = 2000;
while (!(in_le32(&chip->awacs->codec_stat) & MASK_VALID)) {
mdelay(1);
if (! --timeout) {
snd_printd("snd_pmac_screamer_wait timeout\n");
break;
}
}
}
/*
* write AWACS register
*/
static void
snd_pmac_awacs_write(struct snd_pmac *chip, int val)
{
long timeout = 5000000;
if (chip->model == PMAC_SCREAMER)
snd_pmac_screamer_wait(chip);
out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22));
while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) {
if (! --timeout) {
snd_printd("snd_pmac_awacs_write timeout\n");
break;
}
}
}
static void
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(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(struct snd_pmac *chip)
{
if (chip->model != PMAC_SCREAMER)
return;
/* Sorry for the horrible delays... I hope to get that improved
* by making the whole PM process asynchronous in a future version
*/
snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
if (chip->manufacturer == 0x1)
/* delay for broken crystal part */
msleep(750);
snd_pmac_awacs_write_noreg(chip, 1,
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]);
}
#else
#define screamer_recalibrate(chip) /* NOP */
#endif
/*
* additional callback to set the pcm format
*/
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;
snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]);
}
/*
* AWACS volume callbacks
*/
/*
* volumes: 0-15 stereo
*/
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;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 15;
return 0;
}
static int snd_pmac_awacs_get_volume(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
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 >>