/* * intelmid_v2_control.c - Intel Sound card driver for MID * * Copyright (C) 2008-10 Intel Corp * Authors: Vinod Koul * Harsha Priya * KP Jeeja * Dharageswari R * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This file contains the control operations of vendor 3 */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include "intel_sst.h" #include "intelmid_snd_control.h" #include "intelmid.h" enum reg_v3 { VAUDIOCNT = 0x51, VOICEPORT1 = 0x100, VOICEPORT2 = 0x101, AUDIOPORT1 = 0x102, AUDIOPORT2 = 0x103, ADCSAMPLERATE = 0x104, DMICCTRL1 = 0x105, DMICCTRL2 = 0x106, MICCTRL = 0x107, MICSELVOL = 0x108, LILSEL = 0x109, LIRSEL = 0x10a, VOICEVOL = 0x10b, AUDIOLVOL = 0x10c, AUDIORVOL = 0x10d, LMUTE = 0x10e, RMUTE = 0x10f, POWERCTRL1 = 0x110, POWERCTRL2 = 0x111, DRVPOWERCTRL = 0x112, VREFPLL = 0x113, PCMBUFCTRL = 0x114, SOFTMUTE = 0x115, DTMFPATH = 0x116, DTMFVOL = 0x117, DTMFFREQ = 0x118, DTMFHFREQ = 0x119, DTMFLFREQ = 0x11a, DTMFCTRL = 0x11b, DTMFASON = 0x11c, DTMFASOFF = 0x11d, DTMFASINUM = 0x11e, CLASSDVOL = 0x11f, VOICEDACAVOL = 0x120, AUDDACAVOL = 0x121, LOMUTEVOL = 0x122, HPLVOL = 0x123, HPRVOL = 0x124, MONOVOL = 0x125, LINEOUTMIXVOL = 0x126, EPMIXVOL = 0x127, LINEOUTLSEL = 0x128, LINEOUTRSEL = 0x129, EPMIXOUTSEL = 0x12a, HPLMIXSEL = 0x12b, HPRMIXSEL = 0x12c, LOANTIPOP = 0x12d, AUXDBNC = 0x12f, }; static void nc_set_amp_power(int power) { if (snd_pmic_ops_nc.gpio_amp) gpio_set_value(snd_pmic_ops_nc.gpio_amp, power); } /**** * nc_init_card - initialize the sound card * * This initializes the audio paths to know values in case of this sound card */ static int nc_init_card(void) { struct sc_reg_access sc_access[] = { {VAUDIOCNT, 0x25, 0}, {VOICEPORT1, 0x00, 0}, {VOICEPORT2, 0x00, 0}, {AUDIOPORT1, 0x98, 0}, {AUDIOPORT2, 0x09, 0}, {AUDIOLVOL, 0x00, 0}, {AUDIORVOL, 0x00, 0}, {LMUTE, 0x03, 0}, {RMUTE, 0x03, 0}, {POWERCTRL1, 0x00, 0}, {POWERCTRL2, 0x00, 0}, {DRVPOWERCTRL, 0x00, 0}, {VREFPLL, 0x10, 0}, {HPLMIXSEL, 0xee, 0}, {HPRMIXSEL, 0xf6, 0}, {PCMBUFCTRL, 0x0, 0}, {VOICEVOL, 0x0e, 0}, {HPLVOL, 0x06, 0}, {HPRVOL, 0x06, 0}, {MICCTRL, 0x51, 0x00}, {ADCSAMPLERATE, 0x8B, 0x00}, {MICSELVOL, 0x5B, 0x00}, {LILSEL, 0x06, 0}, {LIRSEL, 0x46, 0}, {LOANTIPOP, 0x00, 0}, {DMICCTRL1, 0x40, 0}, {AUXDBNC, 0xff, 0}, }; snd_pmic_ops_nc.card_status = SND_CARD_INIT_DONE; snd_pmic_ops_nc.master_mute = UNMUTE; snd_pmic_ops_nc.mute_status = UNMUTE; sst_sc_reg_access(sc_access, PMIC_WRITE, 27); mutex_init(&snd_pmic_ops_nc.lock); pr_debug("init complete!!\n"); return 0; } static int nc_enable_audiodac(int value) { struct sc_reg_access sc_access[3]; int mute_val = 0; if (snd_pmic_ops_nc.mute_status == MUTE) return 0; if (((snd_pmic_ops_nc.output_dev_id == MONO_EARPIECE) || (snd_pmic_ops_nc.output_dev_id == INTERNAL_SPKR)) && (value == UNMUTE)) return 0; if (value == UNMUTE) { /* unmute the system, set the 7th bit to zero */ mute_val = 0x00; } else { /* MUTE:Set the seventh bit */ mute_val = 0x04; } sc_access[0].reg_addr = LMUTE; sc_access[1].reg_addr = RMUTE; sc_access[0].mask = sc_access[1].mask = MASK2; sc_access[0].value = sc_access[1].value = mute_val; if (snd_pmic_ops_nc.num_channel == 1) sc_access[1].value = 0x04; return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); } static int nc_power_up_pb(unsigned int port) { struct sc_reg_access sc_access[7]; int retval = 0; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; if (port == 0xFF) return 0; mutex_lock(&snd_pmic_ops_nc.lock); nc_enable_audiodac(MUTE); msleep(30); pr_debug("powering up pb....\n"); sc_access[0].reg_addr = VAUDIOCNT; sc_access[0].value = 0x27; sc_access[0].mask = 0x27; sc_access[1].reg_addr = VREFPLL; if (port == 0) { sc_access[1].value = 0x3A; sc_access[1].mask = 0x3A; } else if (port == 1) { sc_access[1].value = 0x35; sc_access[1].mask = 0x35; } retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); sc_access[0].reg_addr = POWERCTRL1; if (port == 0) { sc_access[0].value = 0x40; sc_access[0].mask = 0x40; } else if (port == 1) { sc_access[0].value = 0x01; sc_access[0].mask = 0x01; } sc_access[1].reg_addr = POWERCTRL2; sc_access[1].value = 0x0C; sc_access[1].mask = 0x0C; sc_access[2].reg_addr = DRVPOWERCTRL; sc_access[2].value = 0x86; sc_access[2].mask = 0x86; sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); msleep(30); snd_pmic_ops_nc.pb_on = 1; /* * There is a mismatch between Playback Sources and the enumerated * values of output sources. This mismatch causes ALSA upper to send * Item 1 for Internal Speaker, but the expected enumeration is 2! For * now, treat MONO_EARPIECE and INTERNAL_SPKR identically and power up * the needed resources */ if (snd_pmic_ops_nc.output_dev_id == MONO_EARPIECE || snd_pmic_ops_nc.output_dev_id == INTERNAL_SPKR) nc_set_amp_power(1); nc_enable_audiodac(UNMUTE); mutex_unlock(&snd_pmic_ops_nc.lock); return 0; } static int nc_power_up_cp(unsigned int port) { struct sc_reg_access sc_access[5]; int retval = 0; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; pr_debug("powering up cp....\n"); if (port == 0xFF) return 0; sc_access[0].reg_addr = VAUDIOCNT; sc_access[0].value = 0x27; sc_access[0].mask = 0x27; sc_access[1].reg_addr = VREFPLL; if (port == 0) { sc_access[1].value = 0x3E; sc_access[1].mask = 0x3E; } else if (port == 1) { sc_access[1].value = 0x35; sc_access[1].mask = 0x35; } retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); sc_access[0].reg_addr = POWERCTRL1; if (port == 0) { sc_access[0].value = 0xB4; sc_access[0].mask = 0xB4; } else if (port == 1) { sc_access[0].value = 0xBF; sc_access[0].mask = 0xBF; } sc_access[1].reg_addr = POWERCTRL2; if (port == 0) { sc_access[1].value = 0x0C; sc_access[1].mask = 0x0C; } else if (port == 1) { sc_access[1].value = 0x02; sc_access[1].mask = 0x02; } return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); } static int nc_power_down(void) { int retval = 0; struct sc_reg_access sc_access[5]; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; nc_enable_audiodac(MUTE); pr_debug("powering dn nc_power_down ....\n"); if (snd_pmic_ops_nc.output_dev_id == MONO_EARPIECE || snd_pmic_ops_nc.output_dev_id == INTERNAL_SPKR) nc_set_amp_power(0); msleep(30); sc_access[0].reg_addr = DRVPOWERCTRL; sc_access[0].value = 0x00; sc_access[0].mask = 0x00; sst_sc_reg_access(sc_access, PMIC_WRITE, 1); sc_access[0].reg_addr = POWERCTRL1; sc_access[0].value = 0x00; sc_access[0].mask = 0x00; sc_access[1].reg_addr = POWERCTRL2; sc_access[1].value = 0x00; sc_access[1].mask = 0x00; sst_sc_reg_access(sc_access, PMIC_WRITE, 2); msleep(30); sc_access[0].reg_addr = VREFPLL; sc_access[0].value = 0x10; sc_access[0].mask = 0x10; sc_access[1].reg_addr = VAUDIOCNT; sc_access[1].value = 0x25; sc_access[1].mask = 0x25; retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 2); msleep(30); return nc_enable_audiodac(UNMUTE); } static int nc_power_down_pb(unsigned int device) { int retval = 0; struct sc_reg_access sc_access[5]; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; pr_debug("powering dn pb....\n"); mutex_lock(&snd_pmic_ops_nc.lock); nc_enable_audiodac(MUTE); msleep(30); sc_access[0].reg_addr = DRVPOWERCTRL; sc_access[0].value = 0x00; sc_access[0].mask = 0x00; sst_sc_reg_access(sc_access, PMIC_WRITE, 1); msleep(30); sc_access[0].reg_addr = POWERCTRL1; sc_access[0].value = 0x00; sc_access[0].mask = 0x41; sc_access[1].reg_addr = POWERCTRL2; sc_access[1].value = 0x00; sc_access[1].mask = 0x0C; sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); msleep(30); snd_pmic_ops_nc.pb_on = 0; nc_enable_audiodac(UNMUTE); mutex_unlock(&snd_pmic_ops_nc.lock); return 0; } static int nc_power_down_cp(unsigned int device) { struct sc_reg_access sc_access[] = { {POWERCTRL1, 0x00, 0xBE}, {POWERCTRL2, 0x00, 0x02}, }; int retval = 0; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; pr_debug("powering dn cp....\n"); return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); } static int nc_set_pcm_voice_params(void) { struct sc_reg_access sc_access[] = { {0x100, 0xD5, 0}, {0x101, 0x08, 0}, {0x104, 0x03, 0}, {0x107, 0x10, 0}, {0x10B, 0x0E, 0}, {0x10E, 0x03, 0}, {0x10F, 0x03, 0}, {0x114, 0x13, 0}, {0x115, 0x00, 0}, {0x128, 0xFE, 0}, {0x129, 0xFE, 0}, {0x12A, 0xFE, 0}, {0x12B, 0xDE, 0}, {0x12C, 0xDE, 0}, }; int retval = 0; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; sst_sc_reg_access(sc_access, PMIC_WRITE, 14); pr_debug("Voice parameters set successfully!!\n"); return 0; } static int nc_set_pcm_audio_params(int sfreq, int word_size, int num_channel) { int config2 = 0; struct sc_reg_access sc_access; int retval = 0; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; switch (sfreq) { case 8000: config2 = 0x00; break; case 11025: config2 = 0x01; break; case 12000: config2 = 0x02; break; case 16000: config2 = 0x03; break; case 22050: config2 = 0x04; break; case 24000: config2 = 0x05; break; case 32000: config2 = 0x07; break; case 44100: config2 = 0x08; break; case 48000: config2 = 0x09; break; } snd_pmic_ops_nc.num_channel = num_channel; if (snd_pmic_ops_nc.num_channel == 1) { sc_access.value = 0x07; sc_access.reg_addr = RMUTE; pr_debug("RIGHT_HP_MUTE value%d\n", sc_access.value); sc_access.mask = MASK2; sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); } else { sc_access.value = 0x00; sc_access.reg_addr = RMUTE; pr_debug("RIGHT_HP_MUTE value %d\n", sc_access.value); sc_access.mask = MASK2; sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); } pr_debug("word_size = %d\n", word_size); if (word_size == 24) { sc_access.reg_addr = AUDIOPORT2; sc_access.value = config2 | 0x10; sc_access.mask = 0x1F; } else { sc_access.value = config2; sc_access.mask = 0x1F; sc_access.reg_addr = AUDIOPORT2; } sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); pr_debug("word_size = %d\n", word_size); sc_access.reg_addr = AUDIOPORT1; sc_access.mask = MASK5|MASK4|MASK1|MASK0; if (word_size == 16) sc_access.value = 0x98; else if (word_size == 24) sc_access.value = 0xAB; return sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); } static int nc_set_selected_output_dev(u8 value) { struct sc_reg_access sc_access_HP[] = { {LMUTE, 0x02, 0x06}, {RMUTE, 0x02, 0x06}, {DRVPOWERCTRL, 0x06, 0x06}, }; struct sc_reg_access sc_access_IS[] = { {LMUTE, 0x04, 0x06}, {RMUTE, 0x04, 0x06}, {DRVPOWERCTRL, 0x00, 0x06}, }; int retval = 0; snd_pmic_ops_nc.output_dev_id = value; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; pr_debug("nc set selected output:%d\n", value); mutex_lock(&snd_pmic_ops_nc.lock); switch (value) { case STEREO_HEADPHONE: if (snd_pmic_ops_nc.pb_on) sst_sc_reg_access(sc_access_HP+2, PMIC_WRITE, 1); retval = sst_sc_reg_access(sc_access_HP, PMIC_WRITE, 2); nc_set_amp_power(0); break; case MONO_EARPIECE: case INTERNAL_SPKR: retval = sst_sc_reg_access(sc_access_IS, PMIC_WRITE, 3); if (snd_pmic_ops_nc.pb_on) nc_set_amp_power(1); break; default: pr_err("rcvd illegal request: %d\n", value); mutex_unlock(&snd_pmic_ops_nc.lock); return -EINVAL; } mutex_unlock(&snd_pmic_ops_nc.lock); return retval; } static int nc_audio_init(void) { struct sc_reg_access sc_acces, sc_access[] = { {0x100, 0x00, 0}, {0x101, 0x00, 0}, {0x104, 0x8B, 0}, {0x107, 0x11, 0}, {0x10B, 0x0E, 0}, {0x114, 0x00, 0}, {0x115, 0x00, 0}, {0x128, 0x00, 0}, {0x129, 0x00, 0}, {0x12A, 0x00, 0}, {0x12B, 0xee, 0}, {0x12C, 0xf6, 0}, }; sst_sc_reg_access(sc_access, PMIC_WRITE, 12); pr_debug("Audio Init successfully!!\n"); /*set output device */ nc_set_selected_output_dev(snd_pmic_ops_nc.output_dev_id); if (snd_pmic_ops_nc.num_channel == 1) { sc_acces.value = 0x07; sc_acces.reg_addr = RMUTE; pr_debug("RIGHT_HP_MUTE value%d\n", sc_acces.value); sc_acces.mask = MASK2; sst_sc_reg_access(&sc_acces, PMIC_READ_MODIFY, 1); } else { sc_acces.value = 0x00; sc_acces.reg_addr = RMUTE; pr_debug("RIGHT_HP_MUTE value%d\n", sc_acces.value); sc_acces.mask = MASK2; sst_sc_reg_access(&sc_acces, PMIC_READ_MODIFY, 1); } return 0; } static int nc_set_audio_port(int status) { struct sc_reg_access sc_access[2] = {{0,},}; int retval = 0; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; if (status == DEACTIVATE) { /* Deactivate audio port-tristate and power */ sc_access[0].value = 0x00; sc_access[0].mask = MASK4|MASK5; sc_access[0].reg_addr = AUDIOPORT1; return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); } else if (status == ACTIVATE) { /* activate audio port */ nc_audio_init(); sc_access[0].value = 0x10; sc_access[0].mask = MASK4|MASK5 ; sc_access[0].reg_addr = AUDIOPORT1; return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); } else return -EINVAL; } static int nc_set_voice_port(int status) { struct sc_reg_access sc_access[2] = {{0,},}; int retval = 0; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; if (status == DEACTIVATE) { /* Activate Voice port */ sc_access[0].value = 0x00; sc_access[0].mask = MASK4; sc_access[0].reg_addr = VOICEPORT1; return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); } else if (status == ACTIVATE) { /* Deactivate voice port */ nc_set_pcm_voice_params(); sc_access[0].value = 0x10; sc_access[0].mask = MASK4; sc_access[0].reg_addr = VOICEPORT1; return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); } else return -EINVAL; } static int nc_set_mute(int dev_id, u8 value) { struct sc_reg_access sc_access[3]; u8 mute_val, cap_mute; int retval = 0; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; pr_debug("set device id::%d, value %d\n", dev_id, value); switch (dev_id) { case PMIC_SND_MUTE_ALL: pr_debug("PMIC_SND_MUTE_ALL value %d\n", value); snd_pmic_ops_nc.mute_status = value; snd_pmic_ops_nc.master_mute = value; if (value == UNMUTE) { /* unmute the system, set the 7th bit to zero */ mute_val = cap_mute = 0x00; } else { /* MUTE:Set the seventh bit */ mute_val = 0x80; cap_mute = 0x40; } sc_access[0].reg_addr = AUDIOLVOL; sc_access[1].reg_addr = AUDIORVOL; sc_access[0].mask = sc_access[1].mask = MASK7; sc_access[0].value = sc_access[1].value = mute_val; if (snd_pmic_ops_nc.num_channel == 1) sc_access[1].value = 0x80; if (!sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2)) { sc_access[0].reg_addr = 0x109; sc_access[1].reg_addr = 0x10a; sc_access[2].reg_addr = 0x105; sc_access[0].mask = sc_access[1].mask = sc_access[2].mask = MASK6; sc_access[0].value = sc_access[1].value = sc_access[2].value = cap_mute; if ((snd_pmic_ops_nc.input_dev_id == AMIC) || (snd_pmic_ops_nc.input_dev_id == DMIC)) sc_access[1].value = 0x40; if (snd_pmic_ops_nc.input_dev_id == HS_MIC) sc_access[0].value = 0x40; retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); } break; case PMIC_SND_HP_MIC_MUTE: pr_debug("PMIC_SND_HPMIC_MUTE value %d\n", value); if (value == UNMUTE) { /* unmute the system, set the 6th bit to one */ sc_access[0].value = 0x00; } else { /* mute the system, reset the 6th bit to zero */ sc_access[0].value = 0x40; } sc_access[0].reg_addr = LIRSEL; sc_access[0].mask = MASK6; retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); break; case PMIC_SND_AMIC_MUTE: pr_debug("PMIC_SND_AMIC_MUTE value %d\n", value); if (value == UNMUTE) { /* unmute the system, set the 6th bit to one */ sc_access[0].value = 0x00; } else { /* mute the system, reset the 6th bit to zero */ sc_access[0].value = 0x40; } sc_access[0].reg_addr = LILSEL; sc_access[0].mask = MASK6; retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); break; case PMIC_SND_DMIC_MUTE: pr_debug("INPUT_MUTE_DMIC value%d\n", value); if (value == UNMUTE) { /* unmute the system, set the 6th bit to one */ sc_access[1].value = 0x00; sc_access[0].value = 0x00; } else { /* mute the system, reset the 6th bit to zero */ sc_access[1].value = 0x40; sc_access[0].value = 0x40; } sc_access[0].reg_addr = DMICCTRL1; sc_access[0].mask = MASK6; sc_access[1].reg_addr = LILSEL; sc_access[1].mask = MASK6; retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); break; case PMIC_SND_LEFT_HP_MUTE: case PMIC_SND_RIGHT_HP_MUTE: snd_pmic_ops_nc.mute_status = value; if (value == UNMUTE) sc_access[0].value = 0x0; else sc_access[0].value = 0x04; if (dev_id == PMIC_SND_LEFT_HP_MUTE) { sc_access[0].reg_addr = LMUTE; pr_debug("LEFT_HP_MUTE value %d\n", sc_access[0].value); } else { if (snd_pmic_ops_nc.num_channel == 1) sc_access[0].value = 0x04; sc_access[0].reg_addr = RMUTE; pr_debug("RIGHT_HP_MUTE value %d\n", sc_access[0].value); } sc_access[0].mask = MASK2; retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); break; case PMIC_SND_LEFT_SPEAKER_MUTE: case PMIC_SND_RIGHT_SPEAKER_MUTE: if (value == UNMUTE) sc_access[0].value = 0x00; else sc_access[0].value = 0x03; sc_access[0].reg_addr = LMUTE; pr_debug("SPEAKER_MUTE %d\n", sc_access[0].value); sc_access[0].mask = MASK1; retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); break; default: return -EINVAL; } return retval ; } static int nc_set_vol(int dev_id, int value) { struct sc_reg_access sc_access[3]; int retval = 0, entries = 0; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; pr_debug("set volume:%d\n", dev_id); switch (dev_id) { case PMIC_SND_CAPTURE_VOL: pr_debug("PMIC_SND_CAPTURE_VOL:value::%d\n", value); sc_access[0].value = sc_access[1].value = sc_access[2].value = -value; sc_access[0].mask = sc_access[1].mask = sc_access[2].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); sc_access[0].reg_addr = 0x10a; sc_access[1].reg_addr = 0x109; sc_access[2].reg_addr = 0x105; entries = 3; break; case PMIC_SND_LEFT_PB_VOL: pr_debug("PMIC_SND_LEFT_HP_VOL %d\n", value); sc_access[0].value = -value; sc_access[0].reg_addr = HPLVOL; sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4); entries = 1; break; case PMIC_SND_RIGHT_PB_VOL: pr_debug("PMIC_SND_RIGHT_HP_VOL value %d\n", value); if (snd_pmic_ops_nc.num_channel == 1) { sc_access[0].value = 0x04; sc_access[0].reg_addr = RMUTE; sc_access[0].mask = MASK2; } else { sc_access[0].value = -value; sc_access[0].reg_addr = HPRVOL; sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4); } entries = 1; break; case PMIC_SND_LEFT_MASTER_VOL: pr_debug("PMIC_SND_LEFT_MASTER_VOL value %d\n", value); sc_access[0].value = -value; sc_access[0].reg_addr = AUDIOLVOL; sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); entries = 1; break; case PMIC_SND_RIGHT_MASTER_VOL: pr_debug("PMIC_SND_RIGHT_MASTER_VOL value %d\n", value); sc_access[0].value = -value; sc_access[0].reg_addr = AUDIORVOL; sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); entries = 1; break; default: return -EINVAL; } return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, entries); } static int nc_set_selected_input_dev(u8 value) { struct sc_reg_access sc_access[6]; u8 num_val; int retval = 0; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; snd_pmic_ops_nc.input_dev_id = value; pr_debug("nc set selected input:%d\n", value); switch (value) { case AMIC: pr_debug("Selecting AMIC\n"); sc_access[0].reg_addr = 0x107; sc_access[0].value = 0x40; sc_access[0].mask = MASK6|MASK3|MASK1|MASK0; sc_access[1].reg_addr = 0x10a; sc_access[1].value = 0x40; sc_access[1].mask = MASK6; sc_access[2].reg_addr = 0x109; sc_access[2].value = 0x00; sc_access[2].mask = MASK6; sc_access[3].reg_addr = 0x105; sc_access[3].value = 0x40; sc_access[3].mask = MASK6; num_val = 4; break; case HS_MIC: pr_debug("Selecting HS_MIC\n"); sc_access[0].reg_addr = MICCTRL; sc_access[0].mask = MASK6|MASK3|MASK1|MASK0; sc_access[0].value = 0x00; sc_access[1].reg_addr = 0x109; sc_access[1].mask = MASK6; sc_access[1].value = 0x40; sc_access[2].reg_addr = 0x10a; sc_access[2].mask = MASK6; sc_access[2].value = 0x00; sc_access[3].reg_addr = 0x105; sc_access[3].value = 0x40; sc_access[3].mask = MASK6; sc_access[4].reg_addr = ADCSAMPLERATE; sc_access[4].mask = MASK7|MASK6|MASK5|MASK4|MASK3; sc_access[4].value = 0xc8; num_val = 5; break; case DMIC: pr_debug("DMIC\n"); sc_access[0].reg_addr = MICCTRL; sc_access[0].mask = MASK6|MASK3|MASK1|MASK0; sc_access[0].value = 0x0B; sc_access[1].reg_addr = 0x105; sc_access[1].value = 0x80; sc_access[1].mask = MASK7|MASK6; sc_access[2].reg_addr = 0x10a; sc_access[2].value = 0x40; sc_access[2].mask = MASK6; sc_access[3].reg_addr = LILSEL; sc_access[3].mask = MASK6; sc_access[3].value = 0x00; sc_access[4].reg_addr = ADCSAMPLERATE; sc_access[4].mask = MASK7|MASK6|MASK5|MASK4|MASK3; sc_access[4].value = 0x33; num_val = 5; break; default: return -EINVAL; } return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_val); } static int nc_get_mute(int dev_id, u8 *value) { int retval = 0, mask = 0; struct sc_reg_access sc_access = {0,}; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; pr_debug("get mute::%d\n", dev_id); switch (dev_id) { case PMIC_SND_AMIC_MUTE: pr_debug("PMIC_SND_INPUT_MUTE_MIC1\n"); sc_access.reg_addr = LILSEL; mask = MASK6; break; case PMIC_SND_HP_MIC_MUTE: pr_debug("PMIC_SND_INPUT_MUTE_MIC2\n"); sc_access.reg_addr = LIRSEL; mask = MASK6; break; case PMIC_SND_LEFT_HP_MUTE: case PMIC_SND_RIGHT_HP_MUTE: mask = MASK2; pr_debug("PMIC_SN_LEFT/RIGHT_HP_MUTE\n"); if (dev_id == PMIC_SND_RIGHT_HP_MUTE) sc_access.reg_addr = RMUTE; else sc_access.reg_addr = LMUTE; break; case PMIC_SND_LEFT_SPEAKER_MUTE: pr_debug("PMIC_MONO_EARPIECE_MUTE\n"); sc_access.reg_addr = RMUTE; mask = MASK1; break; case PMIC_SND_DMIC_MUTE: pr_debug("PMIC_SND_INPUT_MUTE_DMIC\n"); sc_access.reg_addr = 0x105; mask = MASK6; break; default: return -EINVAL; } retval = sst_sc_reg_access(&sc_access, PMIC_READ, 1); pr_debug("reg value = %d\n", sc_access.value); if (retval) return retval; *value = (sc_access.value) & mask; pr_debug("masked value = %d\n", *value); if (*value) *value = 0; else *value = 1; pr_debug("value returned = 0x%x\n", *value); return retval; } static int nc_get_vol(int dev_id, int *value) { int retval = 0, mask = 0; struct sc_reg_access sc_access = {0,}; if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) return retval; switch (dev_id) { case PMIC_SND_CAPTURE_VOL: pr_debug("PMIC_SND_INPUT_CAPTURE_VOL\n"); sc_access.reg_addr = LILSEL; mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); break; case PMIC_SND_LEFT_MASTER_VOL: pr_debug("GET_VOLUME_PMIC_LEFT_MASTER_VOL\n"); sc_access.reg_addr = AUDIOLVOL; mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); break; case PMIC_SND_RIGHT_MASTER_VOL: pr_debug("GET_VOLUME_PMIC_RIGHT_MASTER_VOL\n"); sc_access.reg_addr = AUDIORVOL; mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); break; case PMIC_SND_RIGHT_PB_VOL: pr_debug("GET_VOLUME_PMIC_RIGHT_HP_VOL\n"); sc_access.reg_addr = HPRVOL; mask = (MASK0|MASK1|MASK2|MASK3|MASK4); break; case PMIC_SND_LEFT_PB_VOL: pr_debug("GET_VOLUME_PMIC_LEFT_HP_VOL\n"); sc_access.reg_addr = HPLVOL; mask = (MASK0|MASK1|MASK2|MASK3|MASK4); break; default: return -EINVAL; } retval = sst_sc_reg_access(&sc_access, PMIC_READ, 1); pr_debug("value read = 0x%x\n", sc_access.value); *value = -((sc_access.value) & mask); pr_debug("get vol value returned = %d\n", *value); return retval; } static void hp_automute(enum snd_jack_types type, int present) { u8 in = DMIC; u8 out = INTERNAL_SPKR; if (present) { if (type == SND_JACK_HEADSET) in = HS_MIC; out = STEREO_HEADPHONE; } nc_set_selected_input_dev(in); nc_set_selected_output_dev(out); } static void nc_pmic_irq_cb(void *cb_data, u8 intsts) { u8 value = 0; struct mad_jack *mjack = NULL; unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; struct snd_intelmad *intelmaddata = cb_data; struct sc_reg_access sc_access_read = {0,}; sc_access_read.reg_addr = 0x132; sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); value = (sc_access_read.value); pr_debug("value returned = 0x%x\n", value); mjack = &intelmaddata->jack[0]; if (intsts & 0x1) { pr_debug("SST DBG:MAD headset detected\n"); /* send headset detect/undetect */ present = (value == 0x1) ? 3 : 0; jack_event_flag = 1; mjack->jack.type = SND_JACK_HEADSET; hp_automute(SND_JACK_HEADSET, present); } if (intsts & 0x2) { pr_debug(":MAD headphone detected\n"); /* send headphone detect/undetect */ present = (value == 0x2) ? 1 : 0; jack_event_flag = 1; mjack->jack.type = SND_JACK_HEADPHONE; hp_automute(SND_JACK_HEADPHONE, present); } if (intsts & 0x4) { pr_debug("MAD short push detected\n"); /* send short push */ present = 1; jack_event_flag = 1; buttonpressflag = 1; mjack->jack.type = MID_JACK_HS_SHORT_PRESS; } if (intsts & 0x8) { pr_debug(":MAD long push detected\n"); /* send long push */ present = 1; jack_event_flag = 1; buttonpressflag = 1; mjack->jack.type = MID_JACK_HS_LONG_PRESS; } if (jack_event_flag) sst_mad_send_jack_report(&mjack->jack, buttonpressflag, present); } static int nc_jack_enable(void) { return 0; } struct snd_pmic_ops snd_pmic_ops_nc = { .input_dev_id = DMIC, .output_dev_id = INTERNAL_SPKR, .set_input_dev = nc_set_selected_input_dev, .set_output_dev = nc_set_selected_output_dev, .set_mute = nc_set_mute, .get_mute = nc_get_mute, .set_vol = nc_set_vol, .get_vol = nc_get_vol, .init_card = nc_init_card, .set_pcm_audio_params = nc_set_pcm_audio_params, .set_pcm_voice_params = nc_set_pcm_voice_params, .set_voice_port = nc_set_voice_port, .set_audio_port = nc_set_audio_port, .power_up_pmic_pb = nc_power_up_pb, .power_up_pmic_cp = nc_power_up_cp, .power_down_pmic_pb = nc_power_down_pb, .power_down_pmic_cp = nc_power_down_cp, .power_down_pmic = nc_power_down, .pmic_irq_cb = nc_pmic_irq_cb, .pmic_jack_enable = nc_jack_enable, };