[Alsa-devel] [RFC 4/7] snd-aoa: add codecs

Takashi Iwai tiwai at suse.de
Mon May 29 22:08:41 EST 2006


At Sun, 28 May 2006 21:00:30 +0200,
Johannes Berg wrote:
> --- /dev/null
> +++ b/sound/aoa/codecs/snd-aoa-codec-onyx.c
> +/* both return 0 if all ok, else on error */
> +static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value)
> +{
> +	s32 v;
> +
> +	if (reg != ONYX_REG_CONTROL) {
> +		*value = onyx->cache[reg-FIRSTREGISTER];
> +		return 0;
> +	}
> +	v = i2c_smbus_read_byte_data(&onyx->i2c, reg);
> +	if (v < 0)
> +		return -1;
> +	*value = (u8)v;
> +	onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value;

Isn't it "reg - FIRSTREGISTER" ?

> +	return 0;
> +}
> +
> +static int onyx_write_register(struct onyx *onyx, u8 reg, u8 value)
> +{
> +	int result;
> +
> +	result = i2c_smbus_write_byte_data(&onyx->i2c, reg, value);
> +	if (!result)
> +		onyx->cache[reg-FIRSTREGISTER] = value;
> +	return result;
> +}
> +
> +/* alsa stuff */
> +
> +static int onyx_dev_register(struct snd_device *dev)
> +{
> +	return 0;
> +}
> +
> +static struct snd_device_ops ops = {
> +	.dev_register = onyx_dev_register,
> +};
> +
> +static int onyx_snd_vol_info(struct snd_kcontrol *kcontrol,
> +	struct snd_ctl_elem_info *uinfo)
> +{
> +	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
> +	uinfo->count = 2;
> +	uinfo->value.integer.min = -128+128;
> +	uinfo->value.integer.max = -1+128;

I'd define a constant for 128.

> +	return 0;
> +}
> +
> +static int onyx_snd_vol_get(struct snd_kcontrol *kcontrol,
> +	struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
> +	s8 l,r;
> +
> +	mutex_lock(&onyx->mutex);
> +	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
> +	onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
> +	mutex_unlock(&onyx->mutex);
> +
> +	ucontrol->value.integer.value[0] = l+128;
> +	ucontrol->value.integer.value[1] = r+128;
> +
> +	return 0;
> +}
> +
> +static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
> +	struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct onyx *onyx = snd_kcontrol_chip(kcontrol);
> +
> +	mutex_lock(&onyx->mutex);
> +	onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, ucontrol->value.integer.value[0]-128);
> +	onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, ucontrol->value.integer.value[1]-128);

Fold lines to fit with 80 columns (heh, blaming other one's code is
easy :)

> +	/* FIXME: we could be checking if anything changed */
> +	mutex_unlock(&onyx->mutex);
> +
> +	return 1;

The put callback is supposed to return 0 if the values are unchanged
(although most apps ignore the return value).

(snip)
> +static u8 register_map[] = {
> +	ONYX_REG_DAC_ATTEN_LEFT,
> +	ONYX_REG_DAC_ATTEN_RIGHT,
> +	ONYX_REG_CONTROL,
> +	ONYX_REG_DAC_CONTROL,
> +	ONYX_REG_DAC_DEEMPH,
> +	ONYX_REG_DAC_FILTER,
> +	ONYX_REG_DAC_OUTPHASE,
> +	ONYX_REG_ADC_CONTROL,
> +	ONYX_REG_ADC_HPF_BYPASS,
> +	ONYX_REG_DIG_INFO1,
> +	ONYX_REG_DIG_INFO2,
> +	ONYX_REG_DIG_INFO3,
> +	ONYX_REG_DIG_INFO4
> +};
> +
> +static u8 initial_values[] = {

Should have ARRAY_SIZE(register_map) since this size must be identical
with register_map.

> +	0x80, 0x80, /* muted */
> +	ONYX_MRST | ONYX_SRST, /* but handled specially! */
> +	ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT,
> +	0, /* no deemphasis */
> +	ONYX_DAC_FILTER_ALWAYS,
> +	ONYX_OUTPHASE_INVERTED,
> +	(-1 /*dB*/ + 8) & 0xF, /* line in selected, -1 dB gain*/
> +	ONYX_ADC_HPF_ALWAYS,
> +	(1<<2),	/* pcm audio */
> +	2,	/* category: pcm coder */
> +	0,	/* sampling frequency 44.1 kHz, clock accuracy level II */
> +	1	/* 24 bit depth */
> +};
> +
> +/* reset registers of chip, either to initial or to previous values */
> +static int onyx_register_init(struct onyx *onyx)
> +{
> +	int i;
> +	u8 val;
> +	u8 regs[sizeof(initial_values)];
> +
> +	if (!onyx->initialised) {
> +		memcpy(regs, initial_values, sizeof(initial_values));
> +		if (onyx_read_register(onyx, ONYX_REG_CONTROL, &val))
> +			return -1;
> +		val &= ~ONYX_SILICONVERSION;
> +		val |= initial_values[3];
> +		regs[3] = val;
> +	} else {
> +		for (i=0; i<sizeof(register_map); i++)
> +			regs[i] = onyx->cache[register_map[i]-FIRSTREGISTER];
> +	}
> +
> +	for (i=0; i<sizeof(register_map); i++) {
> +		if (onyx_write_register(onyx, register_map[i], regs[i]))
> +			return -1;
> +	}
> +	onyx->initialised = 1;
> +	return 0;
> +}
> +
> +static struct transfer_info onyx_transfers[] = {
> +	/* this is first so we can skip it if no input is present...
> +	 * No hardware exists with that, but it's here as an example
> +	 * of what to do :) */
> +	{
> +		/* analog input */
> +		.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,

Too long lines ;)

(snip)
> +	if (onyx->codec.soundbus_dev->attach_codec(onyx->codec.soundbus_dev,
> +						   aoa_get_card(),
> +						   ci, onyx)) {
> +		printk(KERN_ERR PFX "error creating onyx pcm\n");
> +		return -ENODEV;
> +	}
> +#define ADDCTL(n)							\
> +	do {								\
> +		ctl = snd_ctl_new1(&n, onyx);				\
> +		if (ctl) {						\
> +			ctl->id.device =				\
> +				onyx->codec.soundbus_dev->pcm->device;	\
> +			aoa_snd_ctl_add(ctl);				\

No error check?

> +	/* we try to read from register ONYX_REG_CONTROL
> +	 * to check if the codec is present */
> +	if (onyx_read_register(onyx, ONYX_REG_CONTROL, &dummy) != 0) {
> +		i2c_detach_client(&onyx->i2c);
> +		printk(KERN_ERR PFX "failed to read control register\n");
> +		goto fail;
> +	}
> +
> +	strncpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN);

Use strlcpy, or MAX_CODEC_NAME_LEN-1.  Similar lines are found in
tas driver too.

> --- /dev/null
> +++ b/sound/aoa/codecs/snd-aoa-codec-tas.c
> @@ -0,0 +1,669 @@
(snip)
> +struct tas {
> +	u32			primary_magic;
> +	struct aoa_codec	codec;
> +	/* see comment at top of file */
> +	u32			secondary_magic;
> +	struct aoa_codec	secondary;
> +	struct i2c_client	i2c;
> +	u32			muted_l:1, muted_r:1,
> +				controls_created:1;
> +	u8			cached_volume_l, cached_volume_r;
> +	u8			mixer_l[3], mixer_r[3];
> +	u8			acr;
> +};
> +
> +static struct tas *codec_to_tas(struct aoa_codec *codec)
> +{
> +	u32 *tmp = (u32*)codec;
> +	switch (*(tmp-1)) {
> +	case TAS_PRIMARY_MAGIC:
> +		return container_of(codec, struct tas, codec);
> +	case TAS_SECONDARY_MAGIC:
> +		return container_of(codec, struct tas, secondary);
> +	default:
> +		return NULL;
> +	}
> +}

Looks a bit too hacky.  IMO, it's better to define a struct

	struct tas_codec {
		struct aoa_codec codec;
		struct tas *tas;
	}

to make the above simpler like:

	static struct tas *codec_to_tas(struct aoa_codec *codec)
	{
		return ((struct tas_codec *)codec)->tas;
	}

The tas struct becomes:

	struct as {
		struct tas_codec primary;
		struct tas_codec secondary;
		...
	}

and is initialized like:

	tas->primary.tas = tas;
	tas->secondary.tas = tas;



> +static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,
> +	struct snd_ctl_elem_info *uinfo)
> +{
> +	static char* texts[] = { "Line-In", "Microphone" };

char *texts[]

> +static int tas_reset_init(struct tas *tas)
> +{
> +	u8 tmp;
> +/*
> +	char write[8];
> +	union i2c_smbus_data read = { 0 };
> +	int r1, r2;
> +*/
> +	tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
> +	msleep(1);
> +	tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1);
> +	msleep(1);
> +	tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
> +	msleep(1);
> +
> +	tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
> +	tas->acr |= TAS_ACR_B_MONAUREAL | TAS_ACR_B_MON_SEL_RIGHT;
> +	if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
> +		return -ENODEV;
> +
> +	tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT;
> +	if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp))
> +		return -ENODEV;
> +
> +	tmp = 0;
> +	if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp))
> +		return -ENODEV;
> +/* I need help here!

Use ifdef.  Nested comments are bad.

> +	/* This is a bit tricky, but serves to detect if there really
> +	 * is a tas codec present.
> +	 * First, we set the volume register to 00,00,01 (on both channels).
> +	 * This is almost muted. Then, we read back the last 6 bytes we
> +	 * wrote to the chip, and check if they are the same.
> +	 *
> +	write[0] = 7;
> +	write[1] = TAS_REG_VOL;
> +	write[2] = write[3] = 0;
> +	write[4] = 1;
> +	write[5] = write[6] = 0;
> +	write[7] = 1;
> +	r1 = tas_write_reg(tas, TAS_REG_VOL, 6, &write[1]);
> +	/* Hmm, how am I supposed to do the i2c sequence that
> +	 * is mentioned on page 45 of the tas3004 datasheet?
> +	 * This doesn't cut it: *
> +	read.block[0] = 7;
> +	r2 = i2c_smbus_xfer(tas->i2c.adapter, tas->i2c.addr, tas->i2c.flags,
> +			    I2C_SMBUS_READ, TAS_REG_VOL,
> +			    I2C_SMBUS_BLOCK_DATA, &read);
> +
> +	printk(KERN_DEBUG "r1 = %d, r2 = %d, read=%x %x %x %x %x %x %x %x\n", r1, r2, read.block[0], read.block[1], read.block[2], read.block[3], read.block[4], read.block[5], read.block[6], read.block[7]);
> +
> +	if (r1 || r2 ||  memcmp(write, read.block, 8))
> +		return -ENODEV;
> +*/
> +
> +	return 0;
> +}
> +static int tas_init_codec(struct aoa_codec *codec)
> +{
(snip)
> +	aoa_snd_ctl_add(snd_ctl_new1(&volume_control, tas));

Error checks please.


Takashi



More information about the Linuxppc-dev mailing list