^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * ALSA driver for ICEnsemble VT17xx
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Lowlevel functions for WM8766 codec
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <sound/core.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <sound/control.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <sound/tlv.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include "wm8766.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) /* low-level access */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) static void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) if (addr < WM8766_REG_COUNT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) wm->regs[addr] = data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) wm->ops.write(wm, addr, data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) /* mixer controls */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) static const DECLARE_TLV_DB_SCALE(wm8766_tlv, -12750, 50, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static const struct snd_wm8766_ctl snd_wm8766_default_ctl[WM8766_CTL_COUNT] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) [WM8766_CTL_CH1_VOL] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) .name = "Channel 1 Playback Volume",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) .tlv = wm8766_tlv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) .reg1 = WM8766_REG_DACL1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) .reg2 = WM8766_REG_DACR1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) .mask1 = WM8766_VOL_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) .mask2 = WM8766_VOL_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) .max = 0xff,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) [WM8766_CTL_CH2_VOL] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) .name = "Channel 2 Playback Volume",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) .tlv = wm8766_tlv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) .reg1 = WM8766_REG_DACL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) .reg2 = WM8766_REG_DACR2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) .mask1 = WM8766_VOL_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) .mask2 = WM8766_VOL_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) .max = 0xff,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) [WM8766_CTL_CH3_VOL] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) .name = "Channel 3 Playback Volume",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) .tlv = wm8766_tlv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) .reg1 = WM8766_REG_DACL3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) .reg2 = WM8766_REG_DACR3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) .mask1 = WM8766_VOL_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) .mask2 = WM8766_VOL_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) .max = 0xff,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) [WM8766_CTL_CH1_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) .name = "Channel 1 Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) .reg1 = WM8766_REG_DACCTRL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) .mask1 = WM8766_DAC2_MUTE1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) .flags = WM8766_FLAG_INVERT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) [WM8766_CTL_CH2_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) .name = "Channel 2 Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) .reg1 = WM8766_REG_DACCTRL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) .mask1 = WM8766_DAC2_MUTE2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) .flags = WM8766_FLAG_INVERT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) [WM8766_CTL_CH3_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) .name = "Channel 3 Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) .reg1 = WM8766_REG_DACCTRL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) .mask1 = WM8766_DAC2_MUTE3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) .flags = WM8766_FLAG_INVERT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) [WM8766_CTL_PHASE1_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) .name = "Channel 1 Phase Invert Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) .reg1 = WM8766_REG_IFCTRL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) .mask1 = WM8766_PHASE_INVERT1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) [WM8766_CTL_PHASE2_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) .name = "Channel 2 Phase Invert Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) .reg1 = WM8766_REG_IFCTRL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) .mask1 = WM8766_PHASE_INVERT2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) [WM8766_CTL_PHASE3_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) .name = "Channel 3 Phase Invert Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) .reg1 = WM8766_REG_IFCTRL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) .mask1 = WM8766_PHASE_INVERT3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) [WM8766_CTL_DEEMPH1_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) .name = "Channel 1 Deemphasis Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) .reg1 = WM8766_REG_DACCTRL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) .mask1 = WM8766_DAC2_DEEMP1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) [WM8766_CTL_DEEMPH2_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) .name = "Channel 2 Deemphasis Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) .reg1 = WM8766_REG_DACCTRL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) .mask1 = WM8766_DAC2_DEEMP2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) [WM8766_CTL_DEEMPH3_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) .name = "Channel 3 Deemphasis Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) .reg1 = WM8766_REG_DACCTRL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) .mask1 = WM8766_DAC2_DEEMP3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) [WM8766_CTL_IZD_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) .name = "Infinite Zero Detect Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) .reg1 = WM8766_REG_DACCTRL1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) .mask1 = WM8766_DAC_IZD,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) [WM8766_CTL_ZC_SW] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) .name = "Zero Cross Detect Playback Switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) .reg1 = WM8766_REG_DACCTRL2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) .mask1 = WM8766_DAC2_ZCD,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) .flags = WM8766_FLAG_INVERT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) /* exported functions */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) void snd_wm8766_init(struct snd_wm8766 *wm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) static const u16 default_values[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) 0x000, 0x100,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 0x120, 0x000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 0x000, 0x100, 0x000, 0x100, 0x000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) 0x000, 0x080,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) memcpy(wm->ctl, snd_wm8766_default_ctl, sizeof(wm->ctl));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) snd_wm8766_write(wm, WM8766_REG_RESET, 0x00); /* reset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) udelay(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) /* load defaults */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) for (i = 0; i < ARRAY_SIZE(default_values); i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) snd_wm8766_write(wm, i, default_values[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) void snd_wm8766_resume(struct snd_wm8766 *wm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) for (i = 0; i < WM8766_REG_COUNT; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) snd_wm8766_write(wm, i, wm->regs[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) u16 val = wm->regs[WM8766_REG_IFCTRL] & ~WM8766_IF_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) dac &= WM8766_IF_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) snd_wm8766_write(wm, WM8766_REG_IFCTRL, val | dac);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) void snd_wm8766_volume_restore(struct snd_wm8766 *wm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) u16 val = wm->regs[WM8766_REG_DACR1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) /* restore volume after MCLK stopped */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) snd_wm8766_write(wm, WM8766_REG_DACR1, val | WM8766_VOL_UPDATE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) /* mixer callbacks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) static int snd_wm8766_volume_info(struct snd_kcontrol *kcontrol,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) struct snd_ctl_elem_info *uinfo)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) int n = kcontrol->private_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) uinfo->count = (wm->ctl[n].flags & WM8766_FLAG_STEREO) ? 2 : 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) uinfo->value.integer.min = wm->ctl[n].min;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) uinfo->value.integer.max = wm->ctl[n].max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) static int snd_wm8766_enum_info(struct snd_kcontrol *kcontrol,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) struct snd_ctl_elem_info *uinfo)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) int n = kcontrol->private_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) wm->ctl[n].enum_names);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) static int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) struct snd_ctl_elem_value *ucontrol)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) int n = kcontrol->private_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) u16 val1, val2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) if (wm->ctl[n].get)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) wm->ctl[n].get(wm, &val1, &val2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) val1 >>= __ffs(wm->ctl[n].mask1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) if (wm->ctl[n].flags & WM8766_FLAG_STEREO) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) val2 >>= __ffs(wm->ctl[n].mask2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) val2 &= ~WM8766_VOL_UPDATE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) ucontrol->value.integer.value[0] = val1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) ucontrol->value.integer.value[1] = val2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) static int snd_wm8766_ctl_put(struct snd_kcontrol *kcontrol,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) struct snd_ctl_elem_value *ucontrol)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) int n = kcontrol->private_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) u16 val, regval1, regval2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) /* this also works for enum because value is a union */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) regval1 = ucontrol->value.integer.value[0];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) regval2 = ucontrol->value.integer.value[1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) if (wm->ctl[n].set)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) wm->ctl[n].set(wm, regval1, regval2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) val |= regval1 << __ffs(wm->ctl[n].mask1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) /* both stereo controls in one register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) if (wm->ctl[n].flags & WM8766_FLAG_STEREO &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) wm->ctl[n].reg1 == wm->ctl[n].reg2) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) val &= ~wm->ctl[n].mask2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) val |= regval2 << __ffs(wm->ctl[n].mask2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) snd_wm8766_write(wm, wm->ctl[n].reg1, val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) /* stereo controls in different registers */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) if (wm->ctl[n].flags & WM8766_FLAG_STEREO &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) wm->ctl[n].reg1 != wm->ctl[n].reg2) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) val |= regval2 << __ffs(wm->ctl[n].mask2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) val |= WM8766_VOL_UPDATE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) snd_wm8766_write(wm, wm->ctl[n].reg2, val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) static int snd_wm8766_add_control(struct snd_wm8766 *wm, int num)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) struct snd_kcontrol_new cont;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) struct snd_kcontrol *ctl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) memset(&cont, 0, sizeof(cont));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) cont.private_value = num;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) cont.name = wm->ctl[num].name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) if (wm->ctl[num].flags & WM8766_FLAG_LIM ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) wm->ctl[num].flags & WM8766_FLAG_ALC)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) cont.tlv.p = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) cont.get = snd_wm8766_ctl_get;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) cont.put = snd_wm8766_ctl_put;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) switch (wm->ctl[num].type) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) case SNDRV_CTL_ELEM_TYPE_INTEGER:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) cont.info = snd_wm8766_volume_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) cont.tlv.p = wm->ctl[num].tlv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) wm->ctl[num].max = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) if (wm->ctl[num].flags & WM8766_FLAG_STEREO)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) cont.info = snd_ctl_boolean_stereo_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) cont.info = snd_ctl_boolean_mono_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) cont.info = snd_wm8766_enum_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) ctl = snd_ctl_new1(&cont, wm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) if (!ctl)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) wm->ctl[num].kctl = ctl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) return snd_ctl_add(wm->card, ctl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) int snd_wm8766_build_controls(struct snd_wm8766 *wm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) int err, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) for (i = 0; i < WM8766_CTL_COUNT; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) if (wm->ctl[i].name) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) err = snd_wm8766_add_control(wm, i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) if (err < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) }