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