// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for AK4524 / AK4528 / AK4529 / AK4355 / AK4358 / AK4381
* AD and DA converters
*
* Copyright (c) 2000-2004 Jaroslav Kysela <perex@perex.cz>,
* Takashi Iwai <tiwai@suse.de>
*/
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include <sound/ak4xxx-adda.h>
#include <sound/info.h>
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");
/* write the given register and save the data to the cache */
void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
unsigned char val)
{
ak->ops.lock(ak, chip);
ak->ops.write(ak, chip, reg, val);
/* save the data */
snd_akm4xxx_set(ak, chip, reg, val);
ak->ops.unlock(ak, chip);
}
EXPORT_SYMBOL(snd_akm4xxx_write);
/* reset procedure for AK4524 and AK4528 */
static void ak4524_reset(struct snd_akm4xxx *ak, int state)
{
unsigned int chip;
unsigned char reg;
for (chip = 0; chip < ak->num_dacs/2; chip++) {
snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
if (state)
continue;
/* DAC volumes */
for (reg = 0x04; reg < ak->total_regs; reg++)
snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(ak, chip, reg));
}
}
/* reset procedure for AK4355 and AK4358 */
static void ak435X_reset(struct snd_akm4xxx *ak, int state)
{
unsigned char reg;
if (state) {
snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
return;
}
for (reg = 0x00; reg < ak->total_regs; reg++)
if (reg != 0x01)
snd_akm4xxx_write(ak, 0, reg,
snd_akm4xxx_get(ak, 0, reg));
snd_akm4xxx_write(ak, 0, 0x01, 0x01); /* un-reset, unmute */
}
/* reset procedure for AK4381 */
static void ak4381_reset(struct snd_akm4xxx *ak, int state)
{
unsigned int chip;
unsigned char reg;
for (chip = 0; chip < ak->num_dacs/2; chip++) {
snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
if (state)
continue;
for (reg = 0x01; reg < ak->total_regs; reg++)
snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(ak, chip, reg));
}
}
/*
* reset the AKM codecs
* @state: 1 = reset codec, 0 = restore the registers
*
* assert the reset operation and restores the register values to the chips.
*/
void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
{
switch (ak->type) {
case SND_AK4524:
case SND_AK4528:
case SND_AK4620:
ak4524_reset(ak, state);
break;
case SND_AK4529:
/* FIXME: needed for ak4529? */
break;
case SND_AK4355:
ak435X_reset(ak, state);
break;
case SND_AK4358:
ak435X_reset(ak, state);
break;
case SND_AK4381:
ak4381_reset(ak, state);
break;
default:
break;
}
}
EXPORT_SYMBOL(snd_akm4xxx_reset);
/*
* Volume conversion table for non-linear volumes
* from -63.5dB (mute) to 0dB step 0.5dB
*
* Used for AK4524/AK4620 input/ouput attenuation, AK4528, and
* AK5365 input attenuation
*/
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,
0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f,
0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14,
0x15, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1a, 0x1c,
0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x23,
0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2d,
0x2e, 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3e, 0x3f, 0x40,
0x41, 0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4a,
0x4b, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53, 0x54,
0x55, 0x56