[alsa-devel] [PATCH] ASoC: Add support for multi register mux

Songhee Baek sbaek at nvidia.com
Sat Mar 29 03:30:00 CET 2014


> -----Original Message-----
> From: Songhee Baek
> Sent: Friday, March 28, 2014 11:10 AM
> To: 'Lars-Peter Clausen'
> Cc: Arun Shamanna Lakshmi; 'lgirdwood at gmail.com'; 'broonie at kernel.org';
> 'swarren at wwwdotorg.org'; 'alsa-devel at alsa-project.org'; 'tiwai at suse.de';
> 'linux-kernel at vger.kernel.org'
> Subject: RE: [alsa-devel] [PATCH] ASoC: Add support for multi register mux
> 
> 
> > > On 03/26/2014 11:41 PM, Songhee Baek wrote:
> > > >> On 03/26/2014 01:02 AM, Arun Shamanna Lakshmi wrote:
> > > >>
> > > >> The way you describe this it seems to me that a value array for
> > > >> this kind of mux would look like.
> > > >>
> > > >> 0x00000000, 0x00000000, 0x00000001 0x00000000, 0x00000000,
> > > >> 0x00000002 0x00000000, 0x00000000, 0x00000003 0x00000000,
> > > >> 0x00000000, 0x00000004 0x00000000, 0x00000000, 0x00000008 ...
> > > >>
> > > >> That seems to be extremely tedious. If the MUX uses a one hot
> > > >> encoding how about storing the index of the bit in the values
> > > >> array and use (1 << value) when writing the value to the register?
> > > >
> > > > If we store the index of the bit, the value will be duplicated for
> > > > each
> > > registers inputs since register has 0 to 31bits to shift, then we
> > > need to decode the index to interpret value for which registers to
> > > set. If we need to interpret the decoded value of index, it is
> > > better to have custom put/get function in our driver, isn't it?
> > > >
> > >
> > > I'm not sure I understand. If you use (val / 32) to pick the
> > > register and (val %
> > > 32) to pick the bit in the register this should work just fine.
> > > Maybe I'm missing something. Do you have a real world code example
> > > of of the this type of enum is used?
> > >
> >
> > I can use val/32 and val%32 for this multi register mux.
> 
With this logic, put function would be easy however get function becomes tedious due to looping on each bit per register for 3 of them in our case. Rather, it would be easy to identify a unique MUX_OFFSET to distinguish between the 3
registers as shown in the code below. 

These get/put functions are updated from previous mail, now it works for multi register mux, please review these function whether I can add in current put/get function.

#define MULTI_MUX_INPUT_OFFSET(n)	(5 * n)

int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec =
	snd_soc_dapm_kcontrol_codec(kcontrol);
	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
	unsigned int reg_val, val, reg_idx;

	if (e->reg[0] != SND_SOC_NOPM) {
		for (reg_idx = 0; reg_idx < e->num_regs; reg_idx++) {
	    		reg_val = snd_soc_read(codec, e->reg[reg_idx]);
			val = (reg_val >> e->shift_l) & e->mask[reg_idx];
			if (val) {
				val += MULTI_MUX_INPUT_OFFSET(reg_idx);
				break;
			}
		}
	} else {
		reg_val = dapm_kcontrol_get_value(kcontrol);
		val = (reg_val >> e->shift_l) & e->mask[0];
	}

	ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val);
   	if (e->shift_l != e->shift_r) {
    		val = (reg_val >> e->shift_r) & e->mask[0];
   		val = snd_soc_enum_val_to_item(e, val);
     		ucontrol->value.enumerated.item[1] = val;
       	}

	return 0;
}

int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec =
	snd_soc_dapm_kcontrol_codec(kcontrol);
	struct snd_soc_card *card = codec->card;
	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
	unsigned int *item = ucontrol->value.enumerated.item;
	unsigned int change, i, value, val, update_idx = 0;
	unsigned int mask;
	struct snd_soc_dapm_update update;
	int ret = 0, reg_val;

	if (item[0] >= e->items)
		return -EINVAL;

	val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
        	mask = e->mask[0] << e->shift_l;
	if (e->shift_l != e->shift_r) {
    		if (item[1] > e->items)
	    		return -EINVAL;
    	    	val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_l;
        		mask |= e->mask[0] << e->shift_r;
	}

	if (e->num_regs < 2) {
		value = val;
		goto update_reg;
	}

   	for (i = 0; i < e->num_regs; i++) {
		reg_val = val - MULTI_MUX_INPUT_OFFSET(i);

		/* checking reg_val is power of 2 : one-hot code */
		/* if reg_val after subtract MULTI_MUX_INPUT_OFFSET is not power of 2, reg[i] should be zero */
		if (reg_val & (reg_val - 1)) {
			/* clear the current input register */
			snd_soc_write(codec, e->reg[i], 0);
		} else {
			/* reg_val is power of 2, store updated info */
			value = reg_val;
			mask = e->mask[i];
			update_idx = i;
		}
	}

update_reg:
	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);

	if (e->reg[update_idx] != SND_SOC_NOPM)
        change = snd_soc_test_bits(codec, e->reg[update_idx], mask, value);
	else
        change = dapm_kcontrol_set_value(kcontrol, value);

    if (change) {
    	if (e->reg[update_idx] != SND_SOC_NOPM) {
	   	    update.kcontrol = kcontrol;
			update.reg = e->reg[update_idx];
       		update.mask= mask;
	       	update.val = value;
			card->update = &update;
       	}
       	ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e);
		card->update = NULL;
	}
	mutex_unlock(&card->dapm_mutex);

	if (ret > 0)
		soc_dpcm_runtime_update(card);

	return change;
}
> > > >>> -	int reg;
> > > >>> +	int reg[SOC_ENUM_MAX_REGS];
> > > >>>    	unsigned char shift_l;
> > > >>>    	unsigned char shift_r;
> > > >>>    	unsigned int items;
> > > >>> -	unsigned int mask;
> > > >>> +	unsigned int mask[SOC_ENUM_MAX_REGS];
> > > >>
> > > >> If you make mask and reg pointers instead of arrays this should
> > > >> be much more flexible and not be limited to 3 registers.
> 
We will make reg* and mask* instead of arrays and since we use the same structure, the plan is to share the get and put function code.

Thanks.
Songhee.


More information about the Alsa-devel mailing list