cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ak4531_codec.c (16685B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
      4 *  Universal routines for AK4531 codec
      5 */
      6
      7#include <linux/delay.h>
      8#include <linux/init.h>
      9#include <linux/slab.h>
     10#include <linux/mutex.h>
     11#include <linux/module.h>
     12
     13#include <sound/core.h>
     14#include <sound/ak4531_codec.h>
     15#include <sound/tlv.h>
     16
     17/*
     18MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
     19MODULE_DESCRIPTION("Universal routines for AK4531 codec");
     20MODULE_LICENSE("GPL");
     21*/
     22
     23static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531);
     24
     25/*
     26 *
     27 */
     28 
     29#if 0
     30
     31static void snd_ak4531_dump(struct snd_ak4531 *ak4531)
     32{
     33	int idx;
     34	
     35	for (idx = 0; idx < 0x19; idx++)
     36		printk(KERN_DEBUG "ak4531 0x%x: 0x%x\n",
     37		       idx, ak4531->regs[idx]);
     38}
     39
     40#endif
     41
     42/*
     43 *
     44 */
     45
     46#define AK4531_SINGLE(xname, xindex, reg, shift, mask, invert) \
     47{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
     48  .info = snd_ak4531_info_single, \
     49  .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
     50  .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) }
     51#define AK4531_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv)    \
     52{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
     53  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
     54  .name = xname, .index = xindex, \
     55  .info = snd_ak4531_info_single, \
     56  .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
     57  .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22), \
     58  .tlv = { .p = (xtlv) } }
     59
     60static int snd_ak4531_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
     61{
     62	int mask = (kcontrol->private_value >> 24) & 0xff;
     63
     64	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
     65	uinfo->count = 1;
     66	uinfo->value.integer.min = 0;
     67	uinfo->value.integer.max = mask;
     68	return 0;
     69}
     70 
     71static int snd_ak4531_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
     72{
     73	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
     74	int reg = kcontrol->private_value & 0xff;
     75	int shift = (kcontrol->private_value >> 16) & 0x07;
     76	int mask = (kcontrol->private_value >> 24) & 0xff;
     77	int invert = (kcontrol->private_value >> 22) & 1;
     78	int val;
     79
     80	mutex_lock(&ak4531->reg_mutex);
     81	val = (ak4531->regs[reg] >> shift) & mask;
     82	mutex_unlock(&ak4531->reg_mutex);
     83	if (invert) {
     84		val = mask - val;
     85	}
     86	ucontrol->value.integer.value[0] = val;
     87	return 0;
     88}
     89
     90static int snd_ak4531_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
     91{
     92	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
     93	int reg = kcontrol->private_value & 0xff;
     94	int shift = (kcontrol->private_value >> 16) & 0x07;
     95	int mask = (kcontrol->private_value >> 24) & 0xff;
     96	int invert = (kcontrol->private_value >> 22) & 1;
     97	int change;
     98	int val;
     99
    100	val = ucontrol->value.integer.value[0] & mask;
    101	if (invert) {
    102		val = mask - val;
    103	}
    104	val <<= shift;
    105	mutex_lock(&ak4531->reg_mutex);
    106	val = (ak4531->regs[reg] & ~(mask << shift)) | val;
    107	change = val != ak4531->regs[reg];
    108	ak4531->write(ak4531, reg, ak4531->regs[reg] = val);
    109	mutex_unlock(&ak4531->reg_mutex);
    110	return change;
    111}
    112
    113#define AK4531_DOUBLE(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert) \
    114{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
    115  .info = snd_ak4531_info_double, \
    116  .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
    117  .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) }
    118#define AK4531_DOUBLE_TLV(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert, xtlv) \
    119{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
    120  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
    121  .name = xname, .index = xindex, \
    122  .info = snd_ak4531_info_double, \
    123  .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
    124  .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22), \
    125  .tlv = { .p = (xtlv) } }
    126
    127static int snd_ak4531_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
    128{
    129	int mask = (kcontrol->private_value >> 24) & 0xff;
    130
    131	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
    132	uinfo->count = 2;
    133	uinfo->value.integer.min = 0;
    134	uinfo->value.integer.max = mask;
    135	return 0;
    136}
    137 
    138static int snd_ak4531_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
    139{
    140	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
    141	int left_reg = kcontrol->private_value & 0xff;
    142	int right_reg = (kcontrol->private_value >> 8) & 0xff;
    143	int left_shift = (kcontrol->private_value >> 16) & 0x07;
    144	int right_shift = (kcontrol->private_value >> 19) & 0x07;
    145	int mask = (kcontrol->private_value >> 24) & 0xff;
    146	int invert = (kcontrol->private_value >> 22) & 1;
    147	int left, right;
    148
    149	mutex_lock(&ak4531->reg_mutex);
    150	left = (ak4531->regs[left_reg] >> left_shift) & mask;
    151	right = (ak4531->regs[right_reg] >> right_shift) & mask;
    152	mutex_unlock(&ak4531->reg_mutex);
    153	if (invert) {
    154		left = mask - left;
    155		right = mask - right;
    156	}
    157	ucontrol->value.integer.value[0] = left;
    158	ucontrol->value.integer.value[1] = right;
    159	return 0;
    160}
    161
    162static int snd_ak4531_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
    163{
    164	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
    165	int left_reg = kcontrol->private_value & 0xff;
    166	int right_reg = (kcontrol->private_value >> 8) & 0xff;
    167	int left_shift = (kcontrol->private_value >> 16) & 0x07;
    168	int right_shift = (kcontrol->private_value >> 19) & 0x07;
    169	int mask = (kcontrol->private_value >> 24) & 0xff;
    170	int invert = (kcontrol->private_value >> 22) & 1;
    171	int change;
    172	int left, right;
    173
    174	left = ucontrol->value.integer.value[0] & mask;
    175	right = ucontrol->value.integer.value[1] & mask;
    176	if (invert) {
    177		left = mask - left;
    178		right = mask - right;
    179	}
    180	left <<= left_shift;
    181	right <<= right_shift;
    182	mutex_lock(&ak4531->reg_mutex);
    183	if (left_reg == right_reg) {
    184		left = (ak4531->regs[left_reg] & ~((mask << left_shift) | (mask << right_shift))) | left | right;
    185		change = left != ak4531->regs[left_reg];
    186		ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left);
    187	} else {
    188		left = (ak4531->regs[left_reg] & ~(mask << left_shift)) | left;
    189		right = (ak4531->regs[right_reg] & ~(mask << right_shift)) | right;
    190		change = left != ak4531->regs[left_reg] || right != ak4531->regs[right_reg];
    191		ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left);
    192		ak4531->write(ak4531, right_reg, ak4531->regs[right_reg] = right);
    193	}
    194	mutex_unlock(&ak4531->reg_mutex);
    195	return change;
    196}
    197
    198#define AK4531_INPUT_SW(xname, xindex, reg1, reg2, left_shift, right_shift) \
    199{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
    200  .info = snd_ak4531_info_input_sw, \
    201  .get = snd_ak4531_get_input_sw, .put = snd_ak4531_put_input_sw, \
    202  .private_value = reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) }
    203
    204static int snd_ak4531_info_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
    205{
    206	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
    207	uinfo->count = 4;
    208	uinfo->value.integer.min = 0;
    209	uinfo->value.integer.max = 1;
    210	return 0;
    211}
    212 
    213static int snd_ak4531_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
    214{
    215	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
    216	int reg1 = kcontrol->private_value & 0xff;
    217	int reg2 = (kcontrol->private_value >> 8) & 0xff;
    218	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
    219	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
    220
    221	mutex_lock(&ak4531->reg_mutex);
    222	ucontrol->value.integer.value[0] = (ak4531->regs[reg1] >> left_shift) & 1;
    223	ucontrol->value.integer.value[1] = (ak4531->regs[reg2] >> left_shift) & 1;
    224	ucontrol->value.integer.value[2] = (ak4531->regs[reg1] >> right_shift) & 1;
    225	ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1;
    226	mutex_unlock(&ak4531->reg_mutex);
    227	return 0;
    228}
    229
    230static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
    231{
    232	struct snd_ak4531 *ak4531 = snd_kcontrol_chip(kcontrol);
    233	int reg1 = kcontrol->private_value & 0xff;
    234	int reg2 = (kcontrol->private_value >> 8) & 0xff;
    235	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
    236	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
    237	int change;
    238	int val1, val2;
    239
    240	mutex_lock(&ak4531->reg_mutex);
    241	val1 = ak4531->regs[reg1] & ~((1 << left_shift) | (1 << right_shift));
    242	val2 = ak4531->regs[reg2] & ~((1 << left_shift) | (1 << right_shift));
    243	val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
    244	val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
    245	val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
    246	val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
    247	change = val1 != ak4531->regs[reg1] || val2 != ak4531->regs[reg2];
    248	ak4531->write(ak4531, reg1, ak4531->regs[reg1] = val1);
    249	ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2);
    250	mutex_unlock(&ak4531->reg_mutex);
    251	return change;
    252}
    253
    254static const DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0);
    255static const DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
    256static const DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
    257
    258static const struct snd_kcontrol_new snd_ak4531_controls[] = {
    259
    260AK4531_DOUBLE_TLV("Master Playback Switch", 0,
    261		  AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1,
    262		  db_scale_master),
    263AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1),
    264
    265AK4531_SINGLE_TLV("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1,
    266		  db_scale_mono),
    267AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1),
    268
    269AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1),
    270AK4531_DOUBLE_TLV("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1,
    271		  db_scale_input),
    272AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0),
    273AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0),
    274
    275AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1),
    276AK4531_DOUBLE_TLV("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1,
    277		  db_scale_input),
    278AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0),
    279AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5),
    280
    281AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1),
    282AK4531_DOUBLE_TLV("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1,
    283		  db_scale_input),
    284AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0),
    285AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1),
    286
    287AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1),
    288AK4531_DOUBLE_TLV("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1,
    289		  db_scale_input),
    290AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0),
    291AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3),
    292
    293AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1),
    294AK4531_DOUBLE_TLV("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1,
    295		  db_scale_input),
    296AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0),
    297AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3),
    298
    299AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1),
    300AK4531_SINGLE_TLV("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1, db_scale_input),
    301AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0),
    302AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0),
    303
    304AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1),
    305AK4531_SINGLE_TLV("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1, db_scale_input),
    306AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0),
    307AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0),
    308
    309AK4531_SINGLE_TLV("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1, db_scale_input),
    310AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1),
    311AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0),
    312AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0),
    313
    314AK4531_DOUBLE("Mic Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 7, 7, 1, 0),
    315AK4531_DOUBLE("Mono1 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 6, 6, 1, 0),
    316AK4531_DOUBLE("Mono2 Bypass Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 5, 5, 1, 0),
    317
    318AK4531_SINGLE("AD Input Select", 0, AK4531_AD_IN, 0, 1, 0),
    319AK4531_SINGLE("Mic Boost (+30dB)", 0, AK4531_MIC_GAIN, 0, 1, 0)
    320};
    321
    322static int snd_ak4531_free(struct snd_ak4531 *ak4531)
    323{
    324	if (ak4531) {
    325		if (ak4531->private_free)
    326			ak4531->private_free(ak4531);
    327		kfree(ak4531);
    328	}
    329	return 0;
    330}
    331
    332static int snd_ak4531_dev_free(struct snd_device *device)
    333{
    334	struct snd_ak4531 *ak4531 = device->device_data;
    335	return snd_ak4531_free(ak4531);
    336}
    337
    338static const u8 snd_ak4531_initial_map[0x19 + 1] = {
    339	0x9f,		/* 00: Master Volume Lch */
    340	0x9f,		/* 01: Master Volume Rch */
    341	0x9f,		/* 02: Voice Volume Lch */
    342	0x9f,		/* 03: Voice Volume Rch */
    343	0x9f,		/* 04: FM Volume Lch */
    344	0x9f,		/* 05: FM Volume Rch */
    345	0x9f,		/* 06: CD Audio Volume Lch */
    346	0x9f,		/* 07: CD Audio Volume Rch */
    347	0x9f,		/* 08: Line Volume Lch */
    348	0x9f,		/* 09: Line Volume Rch */
    349	0x9f,		/* 0a: Aux Volume Lch */
    350	0x9f,		/* 0b: Aux Volume Rch */
    351	0x9f,		/* 0c: Mono1 Volume */
    352	0x9f,		/* 0d: Mono2 Volume */
    353	0x9f,		/* 0e: Mic Volume */
    354	0x87,		/* 0f: Mono-out Volume */
    355	0x00,		/* 10: Output Mixer SW1 */
    356	0x00,		/* 11: Output Mixer SW2 */
    357	0x00,		/* 12: Lch Input Mixer SW1 */
    358	0x00,		/* 13: Rch Input Mixer SW1 */
    359	0x00,		/* 14: Lch Input Mixer SW2 */
    360	0x00,		/* 15: Rch Input Mixer SW2 */
    361	0x00,		/* 16: Reset & Power Down */
    362	0x00,		/* 17: Clock Select */
    363	0x00,		/* 18: AD Input Select */
    364	0x01		/* 19: Mic Amp Setup */
    365};
    366
    367int snd_ak4531_mixer(struct snd_card *card,
    368		     struct snd_ak4531 *_ak4531,
    369		     struct snd_ak4531 **rak4531)
    370{
    371	unsigned int idx;
    372	int err;
    373	struct snd_ak4531 *ak4531;
    374	static const struct snd_device_ops ops = {
    375		.dev_free =	snd_ak4531_dev_free,
    376	};
    377
    378	if (snd_BUG_ON(!card || !_ak4531))
    379		return -EINVAL;
    380	if (rak4531)
    381		*rak4531 = NULL;
    382	ak4531 = kzalloc(sizeof(*ak4531), GFP_KERNEL);
    383	if (ak4531 == NULL)
    384		return -ENOMEM;
    385	*ak4531 = *_ak4531;
    386	mutex_init(&ak4531->reg_mutex);
    387	err = snd_component_add(card, "AK4531");
    388	if (err < 0) {
    389		snd_ak4531_free(ak4531);
    390		return err;
    391	}
    392	strcpy(card->mixername, "Asahi Kasei AK4531");
    393	ak4531->write(ak4531, AK4531_RESET, 0x03);	/* no RST, PD */
    394	udelay(100);
    395	ak4531->write(ak4531, AK4531_CLOCK, 0x00);	/* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */
    396	for (idx = 0; idx <= 0x19; idx++) {
    397		if (idx == AK4531_RESET || idx == AK4531_CLOCK)
    398			continue;
    399		ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]);	/* recording source is mixer */
    400	}
    401	for (idx = 0; idx < ARRAY_SIZE(snd_ak4531_controls); idx++) {
    402		err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531));
    403		if (err < 0) {
    404			snd_ak4531_free(ak4531);
    405			return err;
    406		}
    407	}
    408	snd_ak4531_proc_init(card, ak4531);
    409	err = snd_device_new(card, SNDRV_DEV_CODEC, ak4531, &ops);
    410	if (err < 0) {
    411		snd_ak4531_free(ak4531);
    412		return err;
    413	}
    414
    415#if 0
    416	snd_ak4531_dump(ak4531);
    417#endif
    418	if (rak4531)
    419		*rak4531 = ak4531;
    420	return 0;
    421}
    422
    423/*
    424 * power management
    425 */
    426#ifdef CONFIG_PM
    427void snd_ak4531_suspend(struct snd_ak4531 *ak4531)
    428{
    429	/* mute */
    430	ak4531->write(ak4531, AK4531_LMASTER, 0x9f);
    431	ak4531->write(ak4531, AK4531_RMASTER, 0x9f);
    432	/* powerdown */
    433	ak4531->write(ak4531, AK4531_RESET, 0x01);
    434}
    435
    436void snd_ak4531_resume(struct snd_ak4531 *ak4531)
    437{
    438	int idx;
    439
    440	/* initialize */
    441	ak4531->write(ak4531, AK4531_RESET, 0x03);
    442	udelay(100);
    443	ak4531->write(ak4531, AK4531_CLOCK, 0x00);
    444	/* restore mixer registers */
    445	for (idx = 0; idx <= 0x19; idx++) {
    446		if (idx == AK4531_RESET || idx == AK4531_CLOCK)
    447			continue;
    448		ak4531->write(ak4531, idx, ak4531->regs[idx]);
    449	}
    450}
    451#endif
    452
    453/*
    454 * /proc interface
    455 */
    456
    457static void snd_ak4531_proc_read(struct snd_info_entry *entry, 
    458				 struct snd_info_buffer *buffer)
    459{
    460	struct snd_ak4531 *ak4531 = entry->private_data;
    461
    462	snd_iprintf(buffer, "Asahi Kasei AK4531\n\n");
    463	snd_iprintf(buffer, "Recording source   : %s\n"
    464		    "MIC gain           : %s\n",
    465		    ak4531->regs[AK4531_AD_IN] & 1 ? "external" : "mixer",
    466		    ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB");
    467}
    468
    469static void
    470snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
    471{
    472	snd_card_ro_proc_new(card, "ak4531", ak4531, snd_ak4531_proc_read);
    473}