162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Support for Digigram Lola PCI-e boards
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/vmalloc.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <sound/core.h>
1362306a36Sopenharmony_ci#include <sound/control.h>
1462306a36Sopenharmony_ci#include <sound/pcm.h>
1562306a36Sopenharmony_ci#include <sound/tlv.h>
1662306a36Sopenharmony_ci#include "lola.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int lola_init_pin(struct lola *chip, struct lola_pin *pin,
1962306a36Sopenharmony_ci			 int dir, int nid)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	unsigned int val;
2262306a36Sopenharmony_ci	int err;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	pin->nid = nid;
2562306a36Sopenharmony_ci	err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
2662306a36Sopenharmony_ci	if (err < 0) {
2762306a36Sopenharmony_ci		dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
2862306a36Sopenharmony_ci		return err;
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci	val &= 0x00f00fff; /* test TYPE and bits 0..11 */
3162306a36Sopenharmony_ci	if (val == 0x00400200)    /* Type = 4, Digital = 1 */
3262306a36Sopenharmony_ci		pin->is_analog = false;
3362306a36Sopenharmony_ci	else if (val == 0x0040000a && dir == CAPT) /* Dig=0, InAmp/ovrd */
3462306a36Sopenharmony_ci		pin->is_analog = true;
3562306a36Sopenharmony_ci	else if (val == 0x0040000c && dir == PLAY) /* Dig=0, OutAmp/ovrd */
3662306a36Sopenharmony_ci		pin->is_analog = true;
3762306a36Sopenharmony_ci	else {
3862306a36Sopenharmony_ci		dev_err(chip->card->dev, "Invalid wcaps 0x%x for 0x%x\n", val, nid);
3962306a36Sopenharmony_ci		return -EINVAL;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* analog parameters only following, so continue in case of Digital pin
4362306a36Sopenharmony_ci	 */
4462306a36Sopenharmony_ci	if (!pin->is_analog)
4562306a36Sopenharmony_ci		return 0;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (dir == PLAY)
4862306a36Sopenharmony_ci		err = lola_read_param(chip, nid, LOLA_PAR_AMP_OUT_CAP, &val);
4962306a36Sopenharmony_ci	else
5062306a36Sopenharmony_ci		err = lola_read_param(chip, nid, LOLA_PAR_AMP_IN_CAP, &val);
5162306a36Sopenharmony_ci	if (err < 0) {
5262306a36Sopenharmony_ci		dev_err(chip->card->dev, "Can't read AMP-caps for 0x%x\n", nid);
5362306a36Sopenharmony_ci		return err;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	pin->amp_mute = LOLA_AMP_MUTE_CAPABLE(val);
5762306a36Sopenharmony_ci	pin->amp_step_size = LOLA_AMP_STEP_SIZE(val);
5862306a36Sopenharmony_ci	pin->amp_num_steps = LOLA_AMP_NUM_STEPS(val);
5962306a36Sopenharmony_ci	if (pin->amp_num_steps) {
6062306a36Sopenharmony_ci		/* zero as mute state */
6162306a36Sopenharmony_ci		pin->amp_num_steps++;
6262306a36Sopenharmony_ci		pin->amp_step_size++;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci	pin->amp_offset = LOLA_AMP_OFFSET(val);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	err = lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val,
6762306a36Sopenharmony_ci			      NULL);
6862306a36Sopenharmony_ci	if (err < 0) {
6962306a36Sopenharmony_ci		dev_err(chip->card->dev, "Can't get MAX_LEVEL 0x%x\n", nid);
7062306a36Sopenharmony_ci		return err;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci	pin->max_level = val & 0x3ff;   /* 10 bits */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	pin->config_default_reg = 0;
7562306a36Sopenharmony_ci	pin->fixed_gain_list_len = 0;
7662306a36Sopenharmony_ci	pin->cur_gain_step = 0;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciint lola_init_pins(struct lola *chip, int dir, int *nidp)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int i, err, nid;
8462306a36Sopenharmony_ci	nid = *nidp;
8562306a36Sopenharmony_ci	for (i = 0; i < chip->pin[dir].num_pins; i++, nid++) {
8662306a36Sopenharmony_ci		err = lola_init_pin(chip, &chip->pin[dir].pins[i], dir, nid);
8762306a36Sopenharmony_ci		if (err < 0)
8862306a36Sopenharmony_ci			return err;
8962306a36Sopenharmony_ci		if (chip->pin[dir].pins[i].is_analog)
9062306a36Sopenharmony_ci			chip->pin[dir].num_analog_pins++;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	*nidp = nid;
9362306a36Sopenharmony_ci	return 0;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_civoid lola_free_mixer(struct lola *chip)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	vfree(chip->mixer.array_saved);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ciint lola_init_mixer_widget(struct lola *chip, int nid)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	unsigned int val;
10462306a36Sopenharmony_ci	int err;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
10762306a36Sopenharmony_ci	if (err < 0) {
10862306a36Sopenharmony_ci		dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
10962306a36Sopenharmony_ci		return err;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if ((val & 0xfff00000) != 0x02f00000) { /* test SubType and Type */
11362306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "No valid mixer widget\n");
11462306a36Sopenharmony_ci		return 0;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	chip->mixer.nid = nid;
11862306a36Sopenharmony_ci	chip->mixer.caps = val;
11962306a36Sopenharmony_ci	chip->mixer.array = (struct lola_mixer_array __iomem *)
12062306a36Sopenharmony_ci		(chip->bar[BAR1].remap_addr + LOLA_BAR1_SOURCE_GAIN_ENABLE);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* reserve memory to copy mixer data for sleep mode transitions */
12362306a36Sopenharmony_ci	chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array));
12462306a36Sopenharmony_ci	if (!chip->mixer.array_saved)
12562306a36Sopenharmony_ci		return -ENOMEM;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* mixer matrix sources are physical input data and play streams */
12862306a36Sopenharmony_ci	chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams;
12962306a36Sopenharmony_ci	chip->mixer.src_phys_ins = chip->pin[CAPT].num_pins;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* mixer matrix destinations are record streams and physical output */
13262306a36Sopenharmony_ci	chip->mixer.dest_stream_ins = chip->pcm[CAPT].num_streams;
13362306a36Sopenharmony_ci	chip->mixer.dest_phys_outs = chip->pin[PLAY].num_pins;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* mixer matrix may have unused areas between PhysIn and
13662306a36Sopenharmony_ci	 * Play or Record and PhysOut zones
13762306a36Sopenharmony_ci	 */
13862306a36Sopenharmony_ci	chip->mixer.src_stream_out_ofs = chip->mixer.src_phys_ins +
13962306a36Sopenharmony_ci		LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(val);
14062306a36Sopenharmony_ci	chip->mixer.dest_phys_out_ofs = chip->mixer.dest_stream_ins +
14162306a36Sopenharmony_ci		LOLA_MIXER_DEST_REC_OUTPUT_SEPARATION(val);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* example : MixerMatrix of LoLa881 (LoLa16161 uses unused zones)
14462306a36Sopenharmony_ci	 * +-+  0-------8------16-------8------16
14562306a36Sopenharmony_ci	 * | |  |       |       |       |       |
14662306a36Sopenharmony_ci	 * |s|  | INPUT |       | INPUT |       |
14762306a36Sopenharmony_ci	 * | |->|  ->   |unused |  ->   |unused |
14862306a36Sopenharmony_ci	 * |r|  |CAPTURE|       | OUTPUT|       |
14962306a36Sopenharmony_ci	 * | |  |  MIX  |       |  MIX  |       |
15062306a36Sopenharmony_ci	 * |c|  8--------------------------------
15162306a36Sopenharmony_ci	 * | |  |       |       |       |       |
15262306a36Sopenharmony_ci	 * | |  |       |       |       |       |
15362306a36Sopenharmony_ci	 * |g|  |unused |unused |unused |unused |
15462306a36Sopenharmony_ci	 * | |  |       |       |       |       |
15562306a36Sopenharmony_ci	 * |a|  |       |       |       |       |
15662306a36Sopenharmony_ci	 * | |  16-------------------------------
15762306a36Sopenharmony_ci	 * |i|  |       |       |       |       |
15862306a36Sopenharmony_ci	 * | |  | PLAYBK|       | PLAYBK|       |
15962306a36Sopenharmony_ci	 * |n|->|  ->   |unused |  ->   |unused |
16062306a36Sopenharmony_ci	 * | |  |CAPTURE|       | OUTPUT|       |
16162306a36Sopenharmony_ci	 * | |  |  MIX  |       |  MIX  |       |
16262306a36Sopenharmony_ci	 * |a|  8--------------------------------
16362306a36Sopenharmony_ci	 * |r|  |       |       |       |       |
16462306a36Sopenharmony_ci	 * |r|  |       |       |       |       |
16562306a36Sopenharmony_ci	 * |a|  |unused |unused |unused |unused |
16662306a36Sopenharmony_ci	 * |y|  |       |       |       |       |
16762306a36Sopenharmony_ci	 * | |  |       |       |       |       |
16862306a36Sopenharmony_ci	 * +++  16--|---------------|------------
16962306a36Sopenharmony_ci	 *      +---V---------------V-----------+
17062306a36Sopenharmony_ci	 *      |  dest_mix_gain_enable array   |
17162306a36Sopenharmony_ci	 *      +-------------------------------+
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	/* example : MixerMatrix of LoLa280
17462306a36Sopenharmony_ci	 * +-+  0-------8-2
17562306a36Sopenharmony_ci	 * | |  |       | |
17662306a36Sopenharmony_ci	 * |s|  | INPUT | |     INPUT
17762306a36Sopenharmony_ci	 * |r|->|  ->   | |      ->
17862306a36Sopenharmony_ci	 * |c|  |CAPTURE| | <-  OUTPUT
17962306a36Sopenharmony_ci	 * | |  |  MIX  | |      MIX
18062306a36Sopenharmony_ci	 * |g|  8----------
18162306a36Sopenharmony_ci	 * |a|  |       | |
18262306a36Sopenharmony_ci	 * |i|  | PLAYBK| |     PLAYBACK
18362306a36Sopenharmony_ci	 * |n|->|  ->   | |      ->
18462306a36Sopenharmony_ci	 * | |  |CAPTURE| | <-  OUTPUT
18562306a36Sopenharmony_ci	 * |a|  |  MIX  | |      MIX
18662306a36Sopenharmony_ci	 * |r|  8---|----|-
18762306a36Sopenharmony_ci	 * |r|  +---V----V-------------------+
18862306a36Sopenharmony_ci	 * |a|  | dest_mix_gain_enable array |
18962306a36Sopenharmony_ci	 * |y|  +----------------------------+
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	if (chip->mixer.src_stream_out_ofs > MAX_AUDIO_INOUT_COUNT ||
19262306a36Sopenharmony_ci	    chip->mixer.dest_phys_out_ofs > MAX_STREAM_IN_COUNT) {
19362306a36Sopenharmony_ci		dev_err(chip->card->dev, "Invalid mixer widget size\n");
19462306a36Sopenharmony_ci		return -EINVAL;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	chip->mixer.src_mask = ((1U << chip->mixer.src_phys_ins) - 1) |
19862306a36Sopenharmony_ci		(((1U << chip->mixer.src_stream_outs) - 1)
19962306a36Sopenharmony_ci		 << chip->mixer.src_stream_out_ofs);
20062306a36Sopenharmony_ci	chip->mixer.dest_mask = ((1U << chip->mixer.dest_stream_ins) - 1) |
20162306a36Sopenharmony_ci		(((1U << chip->mixer.dest_phys_outs) - 1)
20262306a36Sopenharmony_ci		 << chip->mixer.dest_phys_out_ofs);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "Mixer src_mask=%x, dest_mask=%x\n",
20562306a36Sopenharmony_ci		    chip->mixer.src_mask, chip->mixer.dest_mask);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return 0;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int lola_mixer_set_src_gain(struct lola *chip, unsigned int id,
21162306a36Sopenharmony_ci				   unsigned short gain, bool on)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	unsigned int oldval, val;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (!(chip->mixer.src_mask & (1 << id)))
21662306a36Sopenharmony_ci		return -EINVAL;
21762306a36Sopenharmony_ci	oldval = val = readl(&chip->mixer.array->src_gain_enable);
21862306a36Sopenharmony_ci	if (on)
21962306a36Sopenharmony_ci		val |= (1 << id);
22062306a36Sopenharmony_ci	else
22162306a36Sopenharmony_ci		val &= ~(1 << id);
22262306a36Sopenharmony_ci	/* test if values unchanged */
22362306a36Sopenharmony_ci	if ((val == oldval) &&
22462306a36Sopenharmony_ci	    (gain == readw(&chip->mixer.array->src_gain[id])))
22562306a36Sopenharmony_ci		return 0;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	dev_dbg(chip->card->dev,
22862306a36Sopenharmony_ci		"lola_mixer_set_src_gain (id=%d, gain=%d) enable=%x\n",
22962306a36Sopenharmony_ci			id, gain, val);
23062306a36Sopenharmony_ci	writew(gain, &chip->mixer.array->src_gain[id]);
23162306a36Sopenharmony_ci	writel(val, &chip->mixer.array->src_gain_enable);
23262306a36Sopenharmony_ci	lola_codec_flush(chip);
23362306a36Sopenharmony_ci	/* inform micro-controller about the new source gain */
23462306a36Sopenharmony_ci	return lola_codec_write(chip, chip->mixer.nid,
23562306a36Sopenharmony_ci				LOLA_VERB_SET_SOURCE_GAIN, id, 0);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci#if 0 /* not used */
23962306a36Sopenharmony_cistatic int lola_mixer_set_src_gains(struct lola *chip, unsigned int mask,
24062306a36Sopenharmony_ci				    unsigned short *gains)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	int i;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if ((chip->mixer.src_mask & mask) != mask)
24562306a36Sopenharmony_ci		return -EINVAL;
24662306a36Sopenharmony_ci	for (i = 0; i < LOLA_MIXER_DIM; i++) {
24762306a36Sopenharmony_ci		if (mask & (1 << i)) {
24862306a36Sopenharmony_ci			writew(*gains, &chip->mixer.array->src_gain[i]);
24962306a36Sopenharmony_ci			gains++;
25062306a36Sopenharmony_ci		}
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci	writel(mask, &chip->mixer.array->src_gain_enable);
25362306a36Sopenharmony_ci	lola_codec_flush(chip);
25462306a36Sopenharmony_ci	if (chip->mixer.caps & LOLA_PEAK_METER_CAN_AGC_MASK) {
25562306a36Sopenharmony_ci		/* update for all srcs at once */
25662306a36Sopenharmony_ci		return lola_codec_write(chip, chip->mixer.nid,
25762306a36Sopenharmony_ci					LOLA_VERB_SET_SOURCE_GAIN, 0x80, 0);
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci	/* update manually */
26062306a36Sopenharmony_ci	for (i = 0; i < LOLA_MIXER_DIM; i++) {
26162306a36Sopenharmony_ci		if (mask & (1 << i)) {
26262306a36Sopenharmony_ci			lola_codec_write(chip, chip->mixer.nid,
26362306a36Sopenharmony_ci					 LOLA_VERB_SET_SOURCE_GAIN, i, 0);
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	return 0;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci#endif /* not used */
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int lola_mixer_set_mapping_gain(struct lola *chip,
27162306a36Sopenharmony_ci				       unsigned int src, unsigned int dest,
27262306a36Sopenharmony_ci				       unsigned short gain, bool on)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	unsigned int val;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (!(chip->mixer.src_mask & (1 << src)) ||
27762306a36Sopenharmony_ci	    !(chip->mixer.dest_mask & (1 << dest)))
27862306a36Sopenharmony_ci		return -EINVAL;
27962306a36Sopenharmony_ci	if (on)
28062306a36Sopenharmony_ci		writew(gain, &chip->mixer.array->dest_mix_gain[dest][src]);
28162306a36Sopenharmony_ci	val = readl(&chip->mixer.array->dest_mix_gain_enable[dest]);
28262306a36Sopenharmony_ci	if (on)
28362306a36Sopenharmony_ci		val |= (1 << src);
28462306a36Sopenharmony_ci	else
28562306a36Sopenharmony_ci		val &= ~(1 << src);
28662306a36Sopenharmony_ci	writel(val, &chip->mixer.array->dest_mix_gain_enable[dest]);
28762306a36Sopenharmony_ci	lola_codec_flush(chip);
28862306a36Sopenharmony_ci	return lola_codec_write(chip, chip->mixer.nid, LOLA_VERB_SET_MIX_GAIN,
28962306a36Sopenharmony_ci				src, dest);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci#if 0 /* not used */
29362306a36Sopenharmony_cistatic int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id,
29462306a36Sopenharmony_ci				     unsigned int mask, unsigned short *gains)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	int i;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (!(chip->mixer.dest_mask & (1 << id)) ||
29962306a36Sopenharmony_ci	    (chip->mixer.src_mask & mask) != mask)
30062306a36Sopenharmony_ci		return -EINVAL;
30162306a36Sopenharmony_ci	for (i = 0; i < LOLA_MIXER_DIM; i++) {
30262306a36Sopenharmony_ci		if (mask & (1 << i)) {
30362306a36Sopenharmony_ci			writew(*gains, &chip->mixer.array->dest_mix_gain[id][i]);
30462306a36Sopenharmony_ci			gains++;
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci	writel(mask, &chip->mixer.array->dest_mix_gain_enable[id]);
30862306a36Sopenharmony_ci	lola_codec_flush(chip);
30962306a36Sopenharmony_ci	/* update for all dests at once */
31062306a36Sopenharmony_ci	return lola_codec_write(chip, chip->mixer.nid,
31162306a36Sopenharmony_ci				LOLA_VERB_SET_DESTINATION_GAIN, id, 0);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci#endif /* not used */
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/*
31662306a36Sopenharmony_ci */
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int set_analog_volume(struct lola *chip, int dir,
31962306a36Sopenharmony_ci			     unsigned int idx, unsigned int val,
32062306a36Sopenharmony_ci			     bool external_call);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ciint lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct lola_pin *pin;
32562306a36Sopenharmony_ci	int idx, max_idx;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	pin = chip->pin[dir].pins;
32862306a36Sopenharmony_ci	max_idx = chip->pin[dir].num_pins;
32962306a36Sopenharmony_ci	for (idx = 0; idx < max_idx; idx++) {
33062306a36Sopenharmony_ci		if (pin[idx].is_analog) {
33162306a36Sopenharmony_ci			unsigned int val = mute ? 0 : pin[idx].cur_gain_step;
33262306a36Sopenharmony_ci			/* set volume and do not save the value */
33362306a36Sopenharmony_ci			set_analog_volume(chip, dir, idx, val, false);
33462306a36Sopenharmony_ci		}
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci	return lola_codec_flush(chip);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_civoid lola_save_mixer(struct lola *chip)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	/* mute analog output */
34262306a36Sopenharmony_ci	if (chip->mixer.array_saved) {
34362306a36Sopenharmony_ci		/* store contents of mixer array */
34462306a36Sopenharmony_ci		memcpy_fromio(chip->mixer.array_saved, chip->mixer.array,
34562306a36Sopenharmony_ci			      sizeof(*chip->mixer.array));
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci	lola_setup_all_analog_gains(chip, PLAY, true); /* output mute */
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_civoid lola_restore_mixer(struct lola *chip)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	int i;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/*lola_reset_setups(chip);*/
35562306a36Sopenharmony_ci	if (chip->mixer.array_saved) {
35662306a36Sopenharmony_ci		/* restore contents of mixer array */
35762306a36Sopenharmony_ci		memcpy_toio(chip->mixer.array, chip->mixer.array_saved,
35862306a36Sopenharmony_ci			    sizeof(*chip->mixer.array));
35962306a36Sopenharmony_ci		/* inform micro-controller about all restored values
36062306a36Sopenharmony_ci		 * and ignore return values
36162306a36Sopenharmony_ci		 */
36262306a36Sopenharmony_ci		for (i = 0; i < chip->mixer.src_phys_ins; i++)
36362306a36Sopenharmony_ci			lola_codec_write(chip, chip->mixer.nid,
36462306a36Sopenharmony_ci					 LOLA_VERB_SET_SOURCE_GAIN,
36562306a36Sopenharmony_ci					 i, 0);
36662306a36Sopenharmony_ci		for (i = 0; i < chip->mixer.src_stream_outs; i++)
36762306a36Sopenharmony_ci			lola_codec_write(chip, chip->mixer.nid,
36862306a36Sopenharmony_ci					 LOLA_VERB_SET_SOURCE_GAIN,
36962306a36Sopenharmony_ci					 chip->mixer.src_stream_out_ofs + i, 0);
37062306a36Sopenharmony_ci		for (i = 0; i < chip->mixer.dest_stream_ins; i++)
37162306a36Sopenharmony_ci			lola_codec_write(chip, chip->mixer.nid,
37262306a36Sopenharmony_ci					 LOLA_VERB_SET_DESTINATION_GAIN,
37362306a36Sopenharmony_ci					 i, 0);
37462306a36Sopenharmony_ci		for (i = 0; i < chip->mixer.dest_phys_outs; i++)
37562306a36Sopenharmony_ci			lola_codec_write(chip, chip->mixer.nid,
37662306a36Sopenharmony_ci					 LOLA_VERB_SET_DESTINATION_GAIN,
37762306a36Sopenharmony_ci					 chip->mixer.dest_phys_out_ofs + i, 0);
37862306a36Sopenharmony_ci		lola_codec_flush(chip);
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/*
38362306a36Sopenharmony_ci */
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int set_analog_volume(struct lola *chip, int dir,
38662306a36Sopenharmony_ci			     unsigned int idx, unsigned int val,
38762306a36Sopenharmony_ci			     bool external_call)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	struct lola_pin *pin;
39062306a36Sopenharmony_ci	int err;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (idx >= chip->pin[dir].num_pins)
39362306a36Sopenharmony_ci		return -EINVAL;
39462306a36Sopenharmony_ci	pin = &chip->pin[dir].pins[idx];
39562306a36Sopenharmony_ci	if (!pin->is_analog || pin->amp_num_steps <= val)
39662306a36Sopenharmony_ci		return -EINVAL;
39762306a36Sopenharmony_ci	if (external_call && pin->cur_gain_step == val)
39862306a36Sopenharmony_ci		return 0;
39962306a36Sopenharmony_ci	if (external_call)
40062306a36Sopenharmony_ci		lola_codec_flush(chip);
40162306a36Sopenharmony_ci	dev_dbg(chip->card->dev,
40262306a36Sopenharmony_ci		"set_analog_volume (dir=%d idx=%d, volume=%d)\n",
40362306a36Sopenharmony_ci			dir, idx, val);
40462306a36Sopenharmony_ci	err = lola_codec_write(chip, pin->nid,
40562306a36Sopenharmony_ci			       LOLA_VERB_SET_AMP_GAIN_MUTE, val, 0);
40662306a36Sopenharmony_ci	if (err < 0)
40762306a36Sopenharmony_ci		return err;
40862306a36Sopenharmony_ci	if (external_call)
40962306a36Sopenharmony_ci		pin->cur_gain_step = val;
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ciint lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	int ret = 0;
41662306a36Sopenharmony_ci	int success = 0;
41762306a36Sopenharmony_ci	int n, err;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* SRC can be activated and the dwInputSRCMask is valid? */
42062306a36Sopenharmony_ci	if ((chip->input_src_caps_mask & src_mask) != src_mask)
42162306a36Sopenharmony_ci		return -EINVAL;
42262306a36Sopenharmony_ci	/* handle all even Inputs - SRC is a stereo setting !!! */
42362306a36Sopenharmony_ci	for (n = 0; n < chip->pin[CAPT].num_pins; n += 2) {
42462306a36Sopenharmony_ci		unsigned int mask = 3U << n; /* handle the stereo case */
42562306a36Sopenharmony_ci		unsigned int new_src, src_state;
42662306a36Sopenharmony_ci		if (!(chip->input_src_caps_mask & mask))
42762306a36Sopenharmony_ci			continue;
42862306a36Sopenharmony_ci		/* if one IO needs SRC, both stereo IO will get SRC */
42962306a36Sopenharmony_ci		new_src = (src_mask & mask) != 0;
43062306a36Sopenharmony_ci		if (update) {
43162306a36Sopenharmony_ci			src_state = (chip->input_src_mask & mask) != 0;
43262306a36Sopenharmony_ci			if (src_state == new_src)
43362306a36Sopenharmony_ci				continue;   /* nothing to change for this IO */
43462306a36Sopenharmony_ci		}
43562306a36Sopenharmony_ci		err = lola_codec_write(chip, chip->pcm[CAPT].streams[n].nid,
43662306a36Sopenharmony_ci				       LOLA_VERB_SET_SRC, new_src, 0);
43762306a36Sopenharmony_ci		if (!err)
43862306a36Sopenharmony_ci			success++;
43962306a36Sopenharmony_ci		else
44062306a36Sopenharmony_ci			ret = err;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci	if (success)
44362306a36Sopenharmony_ci		ret = lola_codec_flush(chip);
44462306a36Sopenharmony_ci	if (!ret)
44562306a36Sopenharmony_ci		chip->input_src_mask = src_mask;
44662306a36Sopenharmony_ci	return ret;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/*
45062306a36Sopenharmony_ci */
45162306a36Sopenharmony_cistatic int init_mixer_values(struct lola *chip)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	int i;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* all sample rate converters on */
45662306a36Sopenharmony_ci	lola_set_src_config(chip, (1 << chip->pin[CAPT].num_pins) - 1, false);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/* clear all mixer matrix settings */
45962306a36Sopenharmony_ci	memset_io(chip->mixer.array, 0, sizeof(*chip->mixer.array));
46062306a36Sopenharmony_ci	/* inform firmware about all updated matrix columns - capture part */
46162306a36Sopenharmony_ci	for (i = 0; i < chip->mixer.dest_stream_ins; i++)
46262306a36Sopenharmony_ci		lola_codec_write(chip, chip->mixer.nid,
46362306a36Sopenharmony_ci				 LOLA_VERB_SET_DESTINATION_GAIN,
46462306a36Sopenharmony_ci				 i, 0);
46562306a36Sopenharmony_ci	/* inform firmware about all updated matrix columns - output part */
46662306a36Sopenharmony_ci	for (i = 0; i < chip->mixer.dest_phys_outs; i++)
46762306a36Sopenharmony_ci		lola_codec_write(chip, chip->mixer.nid,
46862306a36Sopenharmony_ci				 LOLA_VERB_SET_DESTINATION_GAIN,
46962306a36Sopenharmony_ci				 chip->mixer.dest_phys_out_ofs + i, 0);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* set all digital input source (master) gains to 0dB */
47262306a36Sopenharmony_ci	for (i = 0; i < chip->mixer.src_phys_ins; i++)
47362306a36Sopenharmony_ci		lola_mixer_set_src_gain(chip, i, 336, true); /* 0dB */
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* set all digital playback source (master) gains to 0dB */
47662306a36Sopenharmony_ci	for (i = 0; i < chip->mixer.src_stream_outs; i++)
47762306a36Sopenharmony_ci		lola_mixer_set_src_gain(chip,
47862306a36Sopenharmony_ci					i + chip->mixer.src_stream_out_ofs,
47962306a36Sopenharmony_ci					336, true); /* 0dB */
48062306a36Sopenharmony_ci	/* set gain value 0dB diagonally in matrix - part INPUT -> CAPTURE */
48162306a36Sopenharmony_ci	for (i = 0; i < chip->mixer.dest_stream_ins; i++) {
48262306a36Sopenharmony_ci		int src = i % chip->mixer.src_phys_ins;
48362306a36Sopenharmony_ci		lola_mixer_set_mapping_gain(chip, src, i, 336, true);
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	/* set gain value 0dB diagonally in matrix , part PLAYBACK -> OUTPUT
48662306a36Sopenharmony_ci	 * (LoLa280 : playback channel 0,2,4,6 linked to output channel 0)
48762306a36Sopenharmony_ci	 * (LoLa280 : playback channel 1,3,5,7 linked to output channel 1)
48862306a36Sopenharmony_ci	 */
48962306a36Sopenharmony_ci	for (i = 0; i < chip->mixer.src_stream_outs; i++) {
49062306a36Sopenharmony_ci		int src = chip->mixer.src_stream_out_ofs + i;
49162306a36Sopenharmony_ci		int dst = chip->mixer.dest_phys_out_ofs +
49262306a36Sopenharmony_ci			i % chip->mixer.dest_phys_outs;
49362306a36Sopenharmony_ci		lola_mixer_set_mapping_gain(chip, src, dst, 336, true);
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci	return 0;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/*
49962306a36Sopenharmony_ci * analog mixer control element
50062306a36Sopenharmony_ci */
50162306a36Sopenharmony_cistatic int lola_analog_vol_info(struct snd_kcontrol *kcontrol,
50262306a36Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
50562306a36Sopenharmony_ci	int dir = kcontrol->private_value;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
50862306a36Sopenharmony_ci	uinfo->count = chip->pin[dir].num_pins;
50962306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
51062306a36Sopenharmony_ci	uinfo->value.integer.max = chip->pin[dir].pins[0].amp_num_steps;
51162306a36Sopenharmony_ci	return 0;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int lola_analog_vol_get(struct snd_kcontrol *kcontrol,
51562306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
51862306a36Sopenharmony_ci	int dir = kcontrol->private_value;
51962306a36Sopenharmony_ci	int i;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	for (i = 0; i < chip->pin[dir].num_pins; i++)
52262306a36Sopenharmony_ci		ucontrol->value.integer.value[i] =
52362306a36Sopenharmony_ci			chip->pin[dir].pins[i].cur_gain_step;
52462306a36Sopenharmony_ci	return 0;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic int lola_analog_vol_put(struct snd_kcontrol *kcontrol,
52862306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
53162306a36Sopenharmony_ci	int dir = kcontrol->private_value;
53262306a36Sopenharmony_ci	int i, err;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	for (i = 0; i < chip->pin[dir].num_pins; i++) {
53562306a36Sopenharmony_ci		err = set_analog_volume(chip, dir, i,
53662306a36Sopenharmony_ci					ucontrol->value.integer.value[i],
53762306a36Sopenharmony_ci					true);
53862306a36Sopenharmony_ci		if (err < 0)
53962306a36Sopenharmony_ci			return err;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci	return 0;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic int lola_analog_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
54562306a36Sopenharmony_ci			       unsigned int size, unsigned int __user *tlv)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
54862306a36Sopenharmony_ci	int dir = kcontrol->private_value;
54962306a36Sopenharmony_ci	unsigned int val1, val2;
55062306a36Sopenharmony_ci	struct lola_pin *pin;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (size < 4 * sizeof(unsigned int))
55362306a36Sopenharmony_ci		return -ENOMEM;
55462306a36Sopenharmony_ci	pin = &chip->pin[dir].pins[0];
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	val2 = pin->amp_step_size * 25;
55762306a36Sopenharmony_ci	val1 = -1 * (int)pin->amp_offset * (int)val2;
55862306a36Sopenharmony_ci#ifdef TLV_DB_SCALE_MUTE
55962306a36Sopenharmony_ci	val2 |= TLV_DB_SCALE_MUTE;
56062306a36Sopenharmony_ci#endif
56162306a36Sopenharmony_ci	if (put_user(SNDRV_CTL_TLVT_DB_SCALE, tlv))
56262306a36Sopenharmony_ci		return -EFAULT;
56362306a36Sopenharmony_ci	if (put_user(2 * sizeof(unsigned int), tlv + 1))
56462306a36Sopenharmony_ci		return -EFAULT;
56562306a36Sopenharmony_ci	if (put_user(val1, tlv + 2))
56662306a36Sopenharmony_ci		return -EFAULT;
56762306a36Sopenharmony_ci	if (put_user(val2, tlv + 3))
56862306a36Sopenharmony_ci		return -EFAULT;
56962306a36Sopenharmony_ci	return 0;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic struct snd_kcontrol_new lola_analog_mixer = {
57362306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
57462306a36Sopenharmony_ci	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
57562306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_READ |
57662306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
57762306a36Sopenharmony_ci	.info = lola_analog_vol_info,
57862306a36Sopenharmony_ci	.get = lola_analog_vol_get,
57962306a36Sopenharmony_ci	.put = lola_analog_vol_put,
58062306a36Sopenharmony_ci	.tlv.c = lola_analog_vol_tlv,
58162306a36Sopenharmony_ci};
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic int create_analog_mixer(struct lola *chip, int dir, char *name)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	if (!chip->pin[dir].num_pins)
58662306a36Sopenharmony_ci		return 0;
58762306a36Sopenharmony_ci	/* no analog volumes on digital only adapters */
58862306a36Sopenharmony_ci	if (chip->pin[dir].num_pins != chip->pin[dir].num_analog_pins)
58962306a36Sopenharmony_ci		return 0;
59062306a36Sopenharmony_ci	lola_analog_mixer.name = name;
59162306a36Sopenharmony_ci	lola_analog_mixer.private_value = dir;
59262306a36Sopenharmony_ci	return snd_ctl_add(chip->card,
59362306a36Sopenharmony_ci			   snd_ctl_new1(&lola_analog_mixer, chip));
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci/*
59762306a36Sopenharmony_ci * Hardware sample rate converter on digital input
59862306a36Sopenharmony_ci */
59962306a36Sopenharmony_cistatic int lola_input_src_info(struct snd_kcontrol *kcontrol,
60062306a36Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
60562306a36Sopenharmony_ci	uinfo->count = chip->pin[CAPT].num_pins;
60662306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
60762306a36Sopenharmony_ci	uinfo->value.integer.max = 1;
60862306a36Sopenharmony_ci	return 0;
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic int lola_input_src_get(struct snd_kcontrol *kcontrol,
61262306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
61562306a36Sopenharmony_ci	int i;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	for (i = 0; i < chip->pin[CAPT].num_pins; i++)
61862306a36Sopenharmony_ci		ucontrol->value.integer.value[i] =
61962306a36Sopenharmony_ci			!!(chip->input_src_mask & (1 << i));
62062306a36Sopenharmony_ci	return 0;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic int lola_input_src_put(struct snd_kcontrol *kcontrol,
62462306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
62762306a36Sopenharmony_ci	int i;
62862306a36Sopenharmony_ci	unsigned int mask;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	mask = 0;
63162306a36Sopenharmony_ci	for (i = 0; i < chip->pin[CAPT].num_pins; i++)
63262306a36Sopenharmony_ci		if (ucontrol->value.integer.value[i])
63362306a36Sopenharmony_ci			mask |= 1 << i;
63462306a36Sopenharmony_ci	return lola_set_src_config(chip, mask, true);
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic const struct snd_kcontrol_new lola_input_src_mixer = {
63862306a36Sopenharmony_ci	.name = "Digital SRC Capture Switch",
63962306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
64062306a36Sopenharmony_ci	.info = lola_input_src_info,
64162306a36Sopenharmony_ci	.get = lola_input_src_get,
64262306a36Sopenharmony_ci	.put = lola_input_src_put,
64362306a36Sopenharmony_ci};
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci/*
64662306a36Sopenharmony_ci * Lola16161 or Lola881 can have Hardware sample rate converters
64762306a36Sopenharmony_ci * on its digital input pins
64862306a36Sopenharmony_ci */
64962306a36Sopenharmony_cistatic int create_input_src_mixer(struct lola *chip)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	if (!chip->input_src_caps_mask)
65262306a36Sopenharmony_ci		return 0;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	return snd_ctl_add(chip->card,
65562306a36Sopenharmony_ci			   snd_ctl_new1(&lola_input_src_mixer, chip));
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci/*
65962306a36Sopenharmony_ci * src gain mixer
66062306a36Sopenharmony_ci */
66162306a36Sopenharmony_cistatic int lola_src_gain_info(struct snd_kcontrol *kcontrol,
66262306a36Sopenharmony_ci			      struct snd_ctl_elem_info *uinfo)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	unsigned int count = (kcontrol->private_value >> 8) & 0xff;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
66762306a36Sopenharmony_ci	uinfo->count = count;
66862306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
66962306a36Sopenharmony_ci	uinfo->value.integer.max = 409;
67062306a36Sopenharmony_ci	return 0;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic int lola_src_gain_get(struct snd_kcontrol *kcontrol,
67462306a36Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
67762306a36Sopenharmony_ci	unsigned int ofs = kcontrol->private_value & 0xff;
67862306a36Sopenharmony_ci	unsigned int count = (kcontrol->private_value >> 8) & 0xff;
67962306a36Sopenharmony_ci	unsigned int mask, i;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	mask = readl(&chip->mixer.array->src_gain_enable);
68262306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
68362306a36Sopenharmony_ci		unsigned int idx = ofs + i;
68462306a36Sopenharmony_ci		unsigned short val;
68562306a36Sopenharmony_ci		if (!(chip->mixer.src_mask & (1 << idx)))
68662306a36Sopenharmony_ci			return -EINVAL;
68762306a36Sopenharmony_ci		if (mask & (1 << idx))
68862306a36Sopenharmony_ci			val = readw(&chip->mixer.array->src_gain[idx]) + 1;
68962306a36Sopenharmony_ci		else
69062306a36Sopenharmony_ci			val = 0;
69162306a36Sopenharmony_ci		ucontrol->value.integer.value[i] = val;
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci	return 0;
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic int lola_src_gain_put(struct snd_kcontrol *kcontrol,
69762306a36Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
70062306a36Sopenharmony_ci	unsigned int ofs = kcontrol->private_value & 0xff;
70162306a36Sopenharmony_ci	unsigned int count = (kcontrol->private_value >> 8) & 0xff;
70262306a36Sopenharmony_ci	int i, err;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
70562306a36Sopenharmony_ci		unsigned int idx = ofs + i;
70662306a36Sopenharmony_ci		unsigned short val = ucontrol->value.integer.value[i];
70762306a36Sopenharmony_ci		if (val)
70862306a36Sopenharmony_ci			val--;
70962306a36Sopenharmony_ci		err = lola_mixer_set_src_gain(chip, idx, val, !!val);
71062306a36Sopenharmony_ci		if (err < 0)
71162306a36Sopenharmony_ci			return err;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci	return 0;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci/* raw value: 0 = -84dB, 336 = 0dB, 408=18dB, incremented 1 for mute */
71762306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(lola_src_gain_tlv, -8425, 25, 1);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic struct snd_kcontrol_new lola_src_gain_mixer = {
72062306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
72162306a36Sopenharmony_ci	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
72262306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
72362306a36Sopenharmony_ci	.info = lola_src_gain_info,
72462306a36Sopenharmony_ci	.get = lola_src_gain_get,
72562306a36Sopenharmony_ci	.put = lola_src_gain_put,
72662306a36Sopenharmony_ci	.tlv.p = lola_src_gain_tlv,
72762306a36Sopenharmony_ci};
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cistatic int create_src_gain_mixer(struct lola *chip,
73062306a36Sopenharmony_ci				 int num, int ofs, char *name)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	lola_src_gain_mixer.name = name;
73362306a36Sopenharmony_ci	lola_src_gain_mixer.private_value = ofs + (num << 8);
73462306a36Sopenharmony_ci	return snd_ctl_add(chip->card,
73562306a36Sopenharmony_ci			   snd_ctl_new1(&lola_src_gain_mixer, chip));
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci#if 0 /* not used */
73962306a36Sopenharmony_ci/*
74062306a36Sopenharmony_ci * destination gain (matrix-like) mixer
74162306a36Sopenharmony_ci */
74262306a36Sopenharmony_cistatic int lola_dest_gain_info(struct snd_kcontrol *kcontrol,
74362306a36Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
74862306a36Sopenharmony_ci	uinfo->count = src_num;
74962306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
75062306a36Sopenharmony_ci	uinfo->value.integer.max = 433;
75162306a36Sopenharmony_ci	return 0;
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic int lola_dest_gain_get(struct snd_kcontrol *kcontrol,
75562306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
75862306a36Sopenharmony_ci	unsigned int src_ofs = kcontrol->private_value & 0xff;
75962306a36Sopenharmony_ci	unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
76062306a36Sopenharmony_ci	unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
76162306a36Sopenharmony_ci	unsigned int dst, mask, i;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
76462306a36Sopenharmony_ci	mask = readl(&chip->mixer.array->dest_mix_gain_enable[dst]);
76562306a36Sopenharmony_ci	for (i = 0; i < src_num; i++) {
76662306a36Sopenharmony_ci		unsigned int src = src_ofs + i;
76762306a36Sopenharmony_ci		unsigned short val;
76862306a36Sopenharmony_ci		if (!(chip->mixer.src_mask & (1 << src)))
76962306a36Sopenharmony_ci			return -EINVAL;
77062306a36Sopenharmony_ci		if (mask & (1 << dst))
77162306a36Sopenharmony_ci			val = readw(&chip->mixer.array->dest_mix_gain[dst][src]) + 1;
77262306a36Sopenharmony_ci		else
77362306a36Sopenharmony_ci			val = 0;
77462306a36Sopenharmony_ci		ucontrol->value.integer.value[i] = val;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci	return 0;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistatic int lola_dest_gain_put(struct snd_kcontrol *kcontrol,
78062306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct lola *chip = snd_kcontrol_chip(kcontrol);
78362306a36Sopenharmony_ci	unsigned int src_ofs = kcontrol->private_value & 0xff;
78462306a36Sopenharmony_ci	unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
78562306a36Sopenharmony_ci	unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
78662306a36Sopenharmony_ci	unsigned int dst, mask;
78762306a36Sopenharmony_ci	unsigned short gains[MAX_STREAM_COUNT];
78862306a36Sopenharmony_ci	int i, num;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	mask = 0;
79162306a36Sopenharmony_ci	num = 0;
79262306a36Sopenharmony_ci	for (i = 0; i < src_num; i++) {
79362306a36Sopenharmony_ci		unsigned short val = ucontrol->value.integer.value[i];
79462306a36Sopenharmony_ci		if (val) {
79562306a36Sopenharmony_ci			gains[num++] = val - 1;
79662306a36Sopenharmony_ci			mask |= 1 << i;
79762306a36Sopenharmony_ci		}
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci	mask <<= src_ofs;
80062306a36Sopenharmony_ci	dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
80162306a36Sopenharmony_ci	return lola_mixer_set_dest_gains(chip, dst, mask, gains);
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(lola_dest_gain_tlv, -8425, 25, 1);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic struct snd_kcontrol_new lola_dest_gain_mixer = {
80762306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
80862306a36Sopenharmony_ci	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
80962306a36Sopenharmony_ci		   SNDRV_CTL_ELEM_ACCESS_TLV_READ),
81062306a36Sopenharmony_ci	.info = lola_dest_gain_info,
81162306a36Sopenharmony_ci	.get = lola_dest_gain_get,
81262306a36Sopenharmony_ci	.put = lola_dest_gain_put,
81362306a36Sopenharmony_ci	.tlv.p = lola_dest_gain_tlv,
81462306a36Sopenharmony_ci};
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic int create_dest_gain_mixer(struct lola *chip,
81762306a36Sopenharmony_ci				  int src_num, int src_ofs,
81862306a36Sopenharmony_ci				  int num, int ofs, char *name)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	lola_dest_gain_mixer.count = num;
82162306a36Sopenharmony_ci	lola_dest_gain_mixer.name = name;
82262306a36Sopenharmony_ci	lola_dest_gain_mixer.private_value =
82362306a36Sopenharmony_ci		src_ofs + (src_num << 8) + (ofs << 16) + (num << 24);
82462306a36Sopenharmony_ci	return snd_ctl_add(chip->card,
82562306a36Sopenharmony_ci			  snd_ctl_new1(&lola_dest_gain_mixer, chip));
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci#endif /* not used */
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci/*
83062306a36Sopenharmony_ci */
83162306a36Sopenharmony_ciint lola_create_mixer(struct lola *chip)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	int err;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	err = create_analog_mixer(chip, PLAY, "Analog Playback Volume");
83662306a36Sopenharmony_ci	if (err < 0)
83762306a36Sopenharmony_ci		return err;
83862306a36Sopenharmony_ci	err = create_analog_mixer(chip, CAPT, "Analog Capture Volume");
83962306a36Sopenharmony_ci	if (err < 0)
84062306a36Sopenharmony_ci		return err;
84162306a36Sopenharmony_ci	err = create_input_src_mixer(chip);
84262306a36Sopenharmony_ci	if (err < 0)
84362306a36Sopenharmony_ci		return err;
84462306a36Sopenharmony_ci	err = create_src_gain_mixer(chip, chip->mixer.src_phys_ins, 0,
84562306a36Sopenharmony_ci				    "Digital Capture Volume");
84662306a36Sopenharmony_ci	if (err < 0)
84762306a36Sopenharmony_ci		return err;
84862306a36Sopenharmony_ci	err = create_src_gain_mixer(chip, chip->mixer.src_stream_outs,
84962306a36Sopenharmony_ci				    chip->mixer.src_stream_out_ofs,
85062306a36Sopenharmony_ci				    "Digital Playback Volume");
85162306a36Sopenharmony_ci	if (err < 0)
85262306a36Sopenharmony_ci		return err;
85362306a36Sopenharmony_ci#if 0
85462306a36Sopenharmony_ci/* FIXME: buggy mixer matrix handling */
85562306a36Sopenharmony_ci	err = create_dest_gain_mixer(chip,
85662306a36Sopenharmony_ci				     chip->mixer.src_phys_ins, 0,
85762306a36Sopenharmony_ci				     chip->mixer.dest_stream_ins, 0,
85862306a36Sopenharmony_ci				     "Line Capture Volume");
85962306a36Sopenharmony_ci	if (err < 0)
86062306a36Sopenharmony_ci		return err;
86162306a36Sopenharmony_ci	err = create_dest_gain_mixer(chip,
86262306a36Sopenharmony_ci				     chip->mixer.src_stream_outs,
86362306a36Sopenharmony_ci				     chip->mixer.src_stream_out_ofs,
86462306a36Sopenharmony_ci				     chip->mixer.dest_stream_ins, 0,
86562306a36Sopenharmony_ci				     "Stream-Loopback Capture Volume");
86662306a36Sopenharmony_ci	if (err < 0)
86762306a36Sopenharmony_ci		return err;
86862306a36Sopenharmony_ci	err = create_dest_gain_mixer(chip,
86962306a36Sopenharmony_ci				     chip->mixer.src_phys_ins, 0,
87062306a36Sopenharmony_ci				     chip->mixer.dest_phys_outs,
87162306a36Sopenharmony_ci				     chip->mixer.dest_phys_out_ofs,
87262306a36Sopenharmony_ci				     "Line-Loopback Playback Volume");
87362306a36Sopenharmony_ci	if (err < 0)
87462306a36Sopenharmony_ci		return err;
87562306a36Sopenharmony_ci	err = create_dest_gain_mixer(chip,
87662306a36Sopenharmony_ci				     chip->mixer.src_stream_outs,
87762306a36Sopenharmony_ci				     chip->mixer.src_stream_out_ofs,
87862306a36Sopenharmony_ci				     chip->mixer.dest_phys_outs,
87962306a36Sopenharmony_ci				     chip->mixer.dest_phys_out_ofs,
88062306a36Sopenharmony_ci				     "Stream Playback Volume");
88162306a36Sopenharmony_ci	if (err < 0)
88262306a36Sopenharmony_ci		return err;
88362306a36Sopenharmony_ci#endif /* FIXME */
88462306a36Sopenharmony_ci	return init_mixer_values(chip);
88562306a36Sopenharmony_ci}
886