[alsa-devel] [patch 4/6] cs42l51: add asoc driver

Arnaud Patard apatard at mandriva.com
Wed May 12 15:46:53 CEST 2010


Mark Brown <broonie at opensource.wolfsonmicro.com> writes:

Hi,

> On Tue, May 11, 2010 at 06:23:46PM +0200, apatard at mandriva.com wrote:
>
>> This patch is adding a ASoC driver for the cs42l51 from Cirrus Logic.
>> Master mode and spi mode are not supported.
>
> This is mostly OK - there's quite a few comments below but there's no
> massive structural things in here, it's generally small, local things.
>
>> + * Based on cs4270.c - Copyright (c) Timur Tabi <timur at freescale.com>
>
> While we're picking up on the copyright stuff IIRC it's actually
> Freescale copyright rather than Timur's personal copyright :)
>
>> +enum funct_mode {
>> +	MODE_SLAVE,
>> +	MODE_SLAVE_AUTO,
>> +	MODE_MASTER,
>> +};
>
> I'd prefer a more meaningful name than "funct_mode" here - I don't
> really know what a funct is.
>
>> +static unsigned int cs42l51_read_reg_cache(struct snd_soc_codec *codec,
>> +	unsigned int reg)
>> +{
>> +	u8 *cache = codec->reg_cache;
>> +
>> +	if ((reg < CS42L51_FIRSTREG) || (reg > CS42L51_LASTREG))
>> +		return -EIO;
>> +
>> +	return cache[reg - CS42L51_FIRSTREG];
>
> Please just use the standard register cache access stuff in soc-cache.c.
> The first register is 1 so you're only burning a single byte of RAM by
> using the generic code.

I'm using i2c_smbus_read_i2c_block_data() and didn't notice it in the
soc-cache.c file. I didn't look further so it's possible that there's
actually something working for my case.

>
>> +	if (ret != MK_CHIP_REV(CHIP_ID, CHIP_REV)) {
>
> Are you sure there's only one device revision in production?

According to the specs there are 2 revs id, the A and B. As I've got a
rev B, I assumed that the rev A has not been on prod. I've changed the
check to handle both revs.

>
>> +		dev_err(&i2c_client->dev, "Invalid chip id\n");
>> +		ret = -ENODEV;
>> +		goto error;
>> +	}
>
>> +	dev_info(&i2c_client->dev, "found device at I2C address %X\n",
>> +				i2c_client->addr);
>
> The dev_() will already be including the I2C address in the log message
> anyway.  I'd be inclined to just drop this, or make it log the device
> revision.

I'll make it log the device revision then. I prefer having a message
telling us something was found than no message at all. It's usefull when
debugging.

>
>> +	ret = cs42l51_fill_cache(codec);
>> +	if (ret < 0) {
>> +		dev_err(&i2c_client->dev, "failed to fill register cache\n");
>> +		goto error;
>> +	}
>
> Normal practice is to reset the chip so it's in a known sensible state
> when the driver starts trying to use it.  Is there a good reason not to
> do this here?

The default reset state is good except for the bits I'm changing few
lines later.

>
>> +	/*
>> +	 * DAC configuration (right place to do that ?)
>
> It's OK to do this for things that the user would unconditionally want.
>
>> +	 * - Use signal processor
>
> What does this mean? It sounds like something that would need to be
> power managed via DAPM?

there are 3 options for DAC:
00 - PCM Serial Port to DAC
01 - Signal Processing Engine to DAC
10 - ADC Serial Port to DAC

but only the "signal processing engine to DAC" configuration is
useful. Using others are dropping some functionnalities.

>
>> +	 * - auto mute
>
> This is probably OK, but exposing as a user visible control would be
> better.

there's a control for that. I'm only setting the register in a known
good state.

>
>> +	 * - vol changes immediate
>
> This is OK.
>
>> +	 * - no de-emphasize
>
> This is normally exposed as a user selectable switch.

idem

>
>> +	/* Unmute PCM-A & PCM-B and set default vol to */
>> +	ret = cs42l51_i2c_write(codec, PCMA_VOL, 0x60);
>> +	if (ret < 0)
>> +		goto error;
>
> This should not be done in the driver, leave it up to userspace.  This
> driver will be used by all systems using this CODEC so just rely on the
> hardware defaults to provide a sane power on configuration - the
> designers will usually ensure that this is at least not harmful.
>

ok. Default is 0db with channel enabled so it's fine I guess.

>> +	/* route microphone */
>> +	ret = cs42l51_i2c_write(codec, ADC_INPUT, 0xF0);
>> +	if (ret < 0)
>> +		goto error;
>
> Ditto.

That's the only way to get microphone support, that's why I didn't
consider it as an option.

>
>> +error_reg:
>> +	snd_soc_unregister_codec(codec);
>> +error:
>> +	return ret;
>
> You're missing some error handling here - there's no free of the device
> data, for example.
>
>> +static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
>> +			struct snd_ctl_elem_value *ucontrol)
>> +{
>> +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
>> +	unsigned long value = snd_soc_read(codec, PCM_MIXER)&3;
>> +
>> +	switch (value) {
>> +	default:
>> +	case 0:
>> +		ucontrol->value.integer.value[0] = 0;
>> +		break;
>> +	case 1:
>> +	case 2:
>> +		ucontrol->value.integer.value[0] = 1;
>> +		break;
>> +	case 3:
>> +		ucontrol->value.integer.value[0] = 2;
>> +		break;
>> +	}
>> +
>> +	return 0;
>> +}
>
> Some comments here might be a little clearer...  Why is the difference
> between 1 and 2 not interesting?

1 and 2 are 2 values for the same things. (L+R)/2 signal is sent to both channels.

>
>> +static const char *mic_boost[] = { "+16dB", "+32dB"};
>
> Could use TLV for this too.
>
>> +static const struct soc_enum cs42l51_mix[] = {
>> +	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(chan_mix), chan_mix),
>> +	SOC_ENUM_DOUBLE(MIC_CTL, 0, 1, 2, mic_boost),
>> +};
>
> Don't do this, declare separate variables for each.  Indexing into a
> table of mixer elements doesn't help leigibility and is asking for off
> by one errors.  Some drivers do this for historical reasons but modern
> drivers don't.
>
>> +	SOC_DOUBLE_R("PCM Playback Switch", PCMA_VOL, PCMB_VOL, 7, 1, 1),
>
> I suspect that this should be managed via the DAI mute function.

I wondered about this too and I choose to not use it. My thoughts was
that if I was to go and implement the DAI mute, it should rather mute
PCM playback and Analog playback channels at the same time I guess.

>
>> +	SOC_SINGLE("Playback Deemphasis", DAC_CTL, 3, 1, 0),
>
> Should have "Switch" at the end of the control name.
>
>> +	SOC_SINGLE("Mic Bias", MIC_POWER_CTL, 1, 1, 1),
>
> This should be a DAPM MICBIAS control.
>
>> +	SOC_DOUBLE("Mic Powerdown", MIC_POWER_CTL, 2, 3, 1, 0),
>
> What does this do?  Sounds like it should be DAPM controls.

It's to power down (or not) the 2 preamplifiers of the microphone
channels and not to power down the microphone. Maybe the name is not the
best one. Better names accepted.
With this new explanation, should I convert it to DAPM or is it fine as
mixer control ?


Arnaud


More information about the Alsa-devel mailing list