18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA driver for AK4524 / AK4528 / AK4529 / AK4355 / AK4358 / AK4381 48c2ecf20Sopenharmony_ci * AD and DA converters 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2000-2004 Jaroslav Kysela <perex@perex.cz>, 78c2ecf20Sopenharmony_ci * Takashi Iwai <tiwai@suse.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <sound/core.h> 168c2ecf20Sopenharmony_ci#include <sound/control.h> 178c2ecf20Sopenharmony_ci#include <sound/tlv.h> 188c2ecf20Sopenharmony_ci#include <sound/ak4xxx-adda.h> 198c2ecf20Sopenharmony_ci#include <sound/info.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>"); 228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters"); 238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* write the given register and save the data to the cache */ 268c2ecf20Sopenharmony_civoid snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, 278c2ecf20Sopenharmony_ci unsigned char val) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci ak->ops.lock(ak, chip); 308c2ecf20Sopenharmony_ci ak->ops.write(ak, chip, reg, val); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci /* save the data */ 338c2ecf20Sopenharmony_ci snd_akm4xxx_set(ak, chip, reg, val); 348c2ecf20Sopenharmony_ci ak->ops.unlock(ak, chip); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_akm4xxx_write); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* reset procedure for AK4524 and AK4528 */ 408c2ecf20Sopenharmony_cistatic void ak4524_reset(struct snd_akm4xxx *ak, int state) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci unsigned int chip; 438c2ecf20Sopenharmony_ci unsigned char reg; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci for (chip = 0; chip < ak->num_dacs/2; chip++) { 468c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03); 478c2ecf20Sopenharmony_ci if (state) 488c2ecf20Sopenharmony_ci continue; 498c2ecf20Sopenharmony_ci /* DAC volumes */ 508c2ecf20Sopenharmony_ci for (reg = 0x04; reg < ak->total_regs; reg++) 518c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, chip, reg, 528c2ecf20Sopenharmony_ci snd_akm4xxx_get(ak, chip, reg)); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* reset procedure for AK4355 and AK4358 */ 578c2ecf20Sopenharmony_cistatic void ak435X_reset(struct snd_akm4xxx *ak, int state) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci unsigned char reg; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (state) { 628c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */ 638c2ecf20Sopenharmony_ci return; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci for (reg = 0x00; reg < ak->total_regs; reg++) 668c2ecf20Sopenharmony_ci if (reg != 0x01) 678c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, 0, reg, 688c2ecf20Sopenharmony_ci snd_akm4xxx_get(ak, 0, reg)); 698c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, 0, 0x01, 0x01); /* un-reset, unmute */ 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* reset procedure for AK4381 */ 738c2ecf20Sopenharmony_cistatic void ak4381_reset(struct snd_akm4xxx *ak, int state) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci unsigned int chip; 768c2ecf20Sopenharmony_ci unsigned char reg; 778c2ecf20Sopenharmony_ci for (chip = 0; chip < ak->num_dacs/2; chip++) { 788c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f); 798c2ecf20Sopenharmony_ci if (state) 808c2ecf20Sopenharmony_ci continue; 818c2ecf20Sopenharmony_ci for (reg = 0x01; reg < ak->total_regs; reg++) 828c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, chip, reg, 838c2ecf20Sopenharmony_ci snd_akm4xxx_get(ak, chip, reg)); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * reset the AKM codecs 898c2ecf20Sopenharmony_ci * @state: 1 = reset codec, 0 = restore the registers 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * assert the reset operation and restores the register values to the chips. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_civoid snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci switch (ak->type) { 968c2ecf20Sopenharmony_ci case SND_AK4524: 978c2ecf20Sopenharmony_ci case SND_AK4528: 988c2ecf20Sopenharmony_ci case SND_AK4620: 998c2ecf20Sopenharmony_ci ak4524_reset(ak, state); 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci case SND_AK4529: 1028c2ecf20Sopenharmony_ci /* FIXME: needed for ak4529? */ 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci case SND_AK4355: 1058c2ecf20Sopenharmony_ci ak435X_reset(ak, state); 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci case SND_AK4358: 1088c2ecf20Sopenharmony_ci ak435X_reset(ak, state); 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci case SND_AK4381: 1118c2ecf20Sopenharmony_ci ak4381_reset(ak, state); 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci default: 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_akm4xxx_reset); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * Volume conversion table for non-linear volumes 1238c2ecf20Sopenharmony_ci * from -63.5dB (mute) to 0dB step 0.5dB 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci * Used for AK4524/AK4620 input/ouput attenuation, AK4528, and 1268c2ecf20Sopenharmony_ci * AK5365 input attenuation 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_cistatic const unsigned char vol_cvt_datt[128] = { 1298c2ecf20Sopenharmony_ci 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 1308c2ecf20Sopenharmony_ci 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 1318c2ecf20Sopenharmony_ci 0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x0a, 1328c2ecf20Sopenharmony_ci 0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 1338c2ecf20Sopenharmony_ci 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14, 1348c2ecf20Sopenharmony_ci 0x15, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1a, 0x1c, 1358c2ecf20Sopenharmony_ci 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x23, 1368c2ecf20Sopenharmony_ci 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2d, 1378c2ecf20Sopenharmony_ci 0x2e, 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 1388c2ecf20Sopenharmony_ci 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3e, 0x3f, 0x40, 1398c2ecf20Sopenharmony_ci 0x41, 0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4a, 1408c2ecf20Sopenharmony_ci 0x4b, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53, 0x54, 1418c2ecf20Sopenharmony_ci 0x55, 0x56, 0x58, 0x59, 0x5b, 0x5c, 0x5e, 0x5f, 1428c2ecf20Sopenharmony_ci 0x60, 0x61, 0x62, 0x64, 0x65, 0x66, 0x67, 0x69, 1438c2ecf20Sopenharmony_ci 0x6a, 0x6c, 0x6d, 0x6f, 0x70, 0x71, 0x72, 0x73, 1448c2ecf20Sopenharmony_ci 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7f, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* 1488c2ecf20Sopenharmony_ci * dB tables 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1); 1518c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1); 1528c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1); 1538c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * initialize all the ak4xxx chips 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_civoid snd_akm4xxx_init(struct snd_akm4xxx *ak) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci static const unsigned char inits_ak4524[] = { 1618c2ecf20Sopenharmony_ci 0x00, 0x07, /* 0: all power up */ 1628c2ecf20Sopenharmony_ci 0x01, 0x00, /* 1: ADC/DAC reset */ 1638c2ecf20Sopenharmony_ci 0x02, 0x60, /* 2: 24bit I2S */ 1648c2ecf20Sopenharmony_ci 0x03, 0x19, /* 3: deemphasis off */ 1658c2ecf20Sopenharmony_ci 0x01, 0x03, /* 1: ADC/DAC enable */ 1668c2ecf20Sopenharmony_ci 0x04, 0x00, /* 4: ADC left muted */ 1678c2ecf20Sopenharmony_ci 0x05, 0x00, /* 5: ADC right muted */ 1688c2ecf20Sopenharmony_ci 0x06, 0x00, /* 6: DAC left muted */ 1698c2ecf20Sopenharmony_ci 0x07, 0x00, /* 7: DAC right muted */ 1708c2ecf20Sopenharmony_ci 0xff, 0xff 1718c2ecf20Sopenharmony_ci }; 1728c2ecf20Sopenharmony_ci static const unsigned char inits_ak4528[] = { 1738c2ecf20Sopenharmony_ci 0x00, 0x07, /* 0: all power up */ 1748c2ecf20Sopenharmony_ci 0x01, 0x00, /* 1: ADC/DAC reset */ 1758c2ecf20Sopenharmony_ci 0x02, 0x60, /* 2: 24bit I2S */ 1768c2ecf20Sopenharmony_ci 0x03, 0x0d, /* 3: deemphasis off, turn LR highpass filters on */ 1778c2ecf20Sopenharmony_ci 0x01, 0x03, /* 1: ADC/DAC enable */ 1788c2ecf20Sopenharmony_ci 0x04, 0x00, /* 4: ADC left muted */ 1798c2ecf20Sopenharmony_ci 0x05, 0x00, /* 5: ADC right muted */ 1808c2ecf20Sopenharmony_ci 0xff, 0xff 1818c2ecf20Sopenharmony_ci }; 1828c2ecf20Sopenharmony_ci static const unsigned char inits_ak4529[] = { 1838c2ecf20Sopenharmony_ci 0x09, 0x01, /* 9: ATS=0, RSTN=1 */ 1848c2ecf20Sopenharmony_ci 0x0a, 0x3f, /* A: all power up, no zero/overflow detection */ 1858c2ecf20Sopenharmony_ci 0x00, 0x0c, /* 0: TDM=0, 24bit I2S, SMUTE=0 */ 1868c2ecf20Sopenharmony_ci 0x01, 0x00, /* 1: ACKS=0, ADC, loop off */ 1878c2ecf20Sopenharmony_ci 0x02, 0xff, /* 2: LOUT1 muted */ 1888c2ecf20Sopenharmony_ci 0x03, 0xff, /* 3: ROUT1 muted */ 1898c2ecf20Sopenharmony_ci 0x04, 0xff, /* 4: LOUT2 muted */ 1908c2ecf20Sopenharmony_ci 0x05, 0xff, /* 5: ROUT2 muted */ 1918c2ecf20Sopenharmony_ci 0x06, 0xff, /* 6: LOUT3 muted */ 1928c2ecf20Sopenharmony_ci 0x07, 0xff, /* 7: ROUT3 muted */ 1938c2ecf20Sopenharmony_ci 0x0b, 0xff, /* B: LOUT4 muted */ 1948c2ecf20Sopenharmony_ci 0x0c, 0xff, /* C: ROUT4 muted */ 1958c2ecf20Sopenharmony_ci 0x08, 0x55, /* 8: deemphasis all off */ 1968c2ecf20Sopenharmony_ci 0xff, 0xff 1978c2ecf20Sopenharmony_ci }; 1988c2ecf20Sopenharmony_ci static const unsigned char inits_ak4355[] = { 1998c2ecf20Sopenharmony_ci 0x01, 0x02, /* 1: reset and soft-mute */ 2008c2ecf20Sopenharmony_ci 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, 2018c2ecf20Sopenharmony_ci * disable DZF, sharp roll-off, RSTN#=0 */ 2028c2ecf20Sopenharmony_ci 0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */ 2038c2ecf20Sopenharmony_ci // 0x02, 0x2e, /* quad speed */ 2048c2ecf20Sopenharmony_ci 0x03, 0x01, /* 3: de-emphasis off */ 2058c2ecf20Sopenharmony_ci 0x04, 0x00, /* 4: LOUT1 volume muted */ 2068c2ecf20Sopenharmony_ci 0x05, 0x00, /* 5: ROUT1 volume muted */ 2078c2ecf20Sopenharmony_ci 0x06, 0x00, /* 6: LOUT2 volume muted */ 2088c2ecf20Sopenharmony_ci 0x07, 0x00, /* 7: ROUT2 volume muted */ 2098c2ecf20Sopenharmony_ci 0x08, 0x00, /* 8: LOUT3 volume muted */ 2108c2ecf20Sopenharmony_ci 0x09, 0x00, /* 9: ROUT3 volume muted */ 2118c2ecf20Sopenharmony_ci 0x0a, 0x00, /* a: DATT speed=0, ignore DZF */ 2128c2ecf20Sopenharmony_ci 0x01, 0x01, /* 1: un-reset, unmute */ 2138c2ecf20Sopenharmony_ci 0xff, 0xff 2148c2ecf20Sopenharmony_ci }; 2158c2ecf20Sopenharmony_ci static const unsigned char inits_ak4358[] = { 2168c2ecf20Sopenharmony_ci 0x01, 0x02, /* 1: reset and soft-mute */ 2178c2ecf20Sopenharmony_ci 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, 2188c2ecf20Sopenharmony_ci * disable DZF, sharp roll-off, RSTN#=0 */ 2198c2ecf20Sopenharmony_ci 0x02, 0x4e, /* 2: DA's power up, normal speed, RSTN#=0 */ 2208c2ecf20Sopenharmony_ci /* 0x02, 0x6e,*/ /* quad speed */ 2218c2ecf20Sopenharmony_ci 0x03, 0x01, /* 3: de-emphasis off */ 2228c2ecf20Sopenharmony_ci 0x04, 0x00, /* 4: LOUT1 volume muted */ 2238c2ecf20Sopenharmony_ci 0x05, 0x00, /* 5: ROUT1 volume muted */ 2248c2ecf20Sopenharmony_ci 0x06, 0x00, /* 6: LOUT2 volume muted */ 2258c2ecf20Sopenharmony_ci 0x07, 0x00, /* 7: ROUT2 volume muted */ 2268c2ecf20Sopenharmony_ci 0x08, 0x00, /* 8: LOUT3 volume muted */ 2278c2ecf20Sopenharmony_ci 0x09, 0x00, /* 9: ROUT3 volume muted */ 2288c2ecf20Sopenharmony_ci 0x0b, 0x00, /* b: LOUT4 volume muted */ 2298c2ecf20Sopenharmony_ci 0x0c, 0x00, /* c: ROUT4 volume muted */ 2308c2ecf20Sopenharmony_ci 0x0a, 0x00, /* a: DATT speed=0, ignore DZF */ 2318c2ecf20Sopenharmony_ci 0x01, 0x01, /* 1: un-reset, unmute */ 2328c2ecf20Sopenharmony_ci 0xff, 0xff 2338c2ecf20Sopenharmony_ci }; 2348c2ecf20Sopenharmony_ci static const unsigned char inits_ak4381[] = { 2358c2ecf20Sopenharmony_ci 0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */ 2368c2ecf20Sopenharmony_ci 0x01, 0x02, /* 1: de-emphasis off, normal speed, 2378c2ecf20Sopenharmony_ci * sharp roll-off, DZF off */ 2388c2ecf20Sopenharmony_ci // 0x01, 0x12, /* quad speed */ 2398c2ecf20Sopenharmony_ci 0x02, 0x00, /* 2: DZF disabled */ 2408c2ecf20Sopenharmony_ci 0x03, 0x00, /* 3: LATT 0 */ 2418c2ecf20Sopenharmony_ci 0x04, 0x00, /* 4: RATT 0 */ 2428c2ecf20Sopenharmony_ci 0x00, 0x0f, /* 0: power-up, un-reset */ 2438c2ecf20Sopenharmony_ci 0xff, 0xff 2448c2ecf20Sopenharmony_ci }; 2458c2ecf20Sopenharmony_ci static const unsigned char inits_ak4620[] = { 2468c2ecf20Sopenharmony_ci 0x00, 0x07, /* 0: normal */ 2478c2ecf20Sopenharmony_ci 0x01, 0x00, /* 0: reset */ 2488c2ecf20Sopenharmony_ci 0x01, 0x02, /* 1: RSTAD */ 2498c2ecf20Sopenharmony_ci 0x01, 0x03, /* 1: RSTDA */ 2508c2ecf20Sopenharmony_ci 0x01, 0x0f, /* 1: normal */ 2518c2ecf20Sopenharmony_ci 0x02, 0x60, /* 2: 24bit I2S */ 2528c2ecf20Sopenharmony_ci 0x03, 0x01, /* 3: deemphasis off */ 2538c2ecf20Sopenharmony_ci 0x04, 0x00, /* 4: LIN muted */ 2548c2ecf20Sopenharmony_ci 0x05, 0x00, /* 5: RIN muted */ 2558c2ecf20Sopenharmony_ci 0x06, 0x00, /* 6: LOUT muted */ 2568c2ecf20Sopenharmony_ci 0x07, 0x00, /* 7: ROUT muted */ 2578c2ecf20Sopenharmony_ci 0xff, 0xff 2588c2ecf20Sopenharmony_ci }; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci int chip; 2618c2ecf20Sopenharmony_ci const unsigned char *ptr, *inits; 2628c2ecf20Sopenharmony_ci unsigned char reg, data; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci memset(ak->images, 0, sizeof(ak->images)); 2658c2ecf20Sopenharmony_ci memset(ak->volumes, 0, sizeof(ak->volumes)); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci switch (ak->type) { 2688c2ecf20Sopenharmony_ci case SND_AK4524: 2698c2ecf20Sopenharmony_ci inits = inits_ak4524; 2708c2ecf20Sopenharmony_ci ak->num_chips = ak->num_dacs / 2; 2718c2ecf20Sopenharmony_ci ak->name = "ak4524"; 2728c2ecf20Sopenharmony_ci ak->total_regs = 0x08; 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci case SND_AK4528: 2758c2ecf20Sopenharmony_ci inits = inits_ak4528; 2768c2ecf20Sopenharmony_ci ak->num_chips = ak->num_dacs / 2; 2778c2ecf20Sopenharmony_ci ak->name = "ak4528"; 2788c2ecf20Sopenharmony_ci ak->total_regs = 0x06; 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci case SND_AK4529: 2818c2ecf20Sopenharmony_ci inits = inits_ak4529; 2828c2ecf20Sopenharmony_ci ak->num_chips = 1; 2838c2ecf20Sopenharmony_ci ak->name = "ak4529"; 2848c2ecf20Sopenharmony_ci ak->total_regs = 0x0d; 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci case SND_AK4355: 2878c2ecf20Sopenharmony_ci inits = inits_ak4355; 2888c2ecf20Sopenharmony_ci ak->num_chips = 1; 2898c2ecf20Sopenharmony_ci ak->name = "ak4355"; 2908c2ecf20Sopenharmony_ci ak->total_regs = 0x0b; 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci case SND_AK4358: 2938c2ecf20Sopenharmony_ci inits = inits_ak4358; 2948c2ecf20Sopenharmony_ci ak->num_chips = 1; 2958c2ecf20Sopenharmony_ci ak->name = "ak4358"; 2968c2ecf20Sopenharmony_ci ak->total_regs = 0x10; 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci case SND_AK4381: 2998c2ecf20Sopenharmony_ci inits = inits_ak4381; 3008c2ecf20Sopenharmony_ci ak->num_chips = ak->num_dacs / 2; 3018c2ecf20Sopenharmony_ci ak->name = "ak4381"; 3028c2ecf20Sopenharmony_ci ak->total_regs = 0x05; 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci case SND_AK5365: 3058c2ecf20Sopenharmony_ci /* FIXME: any init sequence? */ 3068c2ecf20Sopenharmony_ci ak->num_chips = 1; 3078c2ecf20Sopenharmony_ci ak->name = "ak5365"; 3088c2ecf20Sopenharmony_ci ak->total_regs = 0x08; 3098c2ecf20Sopenharmony_ci return; 3108c2ecf20Sopenharmony_ci case SND_AK4620: 3118c2ecf20Sopenharmony_ci inits = inits_ak4620; 3128c2ecf20Sopenharmony_ci ak->num_chips = ak->num_dacs / 2; 3138c2ecf20Sopenharmony_ci ak->name = "ak4620"; 3148c2ecf20Sopenharmony_ci ak->total_regs = 0x08; 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci default: 3178c2ecf20Sopenharmony_ci snd_BUG(); 3188c2ecf20Sopenharmony_ci return; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci for (chip = 0; chip < ak->num_chips; chip++) { 3228c2ecf20Sopenharmony_ci ptr = inits; 3238c2ecf20Sopenharmony_ci while (*ptr != 0xff) { 3248c2ecf20Sopenharmony_ci reg = *ptr++; 3258c2ecf20Sopenharmony_ci data = *ptr++; 3268c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, chip, reg, data); 3278c2ecf20Sopenharmony_ci udelay(10); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_akm4xxx_init); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/* 3358c2ecf20Sopenharmony_ci * Mixer callbacks 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci#define AK_IPGA (1<<20) /* including IPGA */ 3388c2ecf20Sopenharmony_ci#define AK_VOL_CVT (1<<21) /* need dB conversion */ 3398c2ecf20Sopenharmony_ci#define AK_NEEDSMSB (1<<22) /* need MSB update bit */ 3408c2ecf20Sopenharmony_ci#define AK_INVERT (1<<23) /* data is inverted */ 3418c2ecf20Sopenharmony_ci#define AK_GET_CHIP(val) (((val) >> 8) & 0xff) 3428c2ecf20Sopenharmony_ci#define AK_GET_ADDR(val) ((val) & 0xff) 3438c2ecf20Sopenharmony_ci#define AK_GET_SHIFT(val) (((val) >> 16) & 0x0f) 3448c2ecf20Sopenharmony_ci#define AK_GET_VOL_CVT(val) (((val) >> 21) & 1) 3458c2ecf20Sopenharmony_ci#define AK_GET_IPGA(val) (((val) >> 20) & 1) 3468c2ecf20Sopenharmony_ci#define AK_GET_NEEDSMSB(val) (((val) >> 22) & 1) 3478c2ecf20Sopenharmony_ci#define AK_GET_INVERT(val) (((val) >> 23) & 1) 3488c2ecf20Sopenharmony_ci#define AK_GET_MASK(val) (((val) >> 24) & 0xff) 3498c2ecf20Sopenharmony_ci#define AK_COMPOSE(chip,addr,shift,mask) \ 3508c2ecf20Sopenharmony_ci (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24)) 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int snd_akm4xxx_volume_info(struct snd_kcontrol *kcontrol, 3538c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci unsigned int mask = AK_GET_MASK(kcontrol->private_value); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3588c2ecf20Sopenharmony_ci uinfo->count = 1; 3598c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 3608c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int snd_akm4xxx_volume_get(struct snd_kcontrol *kcontrol, 3658c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); 3688c2ecf20Sopenharmony_ci int chip = AK_GET_CHIP(kcontrol->private_value); 3698c2ecf20Sopenharmony_ci int addr = AK_GET_ADDR(kcontrol->private_value); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr); 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int put_ak_reg(struct snd_kcontrol *kcontrol, int addr, 3768c2ecf20Sopenharmony_ci unsigned char nval) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); 3798c2ecf20Sopenharmony_ci unsigned int mask = AK_GET_MASK(kcontrol->private_value); 3808c2ecf20Sopenharmony_ci int chip = AK_GET_CHIP(kcontrol->private_value); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (snd_akm4xxx_get_vol(ak, chip, addr) == nval) 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci snd_akm4xxx_set_vol(ak, chip, addr, nval); 3868c2ecf20Sopenharmony_ci if (AK_GET_VOL_CVT(kcontrol->private_value) && nval < 128) 3878c2ecf20Sopenharmony_ci nval = vol_cvt_datt[nval]; 3888c2ecf20Sopenharmony_ci if (AK_GET_IPGA(kcontrol->private_value) && nval >= 128) 3898c2ecf20Sopenharmony_ci nval++; /* need to correct + 1 since both 127 and 128 are 0dB */ 3908c2ecf20Sopenharmony_ci if (AK_GET_INVERT(kcontrol->private_value)) 3918c2ecf20Sopenharmony_ci nval = mask - nval; 3928c2ecf20Sopenharmony_ci if (AK_GET_NEEDSMSB(kcontrol->private_value)) 3938c2ecf20Sopenharmony_ci nval |= 0x80; 3948c2ecf20Sopenharmony_ci /* printk(KERN_DEBUG "DEBUG - AK writing reg: chip %x addr %x, 3958c2ecf20Sopenharmony_ci nval %x\n", chip, addr, nval); */ 3968c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, chip, addr, nval); 3978c2ecf20Sopenharmony_ci return 1; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol, 4018c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci unsigned int mask = AK_GET_MASK(kcontrol->private_value); 4048c2ecf20Sopenharmony_ci unsigned int val = ucontrol->value.integer.value[0]; 4058c2ecf20Sopenharmony_ci if (val > mask) 4068c2ecf20Sopenharmony_ci return -EINVAL; 4078c2ecf20Sopenharmony_ci return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value), val); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol, 4118c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci unsigned int mask = AK_GET_MASK(kcontrol->private_value); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4168c2ecf20Sopenharmony_ci uinfo->count = 2; 4178c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 4188c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int snd_akm4xxx_stereo_volume_get(struct snd_kcontrol *kcontrol, 4238c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); 4268c2ecf20Sopenharmony_ci int chip = AK_GET_CHIP(kcontrol->private_value); 4278c2ecf20Sopenharmony_ci int addr = AK_GET_ADDR(kcontrol->private_value); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr); 4308c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = snd_akm4xxx_get_vol(ak, chip, addr+1); 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol, 4358c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci int addr = AK_GET_ADDR(kcontrol->private_value); 4388c2ecf20Sopenharmony_ci unsigned int mask = AK_GET_MASK(kcontrol->private_value); 4398c2ecf20Sopenharmony_ci unsigned int val[2]; 4408c2ecf20Sopenharmony_ci int change; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci val[0] = ucontrol->value.integer.value[0]; 4438c2ecf20Sopenharmony_ci val[1] = ucontrol->value.integer.value[1]; 4448c2ecf20Sopenharmony_ci if (val[0] > mask || val[1] > mask) 4458c2ecf20Sopenharmony_ci return -EINVAL; 4468c2ecf20Sopenharmony_ci change = put_ak_reg(kcontrol, addr, val[0]); 4478c2ecf20Sopenharmony_ci change |= put_ak_reg(kcontrol, addr + 1, val[1]); 4488c2ecf20Sopenharmony_ci return change; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic int snd_akm4xxx_deemphasis_info(struct snd_kcontrol *kcontrol, 4528c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci static const char * const texts[4] = { 4558c2ecf20Sopenharmony_ci "44.1kHz", "Off", "48kHz", "32kHz", 4568c2ecf20Sopenharmony_ci }; 4578c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 4, texts); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic int snd_akm4xxx_deemphasis_get(struct snd_kcontrol *kcontrol, 4618c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); 4648c2ecf20Sopenharmony_ci int chip = AK_GET_CHIP(kcontrol->private_value); 4658c2ecf20Sopenharmony_ci int addr = AK_GET_ADDR(kcontrol->private_value); 4668c2ecf20Sopenharmony_ci int shift = AK_GET_SHIFT(kcontrol->private_value); 4678c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 4688c2ecf20Sopenharmony_ci (snd_akm4xxx_get(ak, chip, addr) >> shift) & 3; 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int snd_akm4xxx_deemphasis_put(struct snd_kcontrol *kcontrol, 4738c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); 4768c2ecf20Sopenharmony_ci int chip = AK_GET_CHIP(kcontrol->private_value); 4778c2ecf20Sopenharmony_ci int addr = AK_GET_ADDR(kcontrol->private_value); 4788c2ecf20Sopenharmony_ci int shift = AK_GET_SHIFT(kcontrol->private_value); 4798c2ecf20Sopenharmony_ci unsigned char nval = ucontrol->value.enumerated.item[0] & 3; 4808c2ecf20Sopenharmony_ci int change; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci nval = (nval << shift) | 4838c2ecf20Sopenharmony_ci (snd_akm4xxx_get(ak, chip, addr) & ~(3 << shift)); 4848c2ecf20Sopenharmony_ci change = snd_akm4xxx_get(ak, chip, addr) != nval; 4858c2ecf20Sopenharmony_ci if (change) 4868c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, chip, addr, nval); 4878c2ecf20Sopenharmony_ci return change; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci#define ak4xxx_switch_info snd_ctl_boolean_mono_info 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int ak4xxx_switch_get(struct snd_kcontrol *kcontrol, 4938c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); 4968c2ecf20Sopenharmony_ci int chip = AK_GET_CHIP(kcontrol->private_value); 4978c2ecf20Sopenharmony_ci int addr = AK_GET_ADDR(kcontrol->private_value); 4988c2ecf20Sopenharmony_ci int shift = AK_GET_SHIFT(kcontrol->private_value); 4998c2ecf20Sopenharmony_ci int invert = AK_GET_INVERT(kcontrol->private_value); 5008c2ecf20Sopenharmony_ci /* we observe the (1<<shift) bit only */ 5018c2ecf20Sopenharmony_ci unsigned char val = snd_akm4xxx_get(ak, chip, addr) & (1<<shift); 5028c2ecf20Sopenharmony_ci if (invert) 5038c2ecf20Sopenharmony_ci val = ! val; 5048c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0; 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic int ak4xxx_switch_put(struct snd_kcontrol *kcontrol, 5098c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); 5128c2ecf20Sopenharmony_ci int chip = AK_GET_CHIP(kcontrol->private_value); 5138c2ecf20Sopenharmony_ci int addr = AK_GET_ADDR(kcontrol->private_value); 5148c2ecf20Sopenharmony_ci int shift = AK_GET_SHIFT(kcontrol->private_value); 5158c2ecf20Sopenharmony_ci int invert = AK_GET_INVERT(kcontrol->private_value); 5168c2ecf20Sopenharmony_ci long flag = ucontrol->value.integer.value[0]; 5178c2ecf20Sopenharmony_ci unsigned char val, oval; 5188c2ecf20Sopenharmony_ci int change; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (invert) 5218c2ecf20Sopenharmony_ci flag = ! flag; 5228c2ecf20Sopenharmony_ci oval = snd_akm4xxx_get(ak, chip, addr); 5238c2ecf20Sopenharmony_ci if (flag) 5248c2ecf20Sopenharmony_ci val = oval | (1<<shift); 5258c2ecf20Sopenharmony_ci else 5268c2ecf20Sopenharmony_ci val = oval & ~(1<<shift); 5278c2ecf20Sopenharmony_ci change = (oval != val); 5288c2ecf20Sopenharmony_ci if (change) 5298c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, chip, addr, val); 5308c2ecf20Sopenharmony_ci return change; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci#define AK5365_NUM_INPUTS 5 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int ak4xxx_capture_num_inputs(struct snd_akm4xxx *ak, int mixer_ch) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci int num_names; 5388c2ecf20Sopenharmony_ci const char **input_names; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci input_names = ak->adc_info[mixer_ch].input_names; 5418c2ecf20Sopenharmony_ci num_names = 0; 5428c2ecf20Sopenharmony_ci while (num_names < AK5365_NUM_INPUTS && input_names[num_names]) 5438c2ecf20Sopenharmony_ci ++num_names; 5448c2ecf20Sopenharmony_ci return num_names; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol, 5488c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); 5518c2ecf20Sopenharmony_ci int mixer_ch = AK_GET_SHIFT(kcontrol->private_value); 5528c2ecf20Sopenharmony_ci unsigned int num_names; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci num_names = ak4xxx_capture_num_inputs(ak, mixer_ch); 5558c2ecf20Sopenharmony_ci if (!num_names) 5568c2ecf20Sopenharmony_ci return -EINVAL; 5578c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, num_names, 5588c2ecf20Sopenharmony_ci ak->adc_info[mixer_ch].input_names); 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic int ak4xxx_capture_source_get(struct snd_kcontrol *kcontrol, 5628c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); 5658c2ecf20Sopenharmony_ci int chip = AK_GET_CHIP(kcontrol->private_value); 5668c2ecf20Sopenharmony_ci int addr = AK_GET_ADDR(kcontrol->private_value); 5678c2ecf20Sopenharmony_ci int mask = AK_GET_MASK(kcontrol->private_value); 5688c2ecf20Sopenharmony_ci unsigned char val; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci val = snd_akm4xxx_get(ak, chip, addr) & mask; 5718c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = val; 5728c2ecf20Sopenharmony_ci return 0; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic int ak4xxx_capture_source_put(struct snd_kcontrol *kcontrol, 5768c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); 5798c2ecf20Sopenharmony_ci int mixer_ch = AK_GET_SHIFT(kcontrol->private_value); 5808c2ecf20Sopenharmony_ci int chip = AK_GET_CHIP(kcontrol->private_value); 5818c2ecf20Sopenharmony_ci int addr = AK_GET_ADDR(kcontrol->private_value); 5828c2ecf20Sopenharmony_ci int mask = AK_GET_MASK(kcontrol->private_value); 5838c2ecf20Sopenharmony_ci unsigned char oval, val; 5848c2ecf20Sopenharmony_ci int num_names = ak4xxx_capture_num_inputs(ak, mixer_ch); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] >= num_names) 5878c2ecf20Sopenharmony_ci return -EINVAL; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci oval = snd_akm4xxx_get(ak, chip, addr); 5908c2ecf20Sopenharmony_ci val = oval & ~mask; 5918c2ecf20Sopenharmony_ci val |= ucontrol->value.enumerated.item[0] & mask; 5928c2ecf20Sopenharmony_ci if (val != oval) { 5938c2ecf20Sopenharmony_ci snd_akm4xxx_write(ak, chip, addr, val); 5948c2ecf20Sopenharmony_ci return 1; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci return 0; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci/* 6008c2ecf20Sopenharmony_ci * build AK4xxx controls 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic int build_dac_controls(struct snd_akm4xxx *ak) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci int idx, err, mixer_ch, num_stereo; 6068c2ecf20Sopenharmony_ci struct snd_kcontrol_new knew; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci mixer_ch = 0; 6098c2ecf20Sopenharmony_ci for (idx = 0; idx < ak->num_dacs; ) { 6108c2ecf20Sopenharmony_ci /* mute control for Revolution 7.1 - AK4381 */ 6118c2ecf20Sopenharmony_ci if (ak->type == SND_AK4381 6128c2ecf20Sopenharmony_ci && ak->dac_info[mixer_ch].switch_name) { 6138c2ecf20Sopenharmony_ci memset(&knew, 0, sizeof(knew)); 6148c2ecf20Sopenharmony_ci knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 6158c2ecf20Sopenharmony_ci knew.count = 1; 6168c2ecf20Sopenharmony_ci knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 6178c2ecf20Sopenharmony_ci knew.name = ak->dac_info[mixer_ch].switch_name; 6188c2ecf20Sopenharmony_ci knew.info = ak4xxx_switch_info; 6198c2ecf20Sopenharmony_ci knew.get = ak4xxx_switch_get; 6208c2ecf20Sopenharmony_ci knew.put = ak4xxx_switch_put; 6218c2ecf20Sopenharmony_ci knew.access = 0; 6228c2ecf20Sopenharmony_ci /* register 1, bit 0 (SMUTE): 0 = normal operation, 6238c2ecf20Sopenharmony_ci 1 = mute */ 6248c2ecf20Sopenharmony_ci knew.private_value = 6258c2ecf20Sopenharmony_ci AK_COMPOSE(idx/2, 1, 0, 0) | AK_INVERT; 6268c2ecf20Sopenharmony_ci err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); 6278c2ecf20Sopenharmony_ci if (err < 0) 6288c2ecf20Sopenharmony_ci return err; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci memset(&knew, 0, sizeof(knew)); 6318c2ecf20Sopenharmony_ci if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) { 6328c2ecf20Sopenharmony_ci knew.name = "DAC Volume"; 6338c2ecf20Sopenharmony_ci knew.index = mixer_ch + ak->idx_offset * 2; 6348c2ecf20Sopenharmony_ci num_stereo = 1; 6358c2ecf20Sopenharmony_ci } else { 6368c2ecf20Sopenharmony_ci knew.name = ak->dac_info[mixer_ch].name; 6378c2ecf20Sopenharmony_ci num_stereo = ak->dac_info[mixer_ch].num_channels; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 6408c2ecf20Sopenharmony_ci knew.count = 1; 6418c2ecf20Sopenharmony_ci knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 6428c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ; 6438c2ecf20Sopenharmony_ci if (num_stereo == 2) { 6448c2ecf20Sopenharmony_ci knew.info = snd_akm4xxx_stereo_volume_info; 6458c2ecf20Sopenharmony_ci knew.get = snd_akm4xxx_stereo_volume_get; 6468c2ecf20Sopenharmony_ci knew.put = snd_akm4xxx_stereo_volume_put; 6478c2ecf20Sopenharmony_ci } else { 6488c2ecf20Sopenharmony_ci knew.info = snd_akm4xxx_volume_info; 6498c2ecf20Sopenharmony_ci knew.get = snd_akm4xxx_volume_get; 6508c2ecf20Sopenharmony_ci knew.put = snd_akm4xxx_volume_put; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci switch (ak->type) { 6538c2ecf20Sopenharmony_ci case SND_AK4524: 6548c2ecf20Sopenharmony_ci /* register 6 & 7 */ 6558c2ecf20Sopenharmony_ci knew.private_value = 6568c2ecf20Sopenharmony_ci AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127) | 6578c2ecf20Sopenharmony_ci AK_VOL_CVT; 6588c2ecf20Sopenharmony_ci knew.tlv.p = db_scale_vol_datt; 6598c2ecf20Sopenharmony_ci break; 6608c2ecf20Sopenharmony_ci case SND_AK4528: 6618c2ecf20Sopenharmony_ci /* register 4 & 5 */ 6628c2ecf20Sopenharmony_ci knew.private_value = 6638c2ecf20Sopenharmony_ci AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127) | 6648c2ecf20Sopenharmony_ci AK_VOL_CVT; 6658c2ecf20Sopenharmony_ci knew.tlv.p = db_scale_vol_datt; 6668c2ecf20Sopenharmony_ci break; 6678c2ecf20Sopenharmony_ci case SND_AK4529: { 6688c2ecf20Sopenharmony_ci /* registers 2-7 and b,c */ 6698c2ecf20Sopenharmony_ci int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb; 6708c2ecf20Sopenharmony_ci knew.private_value = 6718c2ecf20Sopenharmony_ci AK_COMPOSE(0, val, 0, 255) | AK_INVERT; 6728c2ecf20Sopenharmony_ci knew.tlv.p = db_scale_8bit; 6738c2ecf20Sopenharmony_ci break; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci case SND_AK4355: 6768c2ecf20Sopenharmony_ci /* register 4-9, chip #0 only */ 6778c2ecf20Sopenharmony_ci knew.private_value = AK_COMPOSE(0, idx + 4, 0, 255); 6788c2ecf20Sopenharmony_ci knew.tlv.p = db_scale_8bit; 6798c2ecf20Sopenharmony_ci break; 6808c2ecf20Sopenharmony_ci case SND_AK4358: { 6818c2ecf20Sopenharmony_ci /* register 4-9 and 11-12, chip #0 only */ 6828c2ecf20Sopenharmony_ci int addr = idx < 6 ? idx + 4 : idx + 5; 6838c2ecf20Sopenharmony_ci knew.private_value = 6848c2ecf20Sopenharmony_ci AK_COMPOSE(0, addr, 0, 127) | AK_NEEDSMSB; 6858c2ecf20Sopenharmony_ci knew.tlv.p = db_scale_7bit; 6868c2ecf20Sopenharmony_ci break; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci case SND_AK4381: 6898c2ecf20Sopenharmony_ci /* register 3 & 4 */ 6908c2ecf20Sopenharmony_ci knew.private_value = 6918c2ecf20Sopenharmony_ci AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); 6928c2ecf20Sopenharmony_ci knew.tlv.p = db_scale_linear; 6938c2ecf20Sopenharmony_ci break; 6948c2ecf20Sopenharmony_ci case SND_AK4620: 6958c2ecf20Sopenharmony_ci /* register 6 & 7 */ 6968c2ecf20Sopenharmony_ci knew.private_value = 6978c2ecf20Sopenharmony_ci AK_COMPOSE(idx/2, (idx%2) + 6, 0, 255); 6988c2ecf20Sopenharmony_ci knew.tlv.p = db_scale_linear; 6998c2ecf20Sopenharmony_ci break; 7008c2ecf20Sopenharmony_ci default: 7018c2ecf20Sopenharmony_ci return -EINVAL; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); 7058c2ecf20Sopenharmony_ci if (err < 0) 7068c2ecf20Sopenharmony_ci return err; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci idx += num_stereo; 7098c2ecf20Sopenharmony_ci mixer_ch++; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci return 0; 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic int build_adc_controls(struct snd_akm4xxx *ak) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci int idx, err, mixer_ch, num_stereo, max_steps; 7178c2ecf20Sopenharmony_ci struct snd_kcontrol_new knew; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci mixer_ch = 0; 7208c2ecf20Sopenharmony_ci if (ak->type == SND_AK4528) 7218c2ecf20Sopenharmony_ci return 0; /* no controls */ 7228c2ecf20Sopenharmony_ci for (idx = 0; idx < ak->num_adcs;) { 7238c2ecf20Sopenharmony_ci memset(&knew, 0, sizeof(knew)); 7248c2ecf20Sopenharmony_ci if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) { 7258c2ecf20Sopenharmony_ci knew.name = "ADC Volume"; 7268c2ecf20Sopenharmony_ci knew.index = mixer_ch + ak->idx_offset * 2; 7278c2ecf20Sopenharmony_ci num_stereo = 1; 7288c2ecf20Sopenharmony_ci } else { 7298c2ecf20Sopenharmony_ci knew.name = ak->adc_info[mixer_ch].name; 7308c2ecf20Sopenharmony_ci num_stereo = ak->adc_info[mixer_ch].num_channels; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7338c2ecf20Sopenharmony_ci knew.count = 1; 7348c2ecf20Sopenharmony_ci knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 7358c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ; 7368c2ecf20Sopenharmony_ci if (num_stereo == 2) { 7378c2ecf20Sopenharmony_ci knew.info = snd_akm4xxx_stereo_volume_info; 7388c2ecf20Sopenharmony_ci knew.get = snd_akm4xxx_stereo_volume_get; 7398c2ecf20Sopenharmony_ci knew.put = snd_akm4xxx_stereo_volume_put; 7408c2ecf20Sopenharmony_ci } else { 7418c2ecf20Sopenharmony_ci knew.info = snd_akm4xxx_volume_info; 7428c2ecf20Sopenharmony_ci knew.get = snd_akm4xxx_volume_get; 7438c2ecf20Sopenharmony_ci knew.put = snd_akm4xxx_volume_put; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci /* register 4 & 5 */ 7468c2ecf20Sopenharmony_ci if (ak->type == SND_AK5365) 7478c2ecf20Sopenharmony_ci max_steps = 152; 7488c2ecf20Sopenharmony_ci else 7498c2ecf20Sopenharmony_ci max_steps = 164; 7508c2ecf20Sopenharmony_ci knew.private_value = 7518c2ecf20Sopenharmony_ci AK_COMPOSE(idx/2, (idx%2) + 4, 0, max_steps) | 7528c2ecf20Sopenharmony_ci AK_VOL_CVT | AK_IPGA; 7538c2ecf20Sopenharmony_ci knew.tlv.p = db_scale_vol_datt; 7548c2ecf20Sopenharmony_ci err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); 7558c2ecf20Sopenharmony_ci if (err < 0) 7568c2ecf20Sopenharmony_ci return err; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (ak->type == SND_AK5365 && (idx % 2) == 0) { 7598c2ecf20Sopenharmony_ci if (! ak->adc_info || 7608c2ecf20Sopenharmony_ci ! ak->adc_info[mixer_ch].switch_name) { 7618c2ecf20Sopenharmony_ci knew.name = "Capture Switch"; 7628c2ecf20Sopenharmony_ci knew.index = mixer_ch + ak->idx_offset * 2; 7638c2ecf20Sopenharmony_ci } else 7648c2ecf20Sopenharmony_ci knew.name = ak->adc_info[mixer_ch].switch_name; 7658c2ecf20Sopenharmony_ci knew.info = ak4xxx_switch_info; 7668c2ecf20Sopenharmony_ci knew.get = ak4xxx_switch_get; 7678c2ecf20Sopenharmony_ci knew.put = ak4xxx_switch_put; 7688c2ecf20Sopenharmony_ci knew.access = 0; 7698c2ecf20Sopenharmony_ci /* register 2, bit 0 (SMUTE): 0 = normal operation, 7708c2ecf20Sopenharmony_ci 1 = mute */ 7718c2ecf20Sopenharmony_ci knew.private_value = 7728c2ecf20Sopenharmony_ci AK_COMPOSE(idx/2, 2, 0, 0) | AK_INVERT; 7738c2ecf20Sopenharmony_ci err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); 7748c2ecf20Sopenharmony_ci if (err < 0) 7758c2ecf20Sopenharmony_ci return err; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci memset(&knew, 0, sizeof(knew)); 7788c2ecf20Sopenharmony_ci if (!ak->adc_info || 7798c2ecf20Sopenharmony_ci !ak->adc_info[mixer_ch].selector_name) { 7808c2ecf20Sopenharmony_ci knew.name = "Capture Channel"; 7818c2ecf20Sopenharmony_ci knew.index = mixer_ch + ak->idx_offset * 2; 7828c2ecf20Sopenharmony_ci } else 7838c2ecf20Sopenharmony_ci knew.name = ak->adc_info[mixer_ch].selector_name; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7868c2ecf20Sopenharmony_ci knew.info = ak4xxx_capture_source_info; 7878c2ecf20Sopenharmony_ci knew.get = ak4xxx_capture_source_get; 7888c2ecf20Sopenharmony_ci knew.put = ak4xxx_capture_source_put; 7898c2ecf20Sopenharmony_ci knew.access = 0; 7908c2ecf20Sopenharmony_ci /* input selector control: reg. 1, bits 0-2. 7918c2ecf20Sopenharmony_ci * mis-use 'shift' to pass mixer_ch */ 7928c2ecf20Sopenharmony_ci knew.private_value 7938c2ecf20Sopenharmony_ci = AK_COMPOSE(idx/2, 1, mixer_ch, 0x07); 7948c2ecf20Sopenharmony_ci err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); 7958c2ecf20Sopenharmony_ci if (err < 0) 7968c2ecf20Sopenharmony_ci return err; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci idx += num_stereo; 8008c2ecf20Sopenharmony_ci mixer_ch++; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci return 0; 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci int idx, err; 8088c2ecf20Sopenharmony_ci struct snd_kcontrol_new knew; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci for (idx = 0; idx < num_emphs; idx++) { 8118c2ecf20Sopenharmony_ci memset(&knew, 0, sizeof(knew)); 8128c2ecf20Sopenharmony_ci knew.name = "Deemphasis"; 8138c2ecf20Sopenharmony_ci knew.index = idx + ak->idx_offset; 8148c2ecf20Sopenharmony_ci knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8158c2ecf20Sopenharmony_ci knew.count = 1; 8168c2ecf20Sopenharmony_ci knew.info = snd_akm4xxx_deemphasis_info; 8178c2ecf20Sopenharmony_ci knew.get = snd_akm4xxx_deemphasis_get; 8188c2ecf20Sopenharmony_ci knew.put = snd_akm4xxx_deemphasis_put; 8198c2ecf20Sopenharmony_ci switch (ak->type) { 8208c2ecf20Sopenharmony_ci case SND_AK4524: 8218c2ecf20Sopenharmony_ci case SND_AK4528: 8228c2ecf20Sopenharmony_ci case SND_AK4620: 8238c2ecf20Sopenharmony_ci /* register 3 */ 8248c2ecf20Sopenharmony_ci knew.private_value = AK_COMPOSE(idx, 3, 0, 0); 8258c2ecf20Sopenharmony_ci break; 8268c2ecf20Sopenharmony_ci case SND_AK4529: { 8278c2ecf20Sopenharmony_ci int shift = idx == 3 ? 6 : (2 - idx) * 2; 8288c2ecf20Sopenharmony_ci /* register 8 with shift */ 8298c2ecf20Sopenharmony_ci knew.private_value = AK_COMPOSE(0, 8, shift, 0); 8308c2ecf20Sopenharmony_ci break; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci case SND_AK4355: 8338c2ecf20Sopenharmony_ci case SND_AK4358: 8348c2ecf20Sopenharmony_ci knew.private_value = AK_COMPOSE(idx, 3, 0, 0); 8358c2ecf20Sopenharmony_ci break; 8368c2ecf20Sopenharmony_ci case SND_AK4381: 8378c2ecf20Sopenharmony_ci knew.private_value = AK_COMPOSE(idx, 1, 1, 0); 8388c2ecf20Sopenharmony_ci break; 8398c2ecf20Sopenharmony_ci default: 8408c2ecf20Sopenharmony_ci return -EINVAL; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); 8438c2ecf20Sopenharmony_ci if (err < 0) 8448c2ecf20Sopenharmony_ci return err; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci return 0; 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic void proc_regs_read(struct snd_info_entry *entry, 8508c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct snd_akm4xxx *ak = entry->private_data; 8538c2ecf20Sopenharmony_ci int reg, val, chip; 8548c2ecf20Sopenharmony_ci for (chip = 0; chip < ak->num_chips; chip++) { 8558c2ecf20Sopenharmony_ci for (reg = 0; reg < ak->total_regs; reg++) { 8568c2ecf20Sopenharmony_ci val = snd_akm4xxx_get(ak, chip, reg); 8578c2ecf20Sopenharmony_ci snd_iprintf(buffer, "chip %d: 0x%02x = 0x%02x\n", chip, 8588c2ecf20Sopenharmony_ci reg, val); 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int proc_init(struct snd_akm4xxx *ak) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci return snd_card_ro_proc_new(ak->card, ak->name, ak, proc_regs_read); 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ciint snd_akm4xxx_build_controls(struct snd_akm4xxx *ak) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci int err, num_emphs; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci err = build_dac_controls(ak); 8738c2ecf20Sopenharmony_ci if (err < 0) 8748c2ecf20Sopenharmony_ci return err; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci err = build_adc_controls(ak); 8778c2ecf20Sopenharmony_ci if (err < 0) 8788c2ecf20Sopenharmony_ci return err; 8798c2ecf20Sopenharmony_ci if (ak->type == SND_AK4355 || ak->type == SND_AK4358) 8808c2ecf20Sopenharmony_ci num_emphs = 1; 8818c2ecf20Sopenharmony_ci else if (ak->type == SND_AK4620) 8828c2ecf20Sopenharmony_ci num_emphs = 0; 8838c2ecf20Sopenharmony_ci else 8848c2ecf20Sopenharmony_ci num_emphs = ak->num_dacs / 2; 8858c2ecf20Sopenharmony_ci err = build_deemphasis(ak, num_emphs); 8868c2ecf20Sopenharmony_ci if (err < 0) 8878c2ecf20Sopenharmony_ci return err; 8888c2ecf20Sopenharmony_ci err = proc_init(ak); 8898c2ecf20Sopenharmony_ci if (err < 0) 8908c2ecf20Sopenharmony_ci return err; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci return 0; 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_akm4xxx_build_controls); 895