18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA driver for ICEnsemble VT17xx 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Lowlevel functions for WM8766 codec 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <sound/core.h> 128c2ecf20Sopenharmony_ci#include <sound/control.h> 138c2ecf20Sopenharmony_ci#include <sound/tlv.h> 148c2ecf20Sopenharmony_ci#include "wm8766.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* low-level access */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci if (addr < WM8766_REG_COUNT) 218c2ecf20Sopenharmony_ci wm->regs[addr] = data; 228c2ecf20Sopenharmony_ci wm->ops.write(wm, addr, data); 238c2ecf20Sopenharmony_ci} 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* mixer controls */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(wm8766_tlv, -12750, 50, 1); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic const struct snd_wm8766_ctl snd_wm8766_default_ctl[WM8766_CTL_COUNT] = { 308c2ecf20Sopenharmony_ci [WM8766_CTL_CH1_VOL] = { 318c2ecf20Sopenharmony_ci .name = "Channel 1 Playback Volume", 328c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 338c2ecf20Sopenharmony_ci .tlv = wm8766_tlv, 348c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACL1, 358c2ecf20Sopenharmony_ci .reg2 = WM8766_REG_DACR1, 368c2ecf20Sopenharmony_ci .mask1 = WM8766_VOL_MASK, 378c2ecf20Sopenharmony_ci .mask2 = WM8766_VOL_MASK, 388c2ecf20Sopenharmony_ci .max = 0xff, 398c2ecf20Sopenharmony_ci .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE, 408c2ecf20Sopenharmony_ci }, 418c2ecf20Sopenharmony_ci [WM8766_CTL_CH2_VOL] = { 428c2ecf20Sopenharmony_ci .name = "Channel 2 Playback Volume", 438c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 448c2ecf20Sopenharmony_ci .tlv = wm8766_tlv, 458c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACL2, 468c2ecf20Sopenharmony_ci .reg2 = WM8766_REG_DACR2, 478c2ecf20Sopenharmony_ci .mask1 = WM8766_VOL_MASK, 488c2ecf20Sopenharmony_ci .mask2 = WM8766_VOL_MASK, 498c2ecf20Sopenharmony_ci .max = 0xff, 508c2ecf20Sopenharmony_ci .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE, 518c2ecf20Sopenharmony_ci }, 528c2ecf20Sopenharmony_ci [WM8766_CTL_CH3_VOL] = { 538c2ecf20Sopenharmony_ci .name = "Channel 3 Playback Volume", 548c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_INTEGER, 558c2ecf20Sopenharmony_ci .tlv = wm8766_tlv, 568c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACL3, 578c2ecf20Sopenharmony_ci .reg2 = WM8766_REG_DACR3, 588c2ecf20Sopenharmony_ci .mask1 = WM8766_VOL_MASK, 598c2ecf20Sopenharmony_ci .mask2 = WM8766_VOL_MASK, 608c2ecf20Sopenharmony_ci .max = 0xff, 618c2ecf20Sopenharmony_ci .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE, 628c2ecf20Sopenharmony_ci }, 638c2ecf20Sopenharmony_ci [WM8766_CTL_CH1_SW] = { 648c2ecf20Sopenharmony_ci .name = "Channel 1 Playback Switch", 658c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 668c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACCTRL2, 678c2ecf20Sopenharmony_ci .mask1 = WM8766_DAC2_MUTE1, 688c2ecf20Sopenharmony_ci .flags = WM8766_FLAG_INVERT, 698c2ecf20Sopenharmony_ci }, 708c2ecf20Sopenharmony_ci [WM8766_CTL_CH2_SW] = { 718c2ecf20Sopenharmony_ci .name = "Channel 2 Playback Switch", 728c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 738c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACCTRL2, 748c2ecf20Sopenharmony_ci .mask1 = WM8766_DAC2_MUTE2, 758c2ecf20Sopenharmony_ci .flags = WM8766_FLAG_INVERT, 768c2ecf20Sopenharmony_ci }, 778c2ecf20Sopenharmony_ci [WM8766_CTL_CH3_SW] = { 788c2ecf20Sopenharmony_ci .name = "Channel 3 Playback Switch", 798c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 808c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACCTRL2, 818c2ecf20Sopenharmony_ci .mask1 = WM8766_DAC2_MUTE3, 828c2ecf20Sopenharmony_ci .flags = WM8766_FLAG_INVERT, 838c2ecf20Sopenharmony_ci }, 848c2ecf20Sopenharmony_ci [WM8766_CTL_PHASE1_SW] = { 858c2ecf20Sopenharmony_ci .name = "Channel 1 Phase Invert Playback Switch", 868c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 878c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_IFCTRL, 888c2ecf20Sopenharmony_ci .mask1 = WM8766_PHASE_INVERT1, 898c2ecf20Sopenharmony_ci }, 908c2ecf20Sopenharmony_ci [WM8766_CTL_PHASE2_SW] = { 918c2ecf20Sopenharmony_ci .name = "Channel 2 Phase Invert Playback Switch", 928c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 938c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_IFCTRL, 948c2ecf20Sopenharmony_ci .mask1 = WM8766_PHASE_INVERT2, 958c2ecf20Sopenharmony_ci }, 968c2ecf20Sopenharmony_ci [WM8766_CTL_PHASE3_SW] = { 978c2ecf20Sopenharmony_ci .name = "Channel 3 Phase Invert Playback Switch", 988c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 998c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_IFCTRL, 1008c2ecf20Sopenharmony_ci .mask1 = WM8766_PHASE_INVERT3, 1018c2ecf20Sopenharmony_ci }, 1028c2ecf20Sopenharmony_ci [WM8766_CTL_DEEMPH1_SW] = { 1038c2ecf20Sopenharmony_ci .name = "Channel 1 Deemphasis Playback Switch", 1048c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 1058c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACCTRL2, 1068c2ecf20Sopenharmony_ci .mask1 = WM8766_DAC2_DEEMP1, 1078c2ecf20Sopenharmony_ci }, 1088c2ecf20Sopenharmony_ci [WM8766_CTL_DEEMPH2_SW] = { 1098c2ecf20Sopenharmony_ci .name = "Channel 2 Deemphasis Playback Switch", 1108c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 1118c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACCTRL2, 1128c2ecf20Sopenharmony_ci .mask1 = WM8766_DAC2_DEEMP2, 1138c2ecf20Sopenharmony_ci }, 1148c2ecf20Sopenharmony_ci [WM8766_CTL_DEEMPH3_SW] = { 1158c2ecf20Sopenharmony_ci .name = "Channel 3 Deemphasis Playback Switch", 1168c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 1178c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACCTRL2, 1188c2ecf20Sopenharmony_ci .mask1 = WM8766_DAC2_DEEMP3, 1198c2ecf20Sopenharmony_ci }, 1208c2ecf20Sopenharmony_ci [WM8766_CTL_IZD_SW] = { 1218c2ecf20Sopenharmony_ci .name = "Infinite Zero Detect Playback Switch", 1228c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 1238c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACCTRL1, 1248c2ecf20Sopenharmony_ci .mask1 = WM8766_DAC_IZD, 1258c2ecf20Sopenharmony_ci }, 1268c2ecf20Sopenharmony_ci [WM8766_CTL_ZC_SW] = { 1278c2ecf20Sopenharmony_ci .name = "Zero Cross Detect Playback Switch", 1288c2ecf20Sopenharmony_ci .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN, 1298c2ecf20Sopenharmony_ci .reg1 = WM8766_REG_DACCTRL2, 1308c2ecf20Sopenharmony_ci .mask1 = WM8766_DAC2_ZCD, 1318c2ecf20Sopenharmony_ci .flags = WM8766_FLAG_INVERT, 1328c2ecf20Sopenharmony_ci }, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* exported functions */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_civoid snd_wm8766_init(struct snd_wm8766 *wm) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci int i; 1408c2ecf20Sopenharmony_ci static const u16 default_values[] = { 1418c2ecf20Sopenharmony_ci 0x000, 0x100, 1428c2ecf20Sopenharmony_ci 0x120, 0x000, 1438c2ecf20Sopenharmony_ci 0x000, 0x100, 0x000, 0x100, 0x000, 1448c2ecf20Sopenharmony_ci 0x000, 0x080, 1458c2ecf20Sopenharmony_ci }; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci memcpy(wm->ctl, snd_wm8766_default_ctl, sizeof(wm->ctl)); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci snd_wm8766_write(wm, WM8766_REG_RESET, 0x00); /* reset */ 1508c2ecf20Sopenharmony_ci udelay(10); 1518c2ecf20Sopenharmony_ci /* load defaults */ 1528c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(default_values); i++) 1538c2ecf20Sopenharmony_ci snd_wm8766_write(wm, i, default_values[i]); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_civoid snd_wm8766_resume(struct snd_wm8766 *wm) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci int i; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci for (i = 0; i < WM8766_REG_COUNT; i++) 1618c2ecf20Sopenharmony_ci snd_wm8766_write(wm, i, wm->regs[i]); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_civoid snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci u16 val = wm->regs[WM8766_REG_IFCTRL] & ~WM8766_IF_MASK; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci dac &= WM8766_IF_MASK; 1698c2ecf20Sopenharmony_ci snd_wm8766_write(wm, WM8766_REG_IFCTRL, val | dac); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_civoid snd_wm8766_volume_restore(struct snd_wm8766 *wm) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u16 val = wm->regs[WM8766_REG_DACR1]; 1758c2ecf20Sopenharmony_ci /* restore volume after MCLK stopped */ 1768c2ecf20Sopenharmony_ci snd_wm8766_write(wm, WM8766_REG_DACR1, val | WM8766_VOL_UPDATE); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* mixer callbacks */ 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int snd_wm8766_volume_info(struct snd_kcontrol *kcontrol, 1828c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol); 1858c2ecf20Sopenharmony_ci int n = kcontrol->private_value; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 1888c2ecf20Sopenharmony_ci uinfo->count = (wm->ctl[n].flags & WM8766_FLAG_STEREO) ? 2 : 1; 1898c2ecf20Sopenharmony_ci uinfo->value.integer.min = wm->ctl[n].min; 1908c2ecf20Sopenharmony_ci uinfo->value.integer.max = wm->ctl[n].max; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int snd_wm8766_enum_info(struct snd_kcontrol *kcontrol, 1968c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol); 1998c2ecf20Sopenharmony_ci int n = kcontrol->private_value; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max, 2028c2ecf20Sopenharmony_ci wm->ctl[n].enum_names); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol, 2068c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol); 2098c2ecf20Sopenharmony_ci int n = kcontrol->private_value; 2108c2ecf20Sopenharmony_ci u16 val1, val2; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (wm->ctl[n].get) 2138c2ecf20Sopenharmony_ci wm->ctl[n].get(wm, &val1, &val2); 2148c2ecf20Sopenharmony_ci else { 2158c2ecf20Sopenharmony_ci val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1; 2168c2ecf20Sopenharmony_ci val1 >>= __ffs(wm->ctl[n].mask1); 2178c2ecf20Sopenharmony_ci if (wm->ctl[n].flags & WM8766_FLAG_STEREO) { 2188c2ecf20Sopenharmony_ci val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2; 2198c2ecf20Sopenharmony_ci val2 >>= __ffs(wm->ctl[n].mask2); 2208c2ecf20Sopenharmony_ci if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE) 2218c2ecf20Sopenharmony_ci val2 &= ~WM8766_VOL_UPDATE; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci if (wm->ctl[n].flags & WM8766_FLAG_INVERT) { 2258c2ecf20Sopenharmony_ci val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min); 2268c2ecf20Sopenharmony_ci if (wm->ctl[n].flags & WM8766_FLAG_STEREO) 2278c2ecf20Sopenharmony_ci val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = val1; 2308c2ecf20Sopenharmony_ci if (wm->ctl[n].flags & WM8766_FLAG_STEREO) 2318c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = val2; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int snd_wm8766_ctl_put(struct snd_kcontrol *kcontrol, 2378c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol); 2408c2ecf20Sopenharmony_ci int n = kcontrol->private_value; 2418c2ecf20Sopenharmony_ci u16 val, regval1, regval2; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* this also works for enum because value is a union */ 2448c2ecf20Sopenharmony_ci regval1 = ucontrol->value.integer.value[0]; 2458c2ecf20Sopenharmony_ci regval2 = ucontrol->value.integer.value[1]; 2468c2ecf20Sopenharmony_ci if (wm->ctl[n].flags & WM8766_FLAG_INVERT) { 2478c2ecf20Sopenharmony_ci regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min); 2488c2ecf20Sopenharmony_ci regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci if (wm->ctl[n].set) 2518c2ecf20Sopenharmony_ci wm->ctl[n].set(wm, regval1, regval2); 2528c2ecf20Sopenharmony_ci else { 2538c2ecf20Sopenharmony_ci val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1; 2548c2ecf20Sopenharmony_ci val |= regval1 << __ffs(wm->ctl[n].mask1); 2558c2ecf20Sopenharmony_ci /* both stereo controls in one register */ 2568c2ecf20Sopenharmony_ci if (wm->ctl[n].flags & WM8766_FLAG_STEREO && 2578c2ecf20Sopenharmony_ci wm->ctl[n].reg1 == wm->ctl[n].reg2) { 2588c2ecf20Sopenharmony_ci val &= ~wm->ctl[n].mask2; 2598c2ecf20Sopenharmony_ci val |= regval2 << __ffs(wm->ctl[n].mask2); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci snd_wm8766_write(wm, wm->ctl[n].reg1, val); 2628c2ecf20Sopenharmony_ci /* stereo controls in different registers */ 2638c2ecf20Sopenharmony_ci if (wm->ctl[n].flags & WM8766_FLAG_STEREO && 2648c2ecf20Sopenharmony_ci wm->ctl[n].reg1 != wm->ctl[n].reg2) { 2658c2ecf20Sopenharmony_ci val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2; 2668c2ecf20Sopenharmony_ci val |= regval2 << __ffs(wm->ctl[n].mask2); 2678c2ecf20Sopenharmony_ci if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE) 2688c2ecf20Sopenharmony_ci val |= WM8766_VOL_UPDATE; 2698c2ecf20Sopenharmony_ci snd_wm8766_write(wm, wm->ctl[n].reg2, val); 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int snd_wm8766_add_control(struct snd_wm8766 *wm, int num) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct snd_kcontrol_new cont; 2798c2ecf20Sopenharmony_ci struct snd_kcontrol *ctl; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci memset(&cont, 0, sizeof(cont)); 2828c2ecf20Sopenharmony_ci cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 2838c2ecf20Sopenharmony_ci cont.private_value = num; 2848c2ecf20Sopenharmony_ci cont.name = wm->ctl[num].name; 2858c2ecf20Sopenharmony_ci cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 2868c2ecf20Sopenharmony_ci if (wm->ctl[num].flags & WM8766_FLAG_LIM || 2878c2ecf20Sopenharmony_ci wm->ctl[num].flags & WM8766_FLAG_ALC) 2888c2ecf20Sopenharmony_ci cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; 2898c2ecf20Sopenharmony_ci cont.tlv.p = NULL; 2908c2ecf20Sopenharmony_ci cont.get = snd_wm8766_ctl_get; 2918c2ecf20Sopenharmony_ci cont.put = snd_wm8766_ctl_put; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci switch (wm->ctl[num].type) { 2948c2ecf20Sopenharmony_ci case SNDRV_CTL_ELEM_TYPE_INTEGER: 2958c2ecf20Sopenharmony_ci cont.info = snd_wm8766_volume_info; 2968c2ecf20Sopenharmony_ci cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; 2978c2ecf20Sopenharmony_ci cont.tlv.p = wm->ctl[num].tlv; 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci case SNDRV_CTL_ELEM_TYPE_BOOLEAN: 3008c2ecf20Sopenharmony_ci wm->ctl[num].max = 1; 3018c2ecf20Sopenharmony_ci if (wm->ctl[num].flags & WM8766_FLAG_STEREO) 3028c2ecf20Sopenharmony_ci cont.info = snd_ctl_boolean_stereo_info; 3038c2ecf20Sopenharmony_ci else 3048c2ecf20Sopenharmony_ci cont.info = snd_ctl_boolean_mono_info; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci case SNDRV_CTL_ELEM_TYPE_ENUMERATED: 3078c2ecf20Sopenharmony_ci cont.info = snd_wm8766_enum_info; 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci default: 3108c2ecf20Sopenharmony_ci return -EINVAL; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci ctl = snd_ctl_new1(&cont, wm); 3138c2ecf20Sopenharmony_ci if (!ctl) 3148c2ecf20Sopenharmony_ci return -ENOMEM; 3158c2ecf20Sopenharmony_ci wm->ctl[num].kctl = ctl; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return snd_ctl_add(wm->card, ctl); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ciint snd_wm8766_build_controls(struct snd_wm8766 *wm) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci int err, i; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci for (i = 0; i < WM8766_CTL_COUNT; i++) 3258c2ecf20Sopenharmony_ci if (wm->ctl[i].name) { 3268c2ecf20Sopenharmony_ci err = snd_wm8766_add_control(wm, i); 3278c2ecf20Sopenharmony_ci if (err < 0) 3288c2ecf20Sopenharmony_ci return err; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci} 333