18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * @File	ctmixer.c
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * @Brief
88c2ecf20Sopenharmony_ci * This file contains the implementation of alsa mixer device functions.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * @Author	Liu Chun
118c2ecf20Sopenharmony_ci * @Date 	May 28 2008
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "ctmixer.h"
168c2ecf20Sopenharmony_ci#include "ctamixer.h"
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <sound/core.h>
198c2ecf20Sopenharmony_ci#include <sound/control.h>
208c2ecf20Sopenharmony_ci#include <sound/asoundef.h>
218c2ecf20Sopenharmony_ci#include <sound/pcm.h>
228c2ecf20Sopenharmony_ci#include <sound/tlv.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cienum CT_SUM_CTL {
258c2ecf20Sopenharmony_ci	SUM_IN_F,
268c2ecf20Sopenharmony_ci	SUM_IN_R,
278c2ecf20Sopenharmony_ci	SUM_IN_C,
288c2ecf20Sopenharmony_ci	SUM_IN_S,
298c2ecf20Sopenharmony_ci	SUM_IN_F_C,
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	NUM_CT_SUMS
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cienum CT_AMIXER_CTL {
358c2ecf20Sopenharmony_ci	/* volume control mixers */
368c2ecf20Sopenharmony_ci	AMIXER_MASTER_F,
378c2ecf20Sopenharmony_ci	AMIXER_MASTER_R,
388c2ecf20Sopenharmony_ci	AMIXER_MASTER_C,
398c2ecf20Sopenharmony_ci	AMIXER_MASTER_S,
408c2ecf20Sopenharmony_ci	AMIXER_PCM_F,
418c2ecf20Sopenharmony_ci	AMIXER_PCM_R,
428c2ecf20Sopenharmony_ci	AMIXER_PCM_C,
438c2ecf20Sopenharmony_ci	AMIXER_PCM_S,
448c2ecf20Sopenharmony_ci	AMIXER_SPDIFI,
458c2ecf20Sopenharmony_ci	AMIXER_LINEIN,
468c2ecf20Sopenharmony_ci	AMIXER_MIC,
478c2ecf20Sopenharmony_ci	AMIXER_SPDIFO,
488c2ecf20Sopenharmony_ci	AMIXER_WAVE_F,
498c2ecf20Sopenharmony_ci	AMIXER_WAVE_R,
508c2ecf20Sopenharmony_ci	AMIXER_WAVE_C,
518c2ecf20Sopenharmony_ci	AMIXER_WAVE_S,
528c2ecf20Sopenharmony_ci	AMIXER_MASTER_F_C,
538c2ecf20Sopenharmony_ci	AMIXER_PCM_F_C,
548c2ecf20Sopenharmony_ci	AMIXER_SPDIFI_C,
558c2ecf20Sopenharmony_ci	AMIXER_LINEIN_C,
568c2ecf20Sopenharmony_ci	AMIXER_MIC_C,
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* this should always be the last one */
598c2ecf20Sopenharmony_ci	NUM_CT_AMIXERS
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cienum CTALSA_MIXER_CTL {
638c2ecf20Sopenharmony_ci	/* volume control mixers */
648c2ecf20Sopenharmony_ci	MIXER_MASTER_P,
658c2ecf20Sopenharmony_ci	MIXER_PCM_P,
668c2ecf20Sopenharmony_ci	MIXER_LINEIN_P,
678c2ecf20Sopenharmony_ci	MIXER_MIC_P,
688c2ecf20Sopenharmony_ci	MIXER_SPDIFI_P,
698c2ecf20Sopenharmony_ci	MIXER_SPDIFO_P,
708c2ecf20Sopenharmony_ci	MIXER_WAVEF_P,
718c2ecf20Sopenharmony_ci	MIXER_WAVER_P,
728c2ecf20Sopenharmony_ci	MIXER_WAVEC_P,
738c2ecf20Sopenharmony_ci	MIXER_WAVES_P,
748c2ecf20Sopenharmony_ci	MIXER_MASTER_C,
758c2ecf20Sopenharmony_ci	MIXER_PCM_C,
768c2ecf20Sopenharmony_ci	MIXER_LINEIN_C,
778c2ecf20Sopenharmony_ci	MIXER_MIC_C,
788c2ecf20Sopenharmony_ci	MIXER_SPDIFI_C,
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* switch control mixers */
818c2ecf20Sopenharmony_ci	MIXER_PCM_C_S,
828c2ecf20Sopenharmony_ci	MIXER_LINEIN_C_S,
838c2ecf20Sopenharmony_ci	MIXER_MIC_C_S,
848c2ecf20Sopenharmony_ci	MIXER_SPDIFI_C_S,
858c2ecf20Sopenharmony_ci	MIXER_SPDIFO_P_S,
868c2ecf20Sopenharmony_ci	MIXER_WAVEF_P_S,
878c2ecf20Sopenharmony_ci	MIXER_WAVER_P_S,
888c2ecf20Sopenharmony_ci	MIXER_WAVEC_P_S,
898c2ecf20Sopenharmony_ci	MIXER_WAVES_P_S,
908c2ecf20Sopenharmony_ci	MIXER_DIGITAL_IO_S,
918c2ecf20Sopenharmony_ci	MIXER_IEC958_MASK,
928c2ecf20Sopenharmony_ci	MIXER_IEC958_DEFAULT,
938c2ecf20Sopenharmony_ci	MIXER_IEC958_STREAM,
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* this should always be the last one */
968c2ecf20Sopenharmony_ci	NUM_CTALSA_MIXERS
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#define VOL_MIXER_START		MIXER_MASTER_P
1008c2ecf20Sopenharmony_ci#define VOL_MIXER_END		MIXER_SPDIFI_C
1018c2ecf20Sopenharmony_ci#define VOL_MIXER_NUM		(VOL_MIXER_END - VOL_MIXER_START + 1)
1028c2ecf20Sopenharmony_ci#define SWH_MIXER_START		MIXER_PCM_C_S
1038c2ecf20Sopenharmony_ci#define SWH_MIXER_END		MIXER_DIGITAL_IO_S
1048c2ecf20Sopenharmony_ci#define SWH_CAPTURE_START	MIXER_PCM_C_S
1058c2ecf20Sopenharmony_ci#define SWH_CAPTURE_END		MIXER_SPDIFI_C_S
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define CHN_NUM		2
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistruct ct_kcontrol_init {
1108c2ecf20Sopenharmony_ci	unsigned char ctl;
1118c2ecf20Sopenharmony_ci	char *name;
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic struct ct_kcontrol_init
1158c2ecf20Sopenharmony_cict_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
1168c2ecf20Sopenharmony_ci	[MIXER_MASTER_P] = {
1178c2ecf20Sopenharmony_ci		.ctl = 1,
1188c2ecf20Sopenharmony_ci		.name = "Master Playback Volume",
1198c2ecf20Sopenharmony_ci	},
1208c2ecf20Sopenharmony_ci	[MIXER_MASTER_C] = {
1218c2ecf20Sopenharmony_ci		.ctl = 1,
1228c2ecf20Sopenharmony_ci		.name = "Master Capture Volume",
1238c2ecf20Sopenharmony_ci	},
1248c2ecf20Sopenharmony_ci	[MIXER_PCM_P] = {
1258c2ecf20Sopenharmony_ci		.ctl = 1,
1268c2ecf20Sopenharmony_ci		.name = "PCM Playback Volume",
1278c2ecf20Sopenharmony_ci	},
1288c2ecf20Sopenharmony_ci	[MIXER_PCM_C] = {
1298c2ecf20Sopenharmony_ci		.ctl = 1,
1308c2ecf20Sopenharmony_ci		.name = "PCM Capture Volume",
1318c2ecf20Sopenharmony_ci	},
1328c2ecf20Sopenharmony_ci	[MIXER_LINEIN_P] = {
1338c2ecf20Sopenharmony_ci		.ctl = 1,
1348c2ecf20Sopenharmony_ci		.name = "Line Playback Volume",
1358c2ecf20Sopenharmony_ci	},
1368c2ecf20Sopenharmony_ci	[MIXER_LINEIN_C] = {
1378c2ecf20Sopenharmony_ci		.ctl = 1,
1388c2ecf20Sopenharmony_ci		.name = "Line Capture Volume",
1398c2ecf20Sopenharmony_ci	},
1408c2ecf20Sopenharmony_ci	[MIXER_MIC_P] = {
1418c2ecf20Sopenharmony_ci		.ctl = 1,
1428c2ecf20Sopenharmony_ci		.name = "Mic Playback Volume",
1438c2ecf20Sopenharmony_ci	},
1448c2ecf20Sopenharmony_ci	[MIXER_MIC_C] = {
1458c2ecf20Sopenharmony_ci		.ctl = 1,
1468c2ecf20Sopenharmony_ci		.name = "Mic Capture Volume",
1478c2ecf20Sopenharmony_ci	},
1488c2ecf20Sopenharmony_ci	[MIXER_SPDIFI_P] = {
1498c2ecf20Sopenharmony_ci		.ctl = 1,
1508c2ecf20Sopenharmony_ci		.name = "IEC958 Playback Volume",
1518c2ecf20Sopenharmony_ci	},
1528c2ecf20Sopenharmony_ci	[MIXER_SPDIFI_C] = {
1538c2ecf20Sopenharmony_ci		.ctl = 1,
1548c2ecf20Sopenharmony_ci		.name = "IEC958 Capture Volume",
1558c2ecf20Sopenharmony_ci	},
1568c2ecf20Sopenharmony_ci	[MIXER_SPDIFO_P] = {
1578c2ecf20Sopenharmony_ci		.ctl = 1,
1588c2ecf20Sopenharmony_ci		.name = "Digital Playback Volume",
1598c2ecf20Sopenharmony_ci	},
1608c2ecf20Sopenharmony_ci	[MIXER_WAVEF_P] = {
1618c2ecf20Sopenharmony_ci		.ctl = 1,
1628c2ecf20Sopenharmony_ci		.name = "Front Playback Volume",
1638c2ecf20Sopenharmony_ci	},
1648c2ecf20Sopenharmony_ci	[MIXER_WAVES_P] = {
1658c2ecf20Sopenharmony_ci		.ctl = 1,
1668c2ecf20Sopenharmony_ci		.name = "Side Playback Volume",
1678c2ecf20Sopenharmony_ci	},
1688c2ecf20Sopenharmony_ci	[MIXER_WAVEC_P] = {
1698c2ecf20Sopenharmony_ci		.ctl = 1,
1708c2ecf20Sopenharmony_ci		.name = "Center/LFE Playback Volume",
1718c2ecf20Sopenharmony_ci	},
1728c2ecf20Sopenharmony_ci	[MIXER_WAVER_P] = {
1738c2ecf20Sopenharmony_ci		.ctl = 1,
1748c2ecf20Sopenharmony_ci		.name = "Surround Playback Volume",
1758c2ecf20Sopenharmony_ci	},
1768c2ecf20Sopenharmony_ci	[MIXER_PCM_C_S] = {
1778c2ecf20Sopenharmony_ci		.ctl = 1,
1788c2ecf20Sopenharmony_ci		.name = "PCM Capture Switch",
1798c2ecf20Sopenharmony_ci	},
1808c2ecf20Sopenharmony_ci	[MIXER_LINEIN_C_S] = {
1818c2ecf20Sopenharmony_ci		.ctl = 1,
1828c2ecf20Sopenharmony_ci		.name = "Line Capture Switch",
1838c2ecf20Sopenharmony_ci	},
1848c2ecf20Sopenharmony_ci	[MIXER_MIC_C_S] = {
1858c2ecf20Sopenharmony_ci		.ctl = 1,
1868c2ecf20Sopenharmony_ci		.name = "Mic Capture Switch",
1878c2ecf20Sopenharmony_ci	},
1888c2ecf20Sopenharmony_ci	[MIXER_SPDIFI_C_S] = {
1898c2ecf20Sopenharmony_ci		.ctl = 1,
1908c2ecf20Sopenharmony_ci		.name = "IEC958 Capture Switch",
1918c2ecf20Sopenharmony_ci	},
1928c2ecf20Sopenharmony_ci	[MIXER_SPDIFO_P_S] = {
1938c2ecf20Sopenharmony_ci		.ctl = 1,
1948c2ecf20Sopenharmony_ci		.name = "Digital Playback Switch",
1958c2ecf20Sopenharmony_ci	},
1968c2ecf20Sopenharmony_ci	[MIXER_WAVEF_P_S] = {
1978c2ecf20Sopenharmony_ci		.ctl = 1,
1988c2ecf20Sopenharmony_ci		.name = "Front Playback Switch",
1998c2ecf20Sopenharmony_ci	},
2008c2ecf20Sopenharmony_ci	[MIXER_WAVES_P_S] = {
2018c2ecf20Sopenharmony_ci		.ctl = 1,
2028c2ecf20Sopenharmony_ci		.name = "Side Playback Switch",
2038c2ecf20Sopenharmony_ci	},
2048c2ecf20Sopenharmony_ci	[MIXER_WAVEC_P_S] = {
2058c2ecf20Sopenharmony_ci		.ctl = 1,
2068c2ecf20Sopenharmony_ci		.name = "Center/LFE Playback Switch",
2078c2ecf20Sopenharmony_ci	},
2088c2ecf20Sopenharmony_ci	[MIXER_WAVER_P_S] = {
2098c2ecf20Sopenharmony_ci		.ctl = 1,
2108c2ecf20Sopenharmony_ci		.name = "Surround Playback Switch",
2118c2ecf20Sopenharmony_ci	},
2128c2ecf20Sopenharmony_ci	[MIXER_DIGITAL_IO_S] = {
2138c2ecf20Sopenharmony_ci		.ctl = 0,
2148c2ecf20Sopenharmony_ci		.name = "Digit-IO Playback Switch",
2158c2ecf20Sopenharmony_ci	},
2168c2ecf20Sopenharmony_ci};
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic void
2198c2ecf20Sopenharmony_cict_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic void
2228c2ecf20Sopenharmony_cict_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/* FIXME: this static looks like it would fail if more than one card was */
2258c2ecf20Sopenharmony_ci/* installed. */
2268c2ecf20Sopenharmony_cistatic struct snd_kcontrol *kctls[2] = {NULL};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	switch (alsa_index) {
2318c2ecf20Sopenharmony_ci	case MIXER_MASTER_P:	return AMIXER_MASTER_F;
2328c2ecf20Sopenharmony_ci	case MIXER_MASTER_C:	return AMIXER_MASTER_F_C;
2338c2ecf20Sopenharmony_ci	case MIXER_PCM_P:	return AMIXER_PCM_F;
2348c2ecf20Sopenharmony_ci	case MIXER_PCM_C:
2358c2ecf20Sopenharmony_ci	case MIXER_PCM_C_S:	return AMIXER_PCM_F_C;
2368c2ecf20Sopenharmony_ci	case MIXER_LINEIN_P:	return AMIXER_LINEIN;
2378c2ecf20Sopenharmony_ci	case MIXER_LINEIN_C:
2388c2ecf20Sopenharmony_ci	case MIXER_LINEIN_C_S:	return AMIXER_LINEIN_C;
2398c2ecf20Sopenharmony_ci	case MIXER_MIC_P:	return AMIXER_MIC;
2408c2ecf20Sopenharmony_ci	case MIXER_MIC_C:
2418c2ecf20Sopenharmony_ci	case MIXER_MIC_C_S:	return AMIXER_MIC_C;
2428c2ecf20Sopenharmony_ci	case MIXER_SPDIFI_P:	return AMIXER_SPDIFI;
2438c2ecf20Sopenharmony_ci	case MIXER_SPDIFI_C:
2448c2ecf20Sopenharmony_ci	case MIXER_SPDIFI_C_S:	return AMIXER_SPDIFI_C;
2458c2ecf20Sopenharmony_ci	case MIXER_SPDIFO_P:	return AMIXER_SPDIFO;
2468c2ecf20Sopenharmony_ci	case MIXER_WAVEF_P:	return AMIXER_WAVE_F;
2478c2ecf20Sopenharmony_ci	case MIXER_WAVES_P:	return AMIXER_WAVE_S;
2488c2ecf20Sopenharmony_ci	case MIXER_WAVEC_P:	return AMIXER_WAVE_C;
2498c2ecf20Sopenharmony_ci	case MIXER_WAVER_P:	return AMIXER_WAVE_R;
2508c2ecf20Sopenharmony_ci	default:		return NUM_CT_AMIXERS;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic enum CT_AMIXER_CTL get_recording_amixer(enum CT_AMIXER_CTL index)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	switch (index) {
2578c2ecf20Sopenharmony_ci	case AMIXER_MASTER_F:	return AMIXER_MASTER_F_C;
2588c2ecf20Sopenharmony_ci	case AMIXER_PCM_F:	return AMIXER_PCM_F_C;
2598c2ecf20Sopenharmony_ci	case AMIXER_SPDIFI:	return AMIXER_SPDIFI_C;
2608c2ecf20Sopenharmony_ci	case AMIXER_LINEIN:	return AMIXER_LINEIN_C;
2618c2ecf20Sopenharmony_ci	case AMIXER_MIC:	return AMIXER_MIC_C;
2628c2ecf20Sopenharmony_ci	default:		return NUM_CT_AMIXERS;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic unsigned char
2678c2ecf20Sopenharmony_ciget_switch_state(struct ct_mixer *mixer, enum CTALSA_MIXER_CTL type)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	return (mixer->switch_state & (0x1 << (type - SWH_MIXER_START)))
2708c2ecf20Sopenharmony_ci		? 1 : 0;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void
2748c2ecf20Sopenharmony_ciset_switch_state(struct ct_mixer *mixer,
2758c2ecf20Sopenharmony_ci		 enum CTALSA_MIXER_CTL type, unsigned char state)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	if (state)
2788c2ecf20Sopenharmony_ci		mixer->switch_state |= (0x1 << (type - SWH_MIXER_START));
2798c2ecf20Sopenharmony_ci	else
2808c2ecf20Sopenharmony_ci		mixer->switch_state &= ~(0x1 << (type - SWH_MIXER_START));
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci#if 0 /* not used */
2848c2ecf20Sopenharmony_ci/* Map integer value ranging from 0 to 65535 to 14-bit float value ranging
2858c2ecf20Sopenharmony_ci * from 2^-6 to (1+1023/1024) */
2868c2ecf20Sopenharmony_cistatic unsigned int uint16_to_float14(unsigned int x)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	unsigned int i;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (x < 17)
2918c2ecf20Sopenharmony_ci		return 0;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	x *= 2031;
2948c2ecf20Sopenharmony_ci	x /= 65535;
2958c2ecf20Sopenharmony_ci	x += 16;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* i <= 6 */
2988c2ecf20Sopenharmony_ci	for (i = 0; !(x & 0x400); i++)
2998c2ecf20Sopenharmony_ci		x <<= 1;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	x = (((7 - i) & 0x7) << 10) | (x & 0x3ff);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return x;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic unsigned int float14_to_uint16(unsigned int x)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	unsigned int e;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (!x)
3118c2ecf20Sopenharmony_ci		return x;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	e = (x >> 10) & 0x7;
3148c2ecf20Sopenharmony_ci	x &= 0x3ff;
3158c2ecf20Sopenharmony_ci	x += 1024;
3168c2ecf20Sopenharmony_ci	x >>= (7 - e);
3178c2ecf20Sopenharmony_ci	x -= 16;
3188c2ecf20Sopenharmony_ci	x *= 65535;
3198c2ecf20Sopenharmony_ci	x /= 2031;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return x;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci#endif /* not used */
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci#define VOL_SCALE	0x1c
3268c2ecf20Sopenharmony_ci#define VOL_MAX		0x100
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(ct_vol_db_scale, -6400, 25, 1);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol,
3318c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_info *uinfo)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3348c2ecf20Sopenharmony_ci	uinfo->count = 2;
3358c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
3368c2ecf20Sopenharmony_ci	uinfo->value.integer.max = VOL_MAX;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int ct_alsa_mix_volume_get(struct snd_kcontrol *kcontrol,
3428c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
3458c2ecf20Sopenharmony_ci	enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value);
3468c2ecf20Sopenharmony_ci	struct amixer *amixer;
3478c2ecf20Sopenharmony_ci	int i, val;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
3508c2ecf20Sopenharmony_ci		amixer = ((struct ct_mixer *)atc->mixer)->
3518c2ecf20Sopenharmony_ci						amixers[type*CHN_NUM+i];
3528c2ecf20Sopenharmony_ci		val = amixer->ops->get_scale(amixer) / VOL_SCALE;
3538c2ecf20Sopenharmony_ci		if (val < 0)
3548c2ecf20Sopenharmony_ci			val = 0;
3558c2ecf20Sopenharmony_ci		else if (val > VOL_MAX)
3568c2ecf20Sopenharmony_ci			val = VOL_MAX;
3578c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[i] = val;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return 0;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol,
3648c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
3678c2ecf20Sopenharmony_ci	struct ct_mixer *mixer = atc->mixer;
3688c2ecf20Sopenharmony_ci	enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value);
3698c2ecf20Sopenharmony_ci	struct amixer *amixer;
3708c2ecf20Sopenharmony_ci	int i, j, val, oval, change = 0;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
3738c2ecf20Sopenharmony_ci		val = ucontrol->value.integer.value[i];
3748c2ecf20Sopenharmony_ci		if (val < 0)
3758c2ecf20Sopenharmony_ci			val = 0;
3768c2ecf20Sopenharmony_ci		else if (val > VOL_MAX)
3778c2ecf20Sopenharmony_ci			val = VOL_MAX;
3788c2ecf20Sopenharmony_ci		val *= VOL_SCALE;
3798c2ecf20Sopenharmony_ci		amixer = mixer->amixers[type*CHN_NUM+i];
3808c2ecf20Sopenharmony_ci		oval = amixer->ops->get_scale(amixer);
3818c2ecf20Sopenharmony_ci		if (val != oval) {
3828c2ecf20Sopenharmony_ci			amixer->ops->set_scale(amixer, val);
3838c2ecf20Sopenharmony_ci			amixer->ops->commit_write(amixer);
3848c2ecf20Sopenharmony_ci			change = 1;
3858c2ecf20Sopenharmony_ci			/* Synchronize Master/PCM playback AMIXERs. */
3868c2ecf20Sopenharmony_ci			if (AMIXER_MASTER_F == type || AMIXER_PCM_F == type) {
3878c2ecf20Sopenharmony_ci				for (j = 1; j < 4; j++) {
3888c2ecf20Sopenharmony_ci					amixer = mixer->
3898c2ecf20Sopenharmony_ci						amixers[(type+j)*CHN_NUM+i];
3908c2ecf20Sopenharmony_ci					amixer->ops->set_scale(amixer, val);
3918c2ecf20Sopenharmony_ci					amixer->ops->commit_write(amixer);
3928c2ecf20Sopenharmony_ci				}
3938c2ecf20Sopenharmony_ci			}
3948c2ecf20Sopenharmony_ci		}
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return change;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new vol_ctl = {
4018c2ecf20Sopenharmony_ci	.access		= SNDRV_CTL_ELEM_ACCESS_READWRITE |
4028c2ecf20Sopenharmony_ci			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
4038c2ecf20Sopenharmony_ci	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
4048c2ecf20Sopenharmony_ci	.info		= ct_alsa_mix_volume_info,
4058c2ecf20Sopenharmony_ci	.get		= ct_alsa_mix_volume_get,
4068c2ecf20Sopenharmony_ci	.put		= ct_alsa_mix_volume_put,
4078c2ecf20Sopenharmony_ci	.tlv		= { .p =  ct_vol_db_scale },
4088c2ecf20Sopenharmony_ci};
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic int output_switch_info(struct snd_kcontrol *kcontrol,
4118c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_info *info)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	static const char *const names[3] = {
4148c2ecf20Sopenharmony_ci	  "FP Headphones", "Headphones", "Speakers"
4158c2ecf20Sopenharmony_ci	};
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(info, 1, 3, names);
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int output_switch_get(struct snd_kcontrol *kcontrol,
4218c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
4248c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = atc->output_switch_get(atc);
4258c2ecf20Sopenharmony_ci	return 0;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic int output_switch_put(struct snd_kcontrol *kcontrol,
4298c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
4328c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 2)
4338c2ecf20Sopenharmony_ci		return -EINVAL;
4348c2ecf20Sopenharmony_ci	return atc->output_switch_put(atc, ucontrol->value.enumerated.item[0]);
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new output_ctl = {
4388c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4398c2ecf20Sopenharmony_ci	.name = "Analog Output Playback Enum",
4408c2ecf20Sopenharmony_ci	.info = output_switch_info,
4418c2ecf20Sopenharmony_ci	.get = output_switch_get,
4428c2ecf20Sopenharmony_ci	.put = output_switch_put,
4438c2ecf20Sopenharmony_ci};
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic int mic_source_switch_info(struct snd_kcontrol *kcontrol,
4468c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_info *info)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	static const char *const names[3] = {
4498c2ecf20Sopenharmony_ci	  "Mic", "FP Mic", "Aux"
4508c2ecf20Sopenharmony_ci	};
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(info, 1, 3, names);
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic int mic_source_switch_get(struct snd_kcontrol *kcontrol,
4568c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
4598c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = atc->mic_source_switch_get(atc);
4608c2ecf20Sopenharmony_ci	return 0;
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic int mic_source_switch_put(struct snd_kcontrol *kcontrol,
4648c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
4678c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 2)
4688c2ecf20Sopenharmony_ci		return -EINVAL;
4698c2ecf20Sopenharmony_ci	return atc->mic_source_switch_put(atc,
4708c2ecf20Sopenharmony_ci					ucontrol->value.enumerated.item[0]);
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new mic_source_ctl = {
4748c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4758c2ecf20Sopenharmony_ci	.name = "Mic Source Capture Enum",
4768c2ecf20Sopenharmony_ci	.info = mic_source_switch_info,
4778c2ecf20Sopenharmony_ci	.get = mic_source_switch_get,
4788c2ecf20Sopenharmony_ci	.put = mic_source_switch_put,
4798c2ecf20Sopenharmony_ci};
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic void
4828c2ecf20Sopenharmony_cido_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (MIXER_LINEIN_C_S == type) {
4868c2ecf20Sopenharmony_ci		atc->select_line_in(atc);
4878c2ecf20Sopenharmony_ci		set_switch_state(atc->mixer, MIXER_MIC_C_S, 0);
4888c2ecf20Sopenharmony_ci		snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE,
4898c2ecf20Sopenharmony_ci							&kctls[1]->id);
4908c2ecf20Sopenharmony_ci	} else if (MIXER_MIC_C_S == type) {
4918c2ecf20Sopenharmony_ci		atc->select_mic_in(atc);
4928c2ecf20Sopenharmony_ci		set_switch_state(atc->mixer, MIXER_LINEIN_C_S, 0);
4938c2ecf20Sopenharmony_ci		snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE,
4948c2ecf20Sopenharmony_ci							&kctls[0]->id);
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic void
4998c2ecf20Sopenharmony_cido_digit_io_switch(struct ct_atc *atc, int state)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct ct_mixer *mixer = atc->mixer;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (state) {
5048c2ecf20Sopenharmony_ci		atc->select_digit_io(atc);
5058c2ecf20Sopenharmony_ci		atc->spdif_out_unmute(atc,
5068c2ecf20Sopenharmony_ci				get_switch_state(mixer, MIXER_SPDIFO_P_S));
5078c2ecf20Sopenharmony_ci		atc->spdif_in_unmute(atc, 1);
5088c2ecf20Sopenharmony_ci		atc->line_in_unmute(atc, 0);
5098c2ecf20Sopenharmony_ci		return;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (get_switch_state(mixer, MIXER_LINEIN_C_S))
5138c2ecf20Sopenharmony_ci		atc->select_line_in(atc);
5148c2ecf20Sopenharmony_ci	else if (get_switch_state(mixer, MIXER_MIC_C_S))
5158c2ecf20Sopenharmony_ci		atc->select_mic_in(atc);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	atc->spdif_out_unmute(atc, 0);
5188c2ecf20Sopenharmony_ci	atc->spdif_in_unmute(atc, 0);
5198c2ecf20Sopenharmony_ci	atc->line_in_unmute(atc, 1);
5208c2ecf20Sopenharmony_ci	return;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct ct_mixer *mixer = atc->mixer;
5268c2ecf20Sopenharmony_ci	struct capabilities cap = atc->capabilities(atc);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/* Do changes in mixer. */
5298c2ecf20Sopenharmony_ci	if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) {
5308c2ecf20Sopenharmony_ci		if (state) {
5318c2ecf20Sopenharmony_ci			ct_mixer_recording_select(mixer,
5328c2ecf20Sopenharmony_ci						  get_amixer_index(type));
5338c2ecf20Sopenharmony_ci		} else {
5348c2ecf20Sopenharmony_ci			ct_mixer_recording_unselect(mixer,
5358c2ecf20Sopenharmony_ci						    get_amixer_index(type));
5368c2ecf20Sopenharmony_ci		}
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci	/* Do changes out of mixer. */
5398c2ecf20Sopenharmony_ci	if (!cap.dedicated_mic &&
5408c2ecf20Sopenharmony_ci	    (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) {
5418c2ecf20Sopenharmony_ci		if (state)
5428c2ecf20Sopenharmony_ci			do_line_mic_switch(atc, type);
5438c2ecf20Sopenharmony_ci		atc->line_in_unmute(atc, state);
5448c2ecf20Sopenharmony_ci	} else if (cap.dedicated_mic && (MIXER_LINEIN_C_S == type))
5458c2ecf20Sopenharmony_ci		atc->line_in_unmute(atc, state);
5468c2ecf20Sopenharmony_ci	else if (cap.dedicated_mic && (MIXER_MIC_C_S == type))
5478c2ecf20Sopenharmony_ci		atc->mic_unmute(atc, state);
5488c2ecf20Sopenharmony_ci	else if (MIXER_SPDIFI_C_S == type)
5498c2ecf20Sopenharmony_ci		atc->spdif_in_unmute(atc, state);
5508c2ecf20Sopenharmony_ci	else if (MIXER_WAVEF_P_S == type)
5518c2ecf20Sopenharmony_ci		atc->line_front_unmute(atc, state);
5528c2ecf20Sopenharmony_ci	else if (MIXER_WAVES_P_S == type)
5538c2ecf20Sopenharmony_ci		atc->line_surround_unmute(atc, state);
5548c2ecf20Sopenharmony_ci	else if (MIXER_WAVEC_P_S == type)
5558c2ecf20Sopenharmony_ci		atc->line_clfe_unmute(atc, state);
5568c2ecf20Sopenharmony_ci	else if (MIXER_WAVER_P_S == type)
5578c2ecf20Sopenharmony_ci		atc->line_rear_unmute(atc, state);
5588c2ecf20Sopenharmony_ci	else if (MIXER_SPDIFO_P_S == type)
5598c2ecf20Sopenharmony_ci		atc->spdif_out_unmute(atc, state);
5608c2ecf20Sopenharmony_ci	else if (MIXER_DIGITAL_IO_S == type)
5618c2ecf20Sopenharmony_ci		do_digit_io_switch(atc, state);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	return;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic int ct_alsa_mix_switch_info(struct snd_kcontrol *kcontrol,
5678c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_info *uinfo)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
5708c2ecf20Sopenharmony_ci	uinfo->count = 1;
5718c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
5728c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1;
5738c2ecf20Sopenharmony_ci	uinfo->value.integer.step = 1;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return 0;
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic int ct_alsa_mix_switch_get(struct snd_kcontrol *kcontrol,
5798c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct ct_mixer *mixer =
5828c2ecf20Sopenharmony_ci		((struct ct_atc *)snd_kcontrol_chip(kcontrol))->mixer;
5838c2ecf20Sopenharmony_ci	enum CTALSA_MIXER_CTL type = kcontrol->private_value;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = get_switch_state(mixer, type);
5868c2ecf20Sopenharmony_ci	return 0;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol,
5908c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
5938c2ecf20Sopenharmony_ci	struct ct_mixer *mixer = atc->mixer;
5948c2ecf20Sopenharmony_ci	enum CTALSA_MIXER_CTL type = kcontrol->private_value;
5958c2ecf20Sopenharmony_ci	int state;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	state = ucontrol->value.integer.value[0];
5988c2ecf20Sopenharmony_ci	if (get_switch_state(mixer, type) == state)
5998c2ecf20Sopenharmony_ci		return 0;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	set_switch_state(mixer, type, state);
6028c2ecf20Sopenharmony_ci	do_switch(atc, type, state);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	return 1;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new swh_ctl = {
6088c2ecf20Sopenharmony_ci	.access		= SNDRV_CTL_ELEM_ACCESS_READWRITE,
6098c2ecf20Sopenharmony_ci	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
6108c2ecf20Sopenharmony_ci	.info		= ct_alsa_mix_switch_info,
6118c2ecf20Sopenharmony_ci	.get		= ct_alsa_mix_switch_get,
6128c2ecf20Sopenharmony_ci	.put		= ct_alsa_mix_switch_put
6138c2ecf20Sopenharmony_ci};
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic int ct_spdif_info(struct snd_kcontrol *kcontrol,
6168c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_info *uinfo)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
6198c2ecf20Sopenharmony_ci	uinfo->count = 1;
6208c2ecf20Sopenharmony_ci	return 0;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic int ct_spdif_get_mask(struct snd_kcontrol *kcontrol,
6248c2ecf20Sopenharmony_ci			     struct snd_ctl_elem_value *ucontrol)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[0] = 0xff;
6278c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[1] = 0xff;
6288c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[2] = 0xff;
6298c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[3] = 0xff;
6308c2ecf20Sopenharmony_ci	return 0;
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_cistatic int ct_spdif_get(struct snd_kcontrol *kcontrol,
6348c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
6378c2ecf20Sopenharmony_ci	unsigned int status;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	atc->spdif_out_get_status(atc, &status);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (status == 0)
6428c2ecf20Sopenharmony_ci		status = SNDRV_PCM_DEFAULT_CON_SPDIF;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
6458c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
6468c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
6478c2ecf20Sopenharmony_ci	ucontrol->value.iec958.status[3] = (status >> 24) & 0xff;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	return 0;
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic int ct_spdif_put(struct snd_kcontrol *kcontrol,
6538c2ecf20Sopenharmony_ci			struct snd_ctl_elem_value *ucontrol)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
6568c2ecf20Sopenharmony_ci	int change;
6578c2ecf20Sopenharmony_ci	unsigned int status, old_status;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	status = (ucontrol->value.iec958.status[0] << 0) |
6608c2ecf20Sopenharmony_ci		 (ucontrol->value.iec958.status[1] << 8) |
6618c2ecf20Sopenharmony_ci		 (ucontrol->value.iec958.status[2] << 16) |
6628c2ecf20Sopenharmony_ci		 (ucontrol->value.iec958.status[3] << 24);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	atc->spdif_out_get_status(atc, &old_status);
6658c2ecf20Sopenharmony_ci	change = (old_status != status);
6668c2ecf20Sopenharmony_ci	if (change)
6678c2ecf20Sopenharmony_ci		atc->spdif_out_set_status(atc, status);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	return change;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new iec958_mask_ctl = {
6738c2ecf20Sopenharmony_ci	.access		= SNDRV_CTL_ELEM_ACCESS_READ,
6748c2ecf20Sopenharmony_ci	.iface		= SNDRV_CTL_ELEM_IFACE_PCM,
6758c2ecf20Sopenharmony_ci	.name		= SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
6768c2ecf20Sopenharmony_ci	.count		= 1,
6778c2ecf20Sopenharmony_ci	.info		= ct_spdif_info,
6788c2ecf20Sopenharmony_ci	.get		= ct_spdif_get_mask,
6798c2ecf20Sopenharmony_ci	.private_value	= MIXER_IEC958_MASK
6808c2ecf20Sopenharmony_ci};
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new iec958_default_ctl = {
6838c2ecf20Sopenharmony_ci	.iface		= SNDRV_CTL_ELEM_IFACE_PCM,
6848c2ecf20Sopenharmony_ci	.name		= SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
6858c2ecf20Sopenharmony_ci	.count		= 1,
6868c2ecf20Sopenharmony_ci	.info		= ct_spdif_info,
6878c2ecf20Sopenharmony_ci	.get		= ct_spdif_get,
6888c2ecf20Sopenharmony_ci	.put		= ct_spdif_put,
6898c2ecf20Sopenharmony_ci	.private_value	= MIXER_IEC958_DEFAULT
6908c2ecf20Sopenharmony_ci};
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new iec958_ctl = {
6938c2ecf20Sopenharmony_ci	.access		= SNDRV_CTL_ELEM_ACCESS_READWRITE,
6948c2ecf20Sopenharmony_ci	.iface		= SNDRV_CTL_ELEM_IFACE_PCM,
6958c2ecf20Sopenharmony_ci	.name		= SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
6968c2ecf20Sopenharmony_ci	.count		= 1,
6978c2ecf20Sopenharmony_ci	.info		= ct_spdif_info,
6988c2ecf20Sopenharmony_ci	.get		= ct_spdif_get,
6998c2ecf20Sopenharmony_ci	.put		= ct_spdif_put,
7008c2ecf20Sopenharmony_ci	.private_value	= MIXER_IEC958_STREAM
7018c2ecf20Sopenharmony_ci};
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci#define NUM_IEC958_CTL 3
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic int
7068c2ecf20Sopenharmony_cict_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
7098c2ecf20Sopenharmony_ci	int err;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	kctl = snd_ctl_new1(new, mixer->atc);
7128c2ecf20Sopenharmony_ci	if (!kctl)
7138c2ecf20Sopenharmony_ci		return -ENOMEM;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface)
7168c2ecf20Sopenharmony_ci		kctl->id.device = IEC958;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	err = snd_ctl_add(mixer->atc->card, kctl);
7198c2ecf20Sopenharmony_ci	if (err)
7208c2ecf20Sopenharmony_ci		return err;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	switch (new->private_value) {
7238c2ecf20Sopenharmony_ci	case MIXER_LINEIN_C_S:
7248c2ecf20Sopenharmony_ci		kctls[0] = kctl; break;
7258c2ecf20Sopenharmony_ci	case MIXER_MIC_C_S:
7268c2ecf20Sopenharmony_ci		kctls[1] = kctl; break;
7278c2ecf20Sopenharmony_ci	default:
7288c2ecf20Sopenharmony_ci		break;
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	return 0;
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_cistatic int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	enum CTALSA_MIXER_CTL type;
7378c2ecf20Sopenharmony_ci	struct ct_atc *atc = mixer->atc;
7388c2ecf20Sopenharmony_ci	struct capabilities cap = atc->capabilities(atc);
7398c2ecf20Sopenharmony_ci	int err;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	/* Create snd kcontrol instances on demand */
7428c2ecf20Sopenharmony_ci	for (type = VOL_MIXER_START; type <= VOL_MIXER_END; type++) {
7438c2ecf20Sopenharmony_ci		if (ct_kcontrol_init_table[type].ctl) {
7448c2ecf20Sopenharmony_ci			vol_ctl.name = ct_kcontrol_init_table[type].name;
7458c2ecf20Sopenharmony_ci			vol_ctl.private_value = (unsigned long)type;
7468c2ecf20Sopenharmony_ci			err = ct_mixer_kcontrol_new(mixer, &vol_ctl);
7478c2ecf20Sopenharmony_ci			if (err)
7488c2ecf20Sopenharmony_ci				return err;
7498c2ecf20Sopenharmony_ci		}
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = cap.digit_io_switch;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) {
7558c2ecf20Sopenharmony_ci		if (ct_kcontrol_init_table[type].ctl) {
7568c2ecf20Sopenharmony_ci			swh_ctl.name = ct_kcontrol_init_table[type].name;
7578c2ecf20Sopenharmony_ci			swh_ctl.private_value = (unsigned long)type;
7588c2ecf20Sopenharmony_ci			err = ct_mixer_kcontrol_new(mixer, &swh_ctl);
7598c2ecf20Sopenharmony_ci			if (err)
7608c2ecf20Sopenharmony_ci				return err;
7618c2ecf20Sopenharmony_ci		}
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	err = ct_mixer_kcontrol_new(mixer, &iec958_mask_ctl);
7658c2ecf20Sopenharmony_ci	if (err)
7668c2ecf20Sopenharmony_ci		return err;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	err = ct_mixer_kcontrol_new(mixer, &iec958_default_ctl);
7698c2ecf20Sopenharmony_ci	if (err)
7708c2ecf20Sopenharmony_ci		return err;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	err = ct_mixer_kcontrol_new(mixer, &iec958_ctl);
7738c2ecf20Sopenharmony_ci	if (err)
7748c2ecf20Sopenharmony_ci		return err;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (cap.output_switch) {
7778c2ecf20Sopenharmony_ci		err = ct_mixer_kcontrol_new(mixer, &output_ctl);
7788c2ecf20Sopenharmony_ci		if (err)
7798c2ecf20Sopenharmony_ci			return err;
7808c2ecf20Sopenharmony_ci	}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (cap.mic_source_switch) {
7838c2ecf20Sopenharmony_ci		err = ct_mixer_kcontrol_new(mixer, &mic_source_ctl);
7848c2ecf20Sopenharmony_ci		if (err)
7858c2ecf20Sopenharmony_ci			return err;
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci	atc->line_front_unmute(atc, 1);
7888c2ecf20Sopenharmony_ci	set_switch_state(mixer, MIXER_WAVEF_P_S, 1);
7898c2ecf20Sopenharmony_ci	atc->line_surround_unmute(atc, 0);
7908c2ecf20Sopenharmony_ci	set_switch_state(mixer, MIXER_WAVES_P_S, 0);
7918c2ecf20Sopenharmony_ci	atc->line_clfe_unmute(atc, 0);
7928c2ecf20Sopenharmony_ci	set_switch_state(mixer, MIXER_WAVEC_P_S, 0);
7938c2ecf20Sopenharmony_ci	atc->line_rear_unmute(atc, 0);
7948c2ecf20Sopenharmony_ci	set_switch_state(mixer, MIXER_WAVER_P_S, 0);
7958c2ecf20Sopenharmony_ci	atc->spdif_out_unmute(atc, 0);
7968c2ecf20Sopenharmony_ci	set_switch_state(mixer, MIXER_SPDIFO_P_S, 0);
7978c2ecf20Sopenharmony_ci	atc->line_in_unmute(atc, 0);
7988c2ecf20Sopenharmony_ci	if (cap.dedicated_mic)
7998c2ecf20Sopenharmony_ci		atc->mic_unmute(atc, 0);
8008c2ecf20Sopenharmony_ci	atc->spdif_in_unmute(atc, 0);
8018c2ecf20Sopenharmony_ci	set_switch_state(mixer, MIXER_PCM_C_S, 0);
8028c2ecf20Sopenharmony_ci	set_switch_state(mixer, MIXER_LINEIN_C_S, 0);
8038c2ecf20Sopenharmony_ci	set_switch_state(mixer, MIXER_SPDIFI_C_S, 0);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return 0;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cistatic void
8098c2ecf20Sopenharmony_cict_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type)
8108c2ecf20Sopenharmony_ci{
8118c2ecf20Sopenharmony_ci	struct amixer *amix_d;
8128c2ecf20Sopenharmony_ci	struct sum *sum_c;
8138c2ecf20Sopenharmony_ci	int i;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
8168c2ecf20Sopenharmony_ci		amix_d = mixer->amixers[type*CHN_NUM+i];
8178c2ecf20Sopenharmony_ci		sum_c = mixer->sums[SUM_IN_F_C*CHN_NUM+i];
8188c2ecf20Sopenharmony_ci		amix_d->ops->set_sum(amix_d, sum_c);
8198c2ecf20Sopenharmony_ci		amix_d->ops->commit_write(amix_d);
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic void
8248c2ecf20Sopenharmony_cict_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	struct amixer *amix_d;
8278c2ecf20Sopenharmony_ci	int i;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
8308c2ecf20Sopenharmony_ci		amix_d = mixer->amixers[type*CHN_NUM+i];
8318c2ecf20Sopenharmony_ci		amix_d->ops->set_sum(amix_d, NULL);
8328c2ecf20Sopenharmony_ci		amix_d->ops->commit_write(amix_d);
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cistatic int ct_mixer_get_resources(struct ct_mixer *mixer)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct sum_mgr *sum_mgr;
8398c2ecf20Sopenharmony_ci	struct sum *sum;
8408c2ecf20Sopenharmony_ci	struct sum_desc sum_desc = {0};
8418c2ecf20Sopenharmony_ci	struct amixer_mgr *amixer_mgr;
8428c2ecf20Sopenharmony_ci	struct amixer *amixer;
8438c2ecf20Sopenharmony_ci	struct amixer_desc am_desc = {0};
8448c2ecf20Sopenharmony_ci	int err;
8458c2ecf20Sopenharmony_ci	int i;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	/* Allocate sum resources for mixer obj */
8488c2ecf20Sopenharmony_ci	sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM];
8498c2ecf20Sopenharmony_ci	sum_desc.msr = mixer->atc->msr;
8508c2ecf20Sopenharmony_ci	for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
8518c2ecf20Sopenharmony_ci		err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum);
8528c2ecf20Sopenharmony_ci		if (err) {
8538c2ecf20Sopenharmony_ci			dev_err(mixer->atc->card->dev,
8548c2ecf20Sopenharmony_ci				"Failed to get sum resources for front output!\n");
8558c2ecf20Sopenharmony_ci			break;
8568c2ecf20Sopenharmony_ci		}
8578c2ecf20Sopenharmony_ci		mixer->sums[i] = sum;
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci	if (err)
8608c2ecf20Sopenharmony_ci		goto error1;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	/* Allocate amixer resources for mixer obj */
8638c2ecf20Sopenharmony_ci	amixer_mgr = (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER];
8648c2ecf20Sopenharmony_ci	am_desc.msr = mixer->atc->msr;
8658c2ecf20Sopenharmony_ci	for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
8668c2ecf20Sopenharmony_ci		err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer);
8678c2ecf20Sopenharmony_ci		if (err) {
8688c2ecf20Sopenharmony_ci			dev_err(mixer->atc->card->dev,
8698c2ecf20Sopenharmony_ci				"Failed to get amixer resources for mixer obj!\n");
8708c2ecf20Sopenharmony_ci			break;
8718c2ecf20Sopenharmony_ci		}
8728c2ecf20Sopenharmony_ci		mixer->amixers[i] = amixer;
8738c2ecf20Sopenharmony_ci	}
8748c2ecf20Sopenharmony_ci	if (err)
8758c2ecf20Sopenharmony_ci		goto error2;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	return 0;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_cierror2:
8808c2ecf20Sopenharmony_ci	for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
8818c2ecf20Sopenharmony_ci		if (NULL != mixer->amixers[i]) {
8828c2ecf20Sopenharmony_ci			amixer = mixer->amixers[i];
8838c2ecf20Sopenharmony_ci			amixer_mgr->put_amixer(amixer_mgr, amixer);
8848c2ecf20Sopenharmony_ci			mixer->amixers[i] = NULL;
8858c2ecf20Sopenharmony_ci		}
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_cierror1:
8888c2ecf20Sopenharmony_ci	for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
8898c2ecf20Sopenharmony_ci		if (NULL != mixer->sums[i]) {
8908c2ecf20Sopenharmony_ci			sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]);
8918c2ecf20Sopenharmony_ci			mixer->sums[i] = NULL;
8928c2ecf20Sopenharmony_ci		}
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	return err;
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic int ct_mixer_get_mem(struct ct_mixer **rmixer)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	struct ct_mixer *mixer;
9018c2ecf20Sopenharmony_ci	int err;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	*rmixer = NULL;
9048c2ecf20Sopenharmony_ci	/* Allocate mem for mixer obj */
9058c2ecf20Sopenharmony_ci	mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
9068c2ecf20Sopenharmony_ci	if (!mixer)
9078c2ecf20Sopenharmony_ci		return -ENOMEM;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	mixer->amixers = kcalloc(NUM_CT_AMIXERS * CHN_NUM, sizeof(void *),
9108c2ecf20Sopenharmony_ci				 GFP_KERNEL);
9118c2ecf20Sopenharmony_ci	if (!mixer->amixers) {
9128c2ecf20Sopenharmony_ci		err = -ENOMEM;
9138c2ecf20Sopenharmony_ci		goto error1;
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci	mixer->sums = kcalloc(NUM_CT_SUMS * CHN_NUM, sizeof(void *),
9168c2ecf20Sopenharmony_ci			      GFP_KERNEL);
9178c2ecf20Sopenharmony_ci	if (!mixer->sums) {
9188c2ecf20Sopenharmony_ci		err = -ENOMEM;
9198c2ecf20Sopenharmony_ci		goto error2;
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	*rmixer = mixer;
9238c2ecf20Sopenharmony_ci	return 0;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_cierror2:
9268c2ecf20Sopenharmony_ci	kfree(mixer->amixers);
9278c2ecf20Sopenharmony_cierror1:
9288c2ecf20Sopenharmony_ci	kfree(mixer);
9298c2ecf20Sopenharmony_ci	return err;
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_cistatic int ct_mixer_topology_build(struct ct_mixer *mixer)
9338c2ecf20Sopenharmony_ci{
9348c2ecf20Sopenharmony_ci	struct sum *sum;
9358c2ecf20Sopenharmony_ci	struct amixer *amix_d, *amix_s;
9368c2ecf20Sopenharmony_ci	enum CT_AMIXER_CTL i, j;
9378c2ecf20Sopenharmony_ci	enum CT_SUM_CTL k;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	/* Build topology from destination to source */
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	/* Set up Master mixer */
9428c2ecf20Sopenharmony_ci	for (i = AMIXER_MASTER_F, k = SUM_IN_F;
9438c2ecf20Sopenharmony_ci					i <= AMIXER_MASTER_S; i++, k++) {
9448c2ecf20Sopenharmony_ci		amix_d = mixer->amixers[i*CHN_NUM];
9458c2ecf20Sopenharmony_ci		sum = mixer->sums[k*CHN_NUM];
9468c2ecf20Sopenharmony_ci		amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
9478c2ecf20Sopenharmony_ci		amix_d = mixer->amixers[i*CHN_NUM+1];
9488c2ecf20Sopenharmony_ci		sum = mixer->sums[k*CHN_NUM+1];
9498c2ecf20Sopenharmony_ci		amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	/* Set up Wave-out mixer */
9538c2ecf20Sopenharmony_ci	for (i = AMIXER_WAVE_F, j = AMIXER_MASTER_F;
9548c2ecf20Sopenharmony_ci					i <= AMIXER_WAVE_S; i++, j++) {
9558c2ecf20Sopenharmony_ci		amix_d = mixer->amixers[i*CHN_NUM];
9568c2ecf20Sopenharmony_ci		amix_s = mixer->amixers[j*CHN_NUM];
9578c2ecf20Sopenharmony_ci		amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
9588c2ecf20Sopenharmony_ci		amix_d = mixer->amixers[i*CHN_NUM+1];
9598c2ecf20Sopenharmony_ci		amix_s = mixer->amixers[j*CHN_NUM+1];
9608c2ecf20Sopenharmony_ci		amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
9618c2ecf20Sopenharmony_ci	}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	/* Set up S/PDIF-out mixer */
9648c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM];
9658c2ecf20Sopenharmony_ci	amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM];
9668c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
9678c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM+1];
9688c2ecf20Sopenharmony_ci	amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM+1];
9698c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	/* Set up PCM-in mixer */
9728c2ecf20Sopenharmony_ci	for (i = AMIXER_PCM_F, k = SUM_IN_F; i <= AMIXER_PCM_S; i++, k++) {
9738c2ecf20Sopenharmony_ci		amix_d = mixer->amixers[i*CHN_NUM];
9748c2ecf20Sopenharmony_ci		sum = mixer->sums[k*CHN_NUM];
9758c2ecf20Sopenharmony_ci		amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
9768c2ecf20Sopenharmony_ci		amix_d = mixer->amixers[i*CHN_NUM+1];
9778c2ecf20Sopenharmony_ci		sum = mixer->sums[k*CHN_NUM+1];
9788c2ecf20Sopenharmony_ci		amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	/* Set up Line-in mixer */
9828c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM];
9838c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F*CHN_NUM];
9848c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
9858c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM+1];
9868c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
9878c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	/* Set up Mic-in mixer */
9908c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM];
9918c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F*CHN_NUM];
9928c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
9938c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM+1];
9948c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
9958c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/* Set up S/PDIF-in mixer */
9988c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM];
9998c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F*CHN_NUM];
10008c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
10018c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM+1];
10028c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
10038c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	/* Set up Master recording mixer */
10068c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM];
10078c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
10088c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
10098c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM+1];
10108c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
10118c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	/* Set up PCM-in recording mixer */
10148c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM];
10158c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
10168c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
10178c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM+1];
10188c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
10198c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	/* Set up Line-in recording mixer */
10228c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM];
10238c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
10248c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
10258c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM+1];
10268c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
10278c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	/* Set up Mic-in recording mixer */
10308c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM];
10318c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
10328c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
10338c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM+1];
10348c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
10358c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	/* Set up S/PDIF-in recording mixer */
10388c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM];
10398c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
10408c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
10418c2ecf20Sopenharmony_ci	amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM+1];
10428c2ecf20Sopenharmony_ci	sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
10438c2ecf20Sopenharmony_ci	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	return 0;
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic int mixer_set_input_port(struct amixer *amixer, struct rsc *rsc)
10498c2ecf20Sopenharmony_ci{
10508c2ecf20Sopenharmony_ci	amixer->ops->set_input(amixer, rsc);
10518c2ecf20Sopenharmony_ci	amixer->ops->commit_write(amixer);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	return 0;
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_cistatic enum CT_AMIXER_CTL port_to_amixer(enum MIXER_PORT_T type)
10578c2ecf20Sopenharmony_ci{
10588c2ecf20Sopenharmony_ci	switch (type) {
10598c2ecf20Sopenharmony_ci	case MIX_WAVE_FRONT:	return AMIXER_WAVE_F;
10608c2ecf20Sopenharmony_ci	case MIX_WAVE_SURROUND:	return AMIXER_WAVE_S;
10618c2ecf20Sopenharmony_ci	case MIX_WAVE_CENTLFE:	return AMIXER_WAVE_C;
10628c2ecf20Sopenharmony_ci	case MIX_WAVE_REAR:	return AMIXER_WAVE_R;
10638c2ecf20Sopenharmony_ci	case MIX_PCMO_FRONT:	return AMIXER_MASTER_F_C;
10648c2ecf20Sopenharmony_ci	case MIX_SPDIF_OUT:	return AMIXER_SPDIFO;
10658c2ecf20Sopenharmony_ci	case MIX_LINE_IN:	return AMIXER_LINEIN;
10668c2ecf20Sopenharmony_ci	case MIX_MIC_IN:	return AMIXER_MIC;
10678c2ecf20Sopenharmony_ci	case MIX_SPDIF_IN:	return AMIXER_SPDIFI;
10688c2ecf20Sopenharmony_ci	case MIX_PCMI_FRONT:	return AMIXER_PCM_F;
10698c2ecf20Sopenharmony_ci	case MIX_PCMI_SURROUND:	return AMIXER_PCM_S;
10708c2ecf20Sopenharmony_ci	case MIX_PCMI_CENTLFE:	return AMIXER_PCM_C;
10718c2ecf20Sopenharmony_ci	case MIX_PCMI_REAR:	return AMIXER_PCM_R;
10728c2ecf20Sopenharmony_ci	default: 		return 0;
10738c2ecf20Sopenharmony_ci	}
10748c2ecf20Sopenharmony_ci}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_cistatic int mixer_get_output_ports(struct ct_mixer *mixer,
10778c2ecf20Sopenharmony_ci				  enum MIXER_PORT_T type,
10788c2ecf20Sopenharmony_ci				  struct rsc **rleft, struct rsc **rright)
10798c2ecf20Sopenharmony_ci{
10808c2ecf20Sopenharmony_ci	enum CT_AMIXER_CTL amix = port_to_amixer(type);
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	if (NULL != rleft)
10838c2ecf20Sopenharmony_ci		*rleft = &((struct amixer *)mixer->amixers[amix*CHN_NUM])->rsc;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	if (NULL != rright)
10868c2ecf20Sopenharmony_ci		*rright =
10878c2ecf20Sopenharmony_ci			&((struct amixer *)mixer->amixers[amix*CHN_NUM+1])->rsc;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	return 0;
10908c2ecf20Sopenharmony_ci}
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_cistatic int mixer_set_input_left(struct ct_mixer *mixer,
10938c2ecf20Sopenharmony_ci				enum MIXER_PORT_T type, struct rsc *rsc)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	enum CT_AMIXER_CTL amix = port_to_amixer(type);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc);
10988c2ecf20Sopenharmony_ci	amix = get_recording_amixer(amix);
10998c2ecf20Sopenharmony_ci	if (amix < NUM_CT_AMIXERS)
11008c2ecf20Sopenharmony_ci		mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc);
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	return 0;
11038c2ecf20Sopenharmony_ci}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_cistatic int
11068c2ecf20Sopenharmony_cimixer_set_input_right(struct ct_mixer *mixer,
11078c2ecf20Sopenharmony_ci		      enum MIXER_PORT_T type, struct rsc *rsc)
11088c2ecf20Sopenharmony_ci{
11098c2ecf20Sopenharmony_ci	enum CT_AMIXER_CTL amix = port_to_amixer(type);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc);
11128c2ecf20Sopenharmony_ci	amix = get_recording_amixer(amix);
11138c2ecf20Sopenharmony_ci	if (amix < NUM_CT_AMIXERS)
11148c2ecf20Sopenharmony_ci		mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	return 0;
11178c2ecf20Sopenharmony_ci}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
11208c2ecf20Sopenharmony_cistatic int mixer_resume(struct ct_mixer *mixer)
11218c2ecf20Sopenharmony_ci{
11228c2ecf20Sopenharmony_ci	int i, state;
11238c2ecf20Sopenharmony_ci	struct amixer *amixer;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	/* resume topology and volume gain. */
11268c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_CT_AMIXERS*CHN_NUM; i++) {
11278c2ecf20Sopenharmony_ci		amixer = mixer->amixers[i];
11288c2ecf20Sopenharmony_ci		amixer->ops->commit_write(amixer);
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	/* resume switch state. */
11328c2ecf20Sopenharmony_ci	for (i = SWH_MIXER_START; i <= SWH_MIXER_END; i++) {
11338c2ecf20Sopenharmony_ci		state = get_switch_state(mixer, i);
11348c2ecf20Sopenharmony_ci		do_switch(mixer->atc, i, state);
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	return 0;
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ci#endif
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ciint ct_mixer_destroy(struct ct_mixer *mixer)
11428c2ecf20Sopenharmony_ci{
11438c2ecf20Sopenharmony_ci	struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM];
11448c2ecf20Sopenharmony_ci	struct amixer_mgr *amixer_mgr =
11458c2ecf20Sopenharmony_ci			(struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER];
11468c2ecf20Sopenharmony_ci	struct amixer *amixer;
11478c2ecf20Sopenharmony_ci	int i = 0;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	/* Release amixer resources */
11508c2ecf20Sopenharmony_ci	for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
11518c2ecf20Sopenharmony_ci		if (NULL != mixer->amixers[i]) {
11528c2ecf20Sopenharmony_ci			amixer = mixer->amixers[i];
11538c2ecf20Sopenharmony_ci			amixer_mgr->put_amixer(amixer_mgr, amixer);
11548c2ecf20Sopenharmony_ci		}
11558c2ecf20Sopenharmony_ci	}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	/* Release sum resources */
11588c2ecf20Sopenharmony_ci	for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
11598c2ecf20Sopenharmony_ci		if (NULL != mixer->sums[i])
11608c2ecf20Sopenharmony_ci			sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]);
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	/* Release mem assigned to mixer object */
11648c2ecf20Sopenharmony_ci	kfree(mixer->sums);
11658c2ecf20Sopenharmony_ci	kfree(mixer->amixers);
11668c2ecf20Sopenharmony_ci	kfree(mixer);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	return 0;
11698c2ecf20Sopenharmony_ci}
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ciint ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer)
11728c2ecf20Sopenharmony_ci{
11738c2ecf20Sopenharmony_ci	struct ct_mixer *mixer;
11748c2ecf20Sopenharmony_ci	int err;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	*rmixer = NULL;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	/* Allocate mem for mixer obj */
11798c2ecf20Sopenharmony_ci	err = ct_mixer_get_mem(&mixer);
11808c2ecf20Sopenharmony_ci	if (err)
11818c2ecf20Sopenharmony_ci		return err;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	mixer->switch_state = 0;
11848c2ecf20Sopenharmony_ci	mixer->atc = atc;
11858c2ecf20Sopenharmony_ci	/* Set operations */
11868c2ecf20Sopenharmony_ci	mixer->get_output_ports = mixer_get_output_ports;
11878c2ecf20Sopenharmony_ci	mixer->set_input_left = mixer_set_input_left;
11888c2ecf20Sopenharmony_ci	mixer->set_input_right = mixer_set_input_right;
11898c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
11908c2ecf20Sopenharmony_ci	mixer->resume = mixer_resume;
11918c2ecf20Sopenharmony_ci#endif
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	/* Allocate chip resources for mixer obj */
11948c2ecf20Sopenharmony_ci	err = ct_mixer_get_resources(mixer);
11958c2ecf20Sopenharmony_ci	if (err)
11968c2ecf20Sopenharmony_ci		goto error;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	/* Build internal mixer topology */
11998c2ecf20Sopenharmony_ci	ct_mixer_topology_build(mixer);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	*rmixer = mixer;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	return 0;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_cierror:
12068c2ecf20Sopenharmony_ci	ct_mixer_destroy(mixer);
12078c2ecf20Sopenharmony_ci	return err;
12088c2ecf20Sopenharmony_ci}
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ciint ct_alsa_mix_create(struct ct_atc *atc,
12118c2ecf20Sopenharmony_ci		       enum CTALSADEVS device,
12128c2ecf20Sopenharmony_ci		       const char *device_name)
12138c2ecf20Sopenharmony_ci{
12148c2ecf20Sopenharmony_ci	int err;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	/* Create snd kcontrol instances on demand */
12178c2ecf20Sopenharmony_ci	/* vol_ctl.device = swh_ctl.device = device; */ /* better w/ device 0 */
12188c2ecf20Sopenharmony_ci	err = ct_mixer_kcontrols_create((struct ct_mixer *)atc->mixer);
12198c2ecf20Sopenharmony_ci	if (err)
12208c2ecf20Sopenharmony_ci		return err;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	strcpy(atc->card->mixername, device_name);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	return 0;
12258c2ecf20Sopenharmony_ci}
1226