/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
*
* 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 <linux/kernel.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/tlv.h>
#include "lola.h"
static int lola_init_pin(struct lola *chip, struct lola_pin *pin,
int dir, int nid)
{
unsigned int val;
int err;
pin->nid = nid;
err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
if (err < 0) {
dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
return err;
}
val &= 0x00f00fff; /* test TYPE and bits 0..11 */
if (val == 0x00400200) /* Type = 4, Digital = 1 */
pin->is_analog = false;
else if (val == 0x0040000a && dir == CAPT) /* Dig=0, InAmp/ovrd */
pin->is_analog = true;
else if (val == 0x0040000c && dir == PLAY) /* Dig=0, OutAmp/ovrd */
pin->is_analog = true;
else {
dev_err(chip->card->dev, "Invalid wcaps 0x%x for 0x%x\n", val, nid);
return -EINVAL;
}
/* analog parameters only following, so continue in case of Digital pin
*/
if (!pin->is_analog)
return 0;
if (dir == PLAY)
err = lola_read_param(chip, nid, LOLA_PAR_AMP_OUT_CAP, &val);
else
err = lola_read_param(chip, nid, LOLA_PAR_AMP_IN_CAP, &val);
if (err < 0) {
dev_err(chip->card->dev, "Can't read AMP-caps for 0x%x\n", nid);
return err;
}
pin->amp_mute = LOLA_AMP_MUTE_CAPABLE(val);
pin->amp_step_size = LOLA_AMP_STEP_SIZE(val);
pin->amp_num_steps = LOLA_AMP_NUM_STEPS(val);
if (pin->amp_num_steps) {
/* zero as mute state */
pin->amp_num_steps++;
pin->amp_step_size++;
}
pin->amp_offset = LOLA_AMP_OFFSET(val);
err = lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val,
NULL);
if (err < 0) {
dev_err(chip->card->dev, "Can't get MAX_LEVEL 0x%x\n", nid);
return err;
}
pin->max_level = val & 0x3ff; /* 10 bits */
pin->config_default_reg = 0;
pin->fixed_gain_list_len = 0;
pin->cur_gain_step = 0;
return 0;
}
int lola_init_pins(struct lola *chip, int dir, int *nidp)
{
int i, err, nid;
nid = *nidp;
for (i = 0; i < chip->pin[dir].num_pins; i++, nid++) {
err = lola_init_pin(chip, &chip->pin[dir].pins[i], dir, nid);
if (err < 0)
return err;
if (chip->pin[dir].pins[i].is_analog)
chip->pin[dir].num_analog_pins++;
}
*nidp = nid;
return 0;
}
void lola_free_mixer(struct lola *chip)
{
vfree(chip->mixer.array_saved);
}
int lola_init_mixer_widget(struct lola *chip, int nid)
{
unsigned int val;
int err;
err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
if (err < 0) {
dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
return err;
}
if ((val & 0xfff00000) != 0x02f00000) { /* test SubType and Type */
dev_dbg(chip->card->dev, "No valid mixer widget\n");
return 0;
}
chip->mixer.nid = nid;
chip->mixer.caps = val;
chip->mixer.array = (struct lola_mixer_array __iomem *)
(chip->bar[BAR1].remap_addr + LOLA_BAR1_SOURCE_GAIN_ENABLE);
/* reserve memory to copy mixer data for sleep mode transitions */
chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array));
/* mixer matrix sources are physical input data and play streams */
chip->mixer.src_stream_outs = chip->pcm[PLAY].