[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