* ALSA driver for AK4524 / AK4528 / AK4529 / AK4355 / AK4358 / AK4381
* AD and DA converters
*
- * Copyright (c) 2000-2004 Jaroslav Kysela <perex@suse.cz>,
+ * Copyright (c) 2000-2004 Jaroslav Kysela <perex@perex.cz>,
* Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <sound/tlv.h>
#include <sound/ak4xxx-adda.h>
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters");
MODULE_LICENSE("GPL");
* Used for AK4524 input/ouput attenuation, AK4528, and
* AK5365 input attenuation
*/
-static unsigned char vol_cvt_datt[128] = {
+static const unsigned char vol_cvt_datt[128] = {
0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x0a,
/*
* dB tables
*/
-static DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1);
-static DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1);
-static DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1);
-static DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1);
+static const DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0);
/*
* initialize all the ak4xxx chips
*/
void snd_akm4xxx_init(struct snd_akm4xxx *ak)
{
- static unsigned char inits_ak4524[] = {
+ static const unsigned char inits_ak4524[] = {
0x00, 0x07, /* 0: all power up */
0x01, 0x00, /* 1: ADC/DAC reset */
0x02, 0x60, /* 2: 24bit I2S */
0x07, 0x00, /* 7: DAC right muted */
0xff, 0xff
};
- static unsigned char inits_ak4528[] = {
+ static const unsigned char inits_ak4528[] = {
0x00, 0x07, /* 0: all power up */
0x01, 0x00, /* 1: ADC/DAC reset */
0x02, 0x60, /* 2: 24bit I2S */
0x05, 0x00, /* 5: ADC right muted */
0xff, 0xff
};
- static unsigned char inits_ak4529[] = {
+ static const unsigned char inits_ak4529[] = {
0x09, 0x01, /* 9: ATS=0, RSTN=1 */
0x0a, 0x3f, /* A: all power up, no zero/overflow detection */
0x00, 0x0c, /* 0: TDM=0, 24bit I2S, SMUTE=0 */
0x08, 0x55, /* 8: deemphasis all off */
0xff, 0xff
};
- static unsigned char inits_ak4355[] = {
+ static const unsigned char inits_ak4355[] = {
0x01, 0x02, /* 1: reset and soft-mute */
0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
* disable DZF, sharp roll-off, RSTN#=0 */
0x01, 0x01, /* 1: un-reset, unmute */
0xff, 0xff
};
- static unsigned char inits_ak4358[] = {
+ static const unsigned char inits_ak4358[] = {
0x01, 0x02, /* 1: reset and soft-mute */
0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
* disable DZF, sharp roll-off, RSTN#=0 */
0x01, 0x01, /* 1: un-reset, unmute */
0xff, 0xff
};
- static unsigned char inits_ak4381[] = {
+ static const unsigned char inits_ak4381[] = {
0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */
0x01, 0x02, /* 1: de-emphasis off, normal speed,
* sharp roll-off, DZF off */
};
int chip, num_chips;
- unsigned char *ptr, reg, data, *inits;
+ const unsigned char *ptr, *inits;
+ unsigned char reg, data;
memset(ak->images, 0, sizeof(ak->images));
memset(ak->volumes, 0, sizeof(ak->volumes));
case SND_AK5365:
/* FIXME: any init sequence? */
return;
+ case NON_AKM:
+ /* fake value for non-akm codecs using akm infrastructure
+ * (e.g. of ice1724) - certainly FIXME
+ */
+ return;
default:
snd_BUG();
return;
static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value),
- ucontrol->value.integer.value[0]);
+ unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+ unsigned int val = ucontrol->value.integer.value[0];
+ if (val > mask)
+ return -EINVAL;
+ return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value), val);
}
static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int addr = AK_GET_ADDR(kcontrol->private_value);
+ unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+ unsigned int val[2];
int change;
- change = put_ak_reg(kcontrol, addr, ucontrol->value.integer.value[0]);
- change |= put_ak_reg(kcontrol, addr + 1,
- ucontrol->value.integer.value[1]);
+ val[0] = ucontrol->value.integer.value[0];
+ val[1] = ucontrol->value.integer.value[1];
+ if (val[0] > mask || val[1] > mask)
+ return -EINVAL;
+ change = put_ak_reg(kcontrol, addr, val[0]);
+ change |= put_ak_reg(kcontrol, addr + 1, val[1]);
return change;
}
return change;
}
-static int ak4xxx_switch_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
-}
+#define ak4xxx_switch_info snd_ctl_boolean_mono_info
static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
int addr = AK_GET_ADDR(kcontrol->private_value);
int shift = AK_GET_SHIFT(kcontrol->private_value);
int invert = AK_GET_INVERT(kcontrol->private_value);
- unsigned char val = snd_akm4xxx_get(ak, chip, addr);
-
+ /* we observe the (1<<shift) bit only */
+ unsigned char val = snd_akm4xxx_get(ak, chip, addr) & (1<<shift);
if (invert)
val = ! val;
ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0;
return change;
}
+#define AK5365_NUM_INPUTS 5
+
+static int ak4xxx_capture_num_inputs(struct snd_akm4xxx *ak, int mixer_ch)
+{
+ int num_names;
+ const char **input_names;
+
+ input_names = ak->adc_info[mixer_ch].input_names;
+ num_names = 0;
+ while (num_names < AK5365_NUM_INPUTS && input_names[num_names])
+ ++num_names;
+ return num_names;
+}
+
+static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
+ const char **input_names;
+ int num_names, idx;
+
+ num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
+ if (!num_names)
+ return -EINVAL;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = num_names;
+ idx = uinfo->value.enumerated.item;
+ if (idx >= num_names)
+ return -EINVAL;
+ input_names = ak->adc_info[mixer_ch].input_names;
+ strncpy(uinfo->value.enumerated.name, input_names[idx],
+ sizeof(uinfo->value.enumerated.name));
+ return 0;
+}
+
+static int ak4xxx_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int chip = AK_GET_CHIP(kcontrol->private_value);
+ int addr = AK_GET_ADDR(kcontrol->private_value);
+ int mask = AK_GET_MASK(kcontrol->private_value);
+ unsigned char val;
+
+ val = snd_akm4xxx_get(ak, chip, addr) & mask;
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int ak4xxx_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
+ int chip = AK_GET_CHIP(kcontrol->private_value);
+ int addr = AK_GET_ADDR(kcontrol->private_value);
+ int mask = AK_GET_MASK(kcontrol->private_value);
+ unsigned char oval, val;
+ int num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
+
+ if (ucontrol->value.enumerated.item[0] >= num_names)
+ return -EINVAL;
+
+ oval = snd_akm4xxx_get(ak, chip, addr);
+ val = oval & ~mask;
+ val |= ucontrol->value.enumerated.item[0] & mask;
+ if (val != oval) {
+ snd_akm4xxx_write(ak, chip, addr, val);
+ return 1;
+ }
+ return 0;
+}
+
/*
* build AK4xxx controls
*/
mixer_ch = 0;
for (idx = 0; idx < ak->num_dacs; ) {
+ /* mute control for Revolution 7.1 - AK4381 */
+ if (ak->type == SND_AK4381
+ && ak->dac_info[mixer_ch].switch_name) {
+ memset(&knew, 0, sizeof(knew));
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.count = 1;
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ knew.name = ak->dac_info[mixer_ch].switch_name;
+ knew.info = ak4xxx_switch_info;
+ knew.get = ak4xxx_switch_get;
+ knew.put = ak4xxx_switch_put;
+ knew.access = 0;
+ /* register 1, bit 0 (SMUTE): 0 = normal operation,
+ 1 = mute */
+ knew.private_value =
+ AK_COMPOSE(idx/2, 1, 0, 0) | AK_INVERT;
+ err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+ if (err < 0)
+ return err;
+ }
memset(&knew, 0, sizeof(knew));
if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
knew.name = "DAC Volume";
if (ak->type == SND_AK5365 && (idx % 2) == 0) {
if (! ak->adc_info ||
- ! ak->adc_info[mixer_ch].switch_name)
+ ! ak->adc_info[mixer_ch].switch_name) {
knew.name = "Capture Switch";
- else
+ knew.index = mixer_ch + ak->idx_offset * 2;
+ } else
knew.name = ak->adc_info[mixer_ch].switch_name;
knew.info = ak4xxx_switch_info;
knew.get = ak4xxx_switch_get;
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
if (err < 0)
return err;
+
+ memset(&knew, 0, sizeof(knew));
+ knew.name = ak->adc_info[mixer_ch].selector_name;
+ if (!knew.name) {
+ knew.name = "Capture Channel";
+ knew.index = mixer_ch + ak->idx_offset * 2;
+ }
+
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.info = ak4xxx_capture_source_info;
+ knew.get = ak4xxx_capture_source_get;
+ knew.put = ak4xxx_capture_source_put;
+ knew.access = 0;
+ /* input selector control: reg. 1, bits 0-2.
+ * mis-use 'shift' to pass mixer_ch */
+ knew.private_value
+ = AK_COMPOSE(idx/2, 1, mixer_ch, 0x07);
+ err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+ if (err < 0)
+ return err;
}
idx += num_stereo;