979 lines
24 KiB
C
979 lines
24 KiB
C
/* intel_sst_v1_control.c - Intel SST Driver for audio engine
|
|
*
|
|
* Copyright (C) 2008-10 Intel Corp
|
|
* Authors: Vinod Koul <vinod.koul@intel.com>
|
|
* Harsha Priya <priya.harsha@intel.com>
|
|
* Dharageswari R <dharageswari.r@intel.com>
|
|
* KP Jeeja <jeeja.kp@intel.com>
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* 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 2
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/pci.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/file.h>
|
|
#include <asm/mrst.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/control.h>
|
|
#include <sound/initval.h>
|
|
#include "intel_sst.h"
|
|
#include "intel_sst_ioctl.h"
|
|
#include "intelmid.h"
|
|
#include "intelmid_snd_control.h"
|
|
|
|
#include <linux/gpio.h>
|
|
#define KOSKI_VOICE_CODEC_ENABLE 46
|
|
|
|
enum _reg_v2 {
|
|
|
|
MASTER_CLOCK_PRESCALAR = 0x205,
|
|
SET_MASTER_AND_LR_CLK1 = 0x20b,
|
|
SET_MASTER_AND_LR_CLK2 = 0x20c,
|
|
MASTER_MODE_AND_DATA_DELAY = 0x20d,
|
|
DIGITAL_INTERFACE_TO_DAI2 = 0x20e,
|
|
CLK_AND_FS1 = 0x208,
|
|
CLK_AND_FS2 = 0x209,
|
|
DAI2_TO_DAC_HP = 0x210,
|
|
HP_OP_SINGLE_ENDED = 0x224,
|
|
ENABLE_OPDEV_CTRL = 0x226,
|
|
ENABLE_DEV_AND_USE_XTAL = 0x227,
|
|
|
|
/* Max audio subsystem (PQ49) MAX 8921 */
|
|
AS_IP_MODE_CTL = 0xF9,
|
|
AS_LEFT_SPKR_VOL_CTL = 0xFA, /* Mono Earpiece volume control */
|
|
AS_RIGHT_SPKR_VOL_CTL = 0xFB,
|
|
AS_LEFT_HP_VOL_CTL = 0xFC,
|
|
AS_RIGHT_HP_VOL_CTL = 0xFD,
|
|
AS_OP_MIX_CTL = 0xFE,
|
|
AS_CONFIG = 0xFF,
|
|
|
|
/* Headphone volume control & mute registers */
|
|
VOL_CTRL_LT = 0x21c,
|
|
VOL_CTRL_RT = 0x21d,
|
|
|
|
};
|
|
/**
|
|
* mx_init_card - initialize the sound card
|
|
*
|
|
* This initializes the audio paths to know values in case of this sound card
|
|
*/
|
|
static int mx_init_card(void)
|
|
{
|
|
struct sc_reg_access sc_access[] = {
|
|
{0x200, 0x80, 0x00},
|
|
{0x201, 0xC0, 0x00},
|
|
{0x202, 0x00, 0x00},
|
|
{0x203, 0x00, 0x00},
|
|
{0x204, 0x02, 0x00},
|
|
{0x205, 0x10, 0x00},
|
|
{0x206, 0x60, 0x00},
|
|
{0x207, 0x00, 0x00},
|
|
{0x208, 0x90, 0x00},
|
|
{0x209, 0x51, 0x00},
|
|
{0x20a, 0x00, 0x00},
|
|
{0x20b, 0x10, 0x00},
|
|
{0x20c, 0x00, 0x00},
|
|
{0x20d, 0x00, 0x00},
|
|
{0x20e, 0x21, 0x00},
|
|
{0x20f, 0x00, 0x00},
|
|
{0x210, 0x84, 0x00},
|
|
{0x211, 0xB3, 0x00},
|
|
{0x212, 0x00, 0x00},
|
|
{0x213, 0x00, 0x00},
|
|
{0x214, 0x41, 0x00},
|
|
{0x215, 0x00, 0x00},
|
|
{0x216, 0x00, 0x00},
|
|
{0x217, 0x00, 0x00},
|
|
{0x218, 0x03, 0x00},
|
|
{0x219, 0x03, 0x00},
|
|
{0x21a, 0x00, 0x00},
|
|
{0x21b, 0x00, 0x00},
|
|
{0x21c, 0x00, 0x00},
|
|
{0x21d, 0x00, 0x00},
|
|
{0x21e, 0x00, 0x00},
|
|
{0x21f, 0x00, 0x00},
|
|
{0x220, 0x20, 0x00},
|
|
{0x221, 0x20, 0x00},
|
|
{0x222, 0x51, 0x00},
|
|
{0x223, 0x20, 0x00},
|
|
{0x224, 0x04, 0x00},
|
|
{0x225, 0x80, 0x00},
|
|
{0x226, 0x0F, 0x00},
|
|
{0x227, 0x08, 0x00},
|
|
{0xf9, 0x40, 0x00},
|
|
{0xfa, 0x1f, 0x00},
|
|
{0xfb, 0x1f, 0x00},
|
|
{0xfc, 0x1f, 0x00},
|
|
{0xfd, 0x1f, 0x00},
|
|
{0xfe, 0x00, 0x00},
|
|
{0xff, 0x0c, 0x00},
|
|
};
|
|
snd_pmic_ops_mx.card_status = SND_CARD_INIT_DONE;
|
|
snd_pmic_ops_mx.num_channel = 2;
|
|
snd_pmic_ops_mx.master_mute = UNMUTE;
|
|
snd_pmic_ops_mx.mute_status = UNMUTE;
|
|
return sst_sc_reg_access(sc_access, PMIC_WRITE, 47);
|
|
}
|
|
|
|
static int mx_enable_audiodac(int value)
|
|
{
|
|
struct sc_reg_access sc_access[3];
|
|
int mute_val = 0;
|
|
int mute_val1 = 0;
|
|
int retval = 0;
|
|
|
|
sc_access[0].reg_addr = AS_LEFT_HP_VOL_CTL;
|
|
sc_access[1].reg_addr = AS_RIGHT_HP_VOL_CTL;
|
|
|
|
if (value == UNMUTE) {
|
|
mute_val = 0x1F;
|
|
mute_val1 = 0x00;
|
|
} else {
|
|
mute_val = 0x00;
|
|
mute_val1 = 0x40;
|
|
}
|
|
sc_access[0].mask = sc_access[1].mask = MASK0|MASK1|MASK2|MASK3|MASK4;
|
|
sc_access[0].value = sc_access[1].value = (u8)mute_val;
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
|
|
if (retval)
|
|
return retval;
|
|
pr_debug("mute status = %d\n", snd_pmic_ops_mx.mute_status);
|
|
if (snd_pmic_ops_mx.mute_status == MUTE ||
|
|
snd_pmic_ops_mx.master_mute == MUTE)
|
|
return retval;
|
|
|
|
sc_access[0].reg_addr = VOL_CTRL_LT;
|
|
sc_access[1].reg_addr = VOL_CTRL_RT;
|
|
sc_access[0].mask = sc_access[1].mask = MASK6;
|
|
sc_access[0].value = sc_access[1].value = mute_val1;
|
|
if (snd_pmic_ops_mx.num_channel == 1)
|
|
sc_access[1].value = 0x40;
|
|
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
|
|
}
|
|
|
|
static int mx_power_up_pb(unsigned int port)
|
|
{
|
|
|
|
int retval = 0;
|
|
struct sc_reg_access sc_access[3];
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
retval = mx_enable_audiodac(MUTE);
|
|
if (retval)
|
|
return retval;
|
|
|
|
msleep(10);
|
|
|
|
sc_access[0].reg_addr = AS_CONFIG;
|
|
sc_access[0].mask = MASK7;
|
|
sc_access[0].value = 0x80;
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
|
|
if (retval)
|
|
return retval;
|
|
|
|
sc_access[0].reg_addr = ENABLE_OPDEV_CTRL;
|
|
sc_access[0].mask = 0xff;
|
|
sc_access[0].value = 0x3C;
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
|
|
if (retval)
|
|
return retval;
|
|
|
|
sc_access[0].reg_addr = ENABLE_DEV_AND_USE_XTAL;
|
|
sc_access[0].mask = 0x80;
|
|
sc_access[0].value = 0x80;
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
|
|
if (retval)
|
|
return retval;
|
|
|
|
return mx_enable_audiodac(UNMUTE);
|
|
}
|
|
|
|
static int mx_power_down_pb(unsigned int device)
|
|
{
|
|
struct sc_reg_access sc_access[3];
|
|
int retval = 0;
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
|
|
retval = mx_enable_audiodac(MUTE);
|
|
if (retval)
|
|
return retval;
|
|
|
|
sc_access[0].reg_addr = ENABLE_OPDEV_CTRL;
|
|
sc_access[0].mask = MASK3|MASK2;
|
|
sc_access[0].value = 0x00;
|
|
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
|
|
if (retval)
|
|
return retval;
|
|
|
|
return mx_enable_audiodac(UNMUTE);
|
|
}
|
|
|
|
static int mx_power_up_cp(unsigned int port)
|
|
{
|
|
int retval = 0;
|
|
struct sc_reg_access sc_access[] = {
|
|
{ENABLE_DEV_AND_USE_XTAL, 0x80, MASK7},
|
|
{ENABLE_OPDEV_CTRL, 0x3, 0x3},
|
|
};
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
|
|
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
|
|
}
|
|
|
|
static int mx_power_down_cp(unsigned int device)
|
|
{
|
|
struct sc_reg_access sc_access[] = {
|
|
{ENABLE_OPDEV_CTRL, 0x00, MASK1|MASK0},
|
|
};
|
|
int retval = 0;
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
|
|
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
|
|
}
|
|
|
|
static int mx_power_down(void)
|
|
{
|
|
int retval = 0;
|
|
struct sc_reg_access sc_access[3];
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
|
|
retval = mx_enable_audiodac(MUTE);
|
|
if (retval)
|
|
return retval;
|
|
|
|
sc_access[0].reg_addr = AS_CONFIG;
|
|
sc_access[0].mask = MASK7;
|
|
sc_access[0].value = 0x00;
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
|
|
if (retval)
|
|
return retval;
|
|
|
|
sc_access[0].reg_addr = ENABLE_DEV_AND_USE_XTAL;
|
|
sc_access[0].mask = MASK7;
|
|
sc_access[0].value = 0x00;
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
|
|
if (retval)
|
|
return retval;
|
|
|
|
sc_access[0].reg_addr = ENABLE_OPDEV_CTRL;
|
|
sc_access[0].mask = MASK3|MASK2;
|
|
sc_access[0].value = 0x00;
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1);
|
|
if (retval)
|
|
return retval;
|
|
|
|
return mx_enable_audiodac(UNMUTE);
|
|
}
|
|
|
|
static int mx_set_pcm_voice_params(void)
|
|
{
|
|
int retval = 0;
|
|
struct sc_reg_access sc_access[] = {
|
|
{0x200, 0x80, 0x00},
|
|
{0x201, 0xC0, 0x00},
|
|
{0x202, 0x00, 0x00},
|
|
{0x203, 0x00, 0x00},
|
|
{0x204, 0x0e, 0x00},
|
|
{0x205, 0x20, 0x00},
|
|
{0x206, 0x8f, 0x00},
|
|
{0x207, 0x21, 0x00},
|
|
{0x208, 0x18, 0x00},
|
|
{0x209, 0x32, 0x00},
|
|
{0x20a, 0x00, 0x00},
|
|
{0x20b, 0x5A, 0x00},
|
|
{0x20c, 0xBE, 0x00},/* 0x00 -> 0xBE Koski */
|
|
{0x20d, 0x00, 0x00}, /* DAI2 'off' */
|
|
{0x20e, 0x40, 0x00},
|
|
{0x20f, 0x00, 0x00},
|
|
{0x210, 0x84, 0x00},
|
|
{0x211, 0x33, 0x00}, /* Voice filter */
|
|
{0x212, 0x00, 0x00},
|
|
{0x213, 0x00, 0x00},
|
|
{0x214, 0x41, 0x00},
|
|
{0x215, 0x00, 0x00},
|
|
{0x216, 0x00, 0x00},
|
|
{0x217, 0x20, 0x00},
|
|
{0x218, 0x00, 0x00},
|
|
{0x219, 0x00, 0x00},
|
|
{0x21a, 0x40, 0x00},
|
|
{0x21b, 0x40, 0x00},
|
|
{0x21c, 0x09, 0x00},
|
|
{0x21d, 0x09, 0x00},
|
|
{0x21e, 0x00, 0x00},
|
|
{0x21f, 0x00, 0x00},
|
|
{0x220, 0x00, 0x00}, /* Microphone configurations */
|
|
{0x221, 0x00, 0x00}, /* Microphone configurations */
|
|
{0x222, 0x50, 0x00}, /* Microphone configurations */
|
|
{0x223, 0x21, 0x00}, /* Microphone configurations */
|
|
{0x224, 0x00, 0x00},
|
|
{0x225, 0x80, 0x00},
|
|
{0xf9, 0x40, 0x00},
|
|
{0xfa, 0x19, 0x00},
|
|
{0xfb, 0x19, 0x00},
|
|
{0xfc, 0x12, 0x00},
|
|
{0xfd, 0x12, 0x00},
|
|
{0xfe, 0x00, 0x00},
|
|
};
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
pr_debug("SST DBG:mx_set_pcm_voice_params called\n");
|
|
return sst_sc_reg_access(sc_access, PMIC_WRITE, 44);
|
|
}
|
|
|
|
static int mx_set_pcm_audio_params(int sfreq, int word_size, int num_channel)
|
|
{
|
|
int retval = 0;
|
|
|
|
int config1 = 0, config2 = 0, filter = 0xB3;
|
|
struct sc_reg_access sc_access[5];
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
|
|
switch (sfreq) {
|
|
case 8000:
|
|
config1 = 0x10;
|
|
config2 = 0x00;
|
|
filter = 0x33;
|
|
break;
|
|
case 11025:
|
|
config1 = 0x16;
|
|
config2 = 0x0d;
|
|
break;
|
|
case 12000:
|
|
config1 = 0x18;
|
|
config2 = 0x00;
|
|
break;
|
|
case 16000:
|
|
config1 = 0x20;
|
|
config2 = 0x00;
|
|
break;
|
|
case 22050:
|
|
config1 = 0x2c;
|
|
config2 = 0x1a;
|
|
break;
|
|
case 24000:
|
|
config1 = 0x30;
|
|
config2 = 0x00;
|
|
break;
|
|
case 32000:
|
|
config1 = 0x40;
|
|
config2 = 0x00;
|
|
break;
|
|
case 44100:
|
|
config1 = 0x58;
|
|
config2 = 0x33;
|
|
break;
|
|
case 48000:
|
|
config1 = 0x60;
|
|
config2 = 0x00;
|
|
break;
|
|
}
|
|
snd_pmic_ops_mx.num_channel = num_channel;
|
|
/*mute the right channel if MONO*/
|
|
if (snd_pmic_ops_mx.num_channel == 1) {
|
|
sc_access[0].reg_addr = VOL_CTRL_RT;
|
|
sc_access[0].value = 0x40;
|
|
sc_access[0].mask = MASK6;
|
|
|
|
sc_access[1].reg_addr = 0x224;
|
|
sc_access[1].value = 0x05;
|
|
sc_access[1].mask = MASK0|MASK1|MASK2;
|
|
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
|
|
if (retval)
|
|
return retval;
|
|
} else {
|
|
sc_access[0].reg_addr = VOL_CTRL_RT;
|
|
sc_access[0].value = 0x00;
|
|
sc_access[0].mask = MASK6;
|
|
|
|
sc_access[1].reg_addr = 0x224;
|
|
sc_access[1].value = 0x04;
|
|
sc_access[1].mask = MASK0|MASK1|MASK2;
|
|
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2);
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
sc_access[0].reg_addr = 0x206;
|
|
sc_access[0].value = config1;
|
|
sc_access[1].reg_addr = 0x207;
|
|
sc_access[1].value = config2;
|
|
|
|
if (word_size == 16) {
|
|
sc_access[2].value = 0x51;
|
|
sc_access[3].value = 0x31;
|
|
} else if (word_size == 24) {
|
|
sc_access[2].value = 0x52;
|
|
sc_access[3].value = 0x92;
|
|
}
|
|
|
|
sc_access[2].reg_addr = 0x209;
|
|
sc_access[3].reg_addr = 0x20e;
|
|
|
|
sc_access[4].reg_addr = 0x211;
|
|
sc_access[4].value = filter;
|
|
|
|
return sst_sc_reg_access(sc_access, PMIC_WRITE, 5);
|
|
}
|
|
|
|
static int mx_set_selected_output_dev(u8 dev_id)
|
|
{
|
|
struct sc_reg_access sc_access[2];
|
|
int num_reg = 0;
|
|
int retval = 0;
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
|
|
pr_debug("mx_set_selected_output_dev dev_id:0x%x\n", dev_id);
|
|
snd_pmic_ops_mx.output_dev_id = dev_id;
|
|
switch (dev_id) {
|
|
case STEREO_HEADPHONE:
|
|
sc_access[0].reg_addr = 0xFF;
|
|
sc_access[0].value = 0x8C;
|
|
sc_access[0].mask =
|
|
MASK2|MASK3|MASK5|MASK6|MASK4;
|
|
|
|
num_reg = 1;
|
|
break;
|
|
case MONO_EARPIECE:
|
|
case INTERNAL_SPKR:
|
|
sc_access[0].reg_addr = 0xFF;
|
|
sc_access[0].value = 0xb0;
|
|
sc_access[0].mask = MASK2|MASK3|MASK5|MASK6|MASK4;
|
|
|
|
num_reg = 1;
|
|
break;
|
|
case RECEIVER:
|
|
pr_debug("RECEIVER Koski selected\n");
|
|
|
|
/* configuration - AS enable, receiver enable */
|
|
sc_access[0].reg_addr = 0xFF;
|
|
sc_access[0].value = 0x81;
|
|
sc_access[0].mask = 0xff;
|
|
|
|
num_reg = 1;
|
|
break;
|
|
default:
|
|
pr_err("Not a valid output dev\n");
|
|
return 0;
|
|
}
|
|
return sst_sc_reg_access(sc_access, PMIC_WRITE, num_reg);
|
|
}
|
|
|
|
|
|
static int mx_set_voice_port(int status)
|
|
{
|
|
int retval = 0;
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
if (status == ACTIVATE)
|
|
retval = mx_set_pcm_voice_params();
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int mx_set_audio_port(int status)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int mx_set_selected_input_dev(u8 dev_id)
|
|
{
|
|
struct sc_reg_access sc_access[2];
|
|
int num_reg = 0;
|
|
int retval = 0;
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
snd_pmic_ops_mx.input_dev_id = dev_id;
|
|
pr_debug("mx_set_selected_input_dev dev_id:0x%x\n", dev_id);
|
|
|
|
switch (dev_id) {
|
|
case AMIC:
|
|
sc_access[0].reg_addr = 0x223;
|
|
sc_access[0].value = 0x00;
|
|
sc_access[0].mask = MASK7|MASK6|MASK5|MASK4|MASK0;
|
|
sc_access[1].reg_addr = 0x222;
|
|
sc_access[1].value = 0x50;
|
|
sc_access[1].mask = MASK7|MASK6|MASK5|MASK4;
|
|
num_reg = 2;
|
|
break;
|
|
|
|
case HS_MIC:
|
|
sc_access[0].reg_addr = 0x223;
|
|
sc_access[0].value = 0x20;
|
|
sc_access[0].mask = MASK7|MASK6|MASK5|MASK4|MASK0;
|
|
sc_access[1].reg_addr = 0x222;
|
|
sc_access[1].value = 0x51;
|
|
sc_access[1].mask = MASK7|MASK6|MASK5|MASK4;
|
|
num_reg = 2;
|
|
break;
|
|
case DMIC:
|
|
sc_access[1].reg_addr = 0x222;
|
|
sc_access[1].value = 0x00;
|
|
sc_access[1].mask = MASK7|MASK6|MASK5|MASK4|MASK0;
|
|
sc_access[0].reg_addr = 0x223;
|
|
sc_access[0].value = 0x20;
|
|
sc_access[0].mask = MASK7|MASK6|MASK5|MASK4|MASK0;
|
|
num_reg = 2;
|
|
break;
|
|
}
|
|
return sst_sc_reg_access(sc_access, PMIC_WRITE, num_reg);
|
|
}
|
|
|
|
static int mx_set_mute(int dev_id, u8 value)
|
|
{
|
|
struct sc_reg_access sc_access[5];
|
|
int num_reg = 0;
|
|
int retval = 0;
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
|
|
|
|
pr_debug("set_mute dev_id:0x%x , value:%d\n", dev_id, value);
|
|
|
|
switch (dev_id) {
|
|
case PMIC_SND_DMIC_MUTE:
|
|
case PMIC_SND_AMIC_MUTE:
|
|
case PMIC_SND_HP_MIC_MUTE:
|
|
sc_access[0].reg_addr = 0x220;
|
|
sc_access[1].reg_addr = 0x221;
|
|
sc_access[2].reg_addr = 0x223;
|
|
if (value == MUTE) {
|
|
sc_access[0].value = 0x00;
|
|
sc_access[1].value = 0x00;
|
|
if (snd_pmic_ops_mx.input_dev_id == DMIC)
|
|
sc_access[2].value = 0x00;
|
|
else
|
|
sc_access[2].value = 0x20;
|
|
} else {
|
|
sc_access[0].value = 0x20;
|
|
sc_access[1].value = 0x20;
|
|
if (snd_pmic_ops_mx.input_dev_id == DMIC)
|
|
sc_access[2].value = 0x20;
|
|
else
|
|
sc_access[2].value = 0x00;
|
|
}
|
|
sc_access[0].mask = MASK5|MASK6;
|
|
sc_access[1].mask = MASK5|MASK6;
|
|
sc_access[2].mask = MASK5|MASK6;
|
|
num_reg = 3;
|
|
break;
|
|
case PMIC_SND_LEFT_SPEAKER_MUTE:
|
|
case PMIC_SND_LEFT_HP_MUTE:
|
|
sc_access[0].reg_addr = VOL_CTRL_LT;
|
|
if (value == MUTE)
|
|
sc_access[0].value = 0x40;
|
|
else
|
|
sc_access[0].value = 0x00;
|
|
sc_access[0].mask = MASK6;
|
|
num_reg = 1;
|
|
snd_pmic_ops_mx.mute_status = value;
|
|
break;
|
|
case PMIC_SND_RIGHT_SPEAKER_MUTE:
|
|
case PMIC_SND_RIGHT_HP_MUTE:
|
|
sc_access[0].reg_addr = VOL_CTRL_RT;
|
|
if (snd_pmic_ops_mx.num_channel == 1)
|
|
value = MUTE;
|
|
if (value == MUTE)
|
|
sc_access[0].value = 0x40;
|
|
else
|
|
sc_access[0].value = 0x00;
|
|
sc_access[0].mask = MASK6;
|
|
num_reg = 1;
|
|
snd_pmic_ops_mx.mute_status = value;
|
|
break;
|
|
case PMIC_SND_MUTE_ALL:
|
|
sc_access[0].reg_addr = VOL_CTRL_RT;
|
|
sc_access[1].reg_addr = VOL_CTRL_LT;
|
|
sc_access[2].reg_addr = 0x220;
|
|
sc_access[3].reg_addr = 0x221;
|
|
sc_access[4].reg_addr = 0x223;
|
|
snd_pmic_ops_mx.master_mute = value;
|
|
if (value == MUTE) {
|
|
sc_access[0].value = sc_access[1].value = 0x40;
|
|
sc_access[2].value = 0x00;
|
|
sc_access[3].value = 0x00;
|
|
if (snd_pmic_ops_mx.input_dev_id == DMIC)
|
|
sc_access[4].value = 0x00;
|
|
else
|
|
sc_access[4].value = 0x20;
|
|
|
|
} else {
|
|
sc_access[0].value = sc_access[1].value = 0x00;
|
|
sc_access[2].value = sc_access[3].value = 0x20;
|
|
sc_access[4].value = 0x20;
|
|
if (snd_pmic_ops_mx.input_dev_id == DMIC)
|
|
sc_access[4].value = 0x20;
|
|
else
|
|
sc_access[4].value = 0x00;
|
|
|
|
|
|
}
|
|
if (snd_pmic_ops_mx.num_channel == 1)
|
|
sc_access[0].value = 0x40;
|
|
sc_access[0].mask = sc_access[1].mask = MASK6;
|
|
sc_access[2].mask = MASK5|MASK6;
|
|
sc_access[3].mask = MASK5|MASK6|MASK2|MASK4;
|
|
sc_access[4].mask = MASK5|MASK6|MASK4;
|
|
|
|
num_reg = 5;
|
|
break;
|
|
case PMIC_SND_RECEIVER_MUTE:
|
|
sc_access[0].reg_addr = VOL_CTRL_RT;
|
|
if (value == MUTE)
|
|
sc_access[0].value = 0x40;
|
|
else
|
|
sc_access[0].value = 0x00;
|
|
sc_access[0].mask = MASK6;
|
|
num_reg = 1;
|
|
break;
|
|
}
|
|
|
|
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg);
|
|
}
|
|
|
|
static int mx_set_vol(int dev_id, int value)
|
|
{
|
|
struct sc_reg_access sc_access[2] = {{0},};
|
|
int num_reg = 0;
|
|
int retval = 0;
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
pr_debug("set_vol dev_id:0x%x ,value:%d\n", dev_id, value);
|
|
switch (dev_id) {
|
|
case PMIC_SND_RECEIVER_VOL:
|
|
return 0;
|
|
break;
|
|
case PMIC_SND_CAPTURE_VOL:
|
|
sc_access[0].reg_addr = 0x220;
|
|
sc_access[1].reg_addr = 0x221;
|
|
sc_access[0].value = sc_access[1].value = -value;
|
|
sc_access[0].mask = sc_access[1].mask =
|
|
(MASK0|MASK1|MASK2|MASK3|MASK4);
|
|
num_reg = 2;
|
|
break;
|
|
case PMIC_SND_LEFT_PB_VOL:
|
|
sc_access[0].value = -value;
|
|
sc_access[0].reg_addr = VOL_CTRL_LT;
|
|
sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5);
|
|
num_reg = 1;
|
|
break;
|
|
case PMIC_SND_RIGHT_PB_VOL:
|
|
sc_access[0].value = -value;
|
|
sc_access[0].reg_addr = VOL_CTRL_RT;
|
|
sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5);
|
|
if (snd_pmic_ops_mx.num_channel == 1) {
|
|
sc_access[0].value = 0x40;
|
|
sc_access[0].mask = MASK6;
|
|
sc_access[0].reg_addr = VOL_CTRL_RT;
|
|
}
|
|
num_reg = 1;
|
|
break;
|
|
}
|
|
return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg);
|
|
}
|
|
|
|
static int mx_get_mute(int dev_id, u8 *value)
|
|
{
|
|
struct sc_reg_access sc_access[4] = {{0},};
|
|
int retval = 0, num_reg = 0, mask = 0;
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
switch (dev_id) {
|
|
case PMIC_SND_DMIC_MUTE:
|
|
case PMIC_SND_AMIC_MUTE:
|
|
case PMIC_SND_HP_MIC_MUTE:
|
|
sc_access[0].reg_addr = 0x220;
|
|
mask = MASK5|MASK6;
|
|
num_reg = 1;
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ, num_reg);
|
|
if (retval)
|
|
return retval;
|
|
*value = sc_access[0].value & mask;
|
|
if (*value)
|
|
*value = UNMUTE;
|
|
else
|
|
*value = MUTE;
|
|
return retval;
|
|
case PMIC_SND_LEFT_HP_MUTE:
|
|
case PMIC_SND_LEFT_SPEAKER_MUTE:
|
|
sc_access[0].reg_addr = VOL_CTRL_LT;
|
|
num_reg = 1;
|
|
mask = MASK6;
|
|
break;
|
|
case PMIC_SND_RIGHT_HP_MUTE:
|
|
case PMIC_SND_RIGHT_SPEAKER_MUTE:
|
|
sc_access[0].reg_addr = VOL_CTRL_RT;
|
|
num_reg = 1;
|
|
mask = MASK6;
|
|
break;
|
|
}
|
|
retval = sst_sc_reg_access(sc_access, PMIC_READ, num_reg);
|
|
if (retval)
|
|
return retval;
|
|
*value = sc_access[0].value & mask;
|
|
if (*value)
|
|
*value = MUTE;
|
|
else
|
|
*value = UNMUTE;
|
|
return retval;
|
|
}
|
|
|
|
static int mx_get_vol(int dev_id, int *value)
|
|
{
|
|
struct sc_reg_access sc_access = {0,};
|
|
int retval = 0, mask = 0, num_reg = 0;
|
|
|
|
if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) {
|
|
retval = mx_init_card();
|
|
if (retval)
|
|
return retval;
|
|
}
|
|
switch (dev_id) {
|
|
case PMIC_SND_CAPTURE_VOL:
|
|
sc_access.reg_addr = 0x220;
|
|
mask = MASK0|MASK1|MASK2|MASK3|MASK4;
|
|
num_reg = 1;
|
|
break;
|
|
case PMIC_SND_LEFT_PB_VOL:
|
|
sc_access.reg_addr = VOL_CTRL_LT;
|
|
mask = MASK0|MASK1|MASK2|MASK3|MASK4|MASK5;
|
|
num_reg = 1;
|
|
break;
|
|
case PMIC_SND_RIGHT_PB_VOL:
|
|
sc_access.reg_addr = VOL_CTRL_RT;
|
|
mask = MASK0|MASK1|MASK2|MASK3|MASK4|MASK5;
|
|
num_reg = 1;
|
|
break;
|
|
}
|
|
retval = sst_sc_reg_access(&sc_access, PMIC_READ, num_reg);
|
|
if (retval)
|
|
return retval;
|
|
*value = -(sc_access.value & mask);
|
|
pr_debug("get volume value extracted %d\n", *value);
|
|
return retval;
|
|
}
|
|
|
|
static u8 mx_get_jack_status(void)
|
|
{
|
|
struct sc_reg_access sc_access_read = {0,};
|
|
|
|
sc_access_read.reg_addr = 0x201;
|
|
sst_sc_reg_access(&sc_access_read, PMIC_READ, 1);
|
|
pr_debug("value returned = 0x%x\n", sc_access_read.value);
|
|
return sc_access_read.value;
|
|
}
|
|
|
|
static void mx_pmic_irq_enable(void *data)
|
|
{
|
|
struct snd_intelmad *intelmaddata = data;
|
|
|
|
intelmaddata->jack_prev_state = 0xc0;
|
|
return;
|
|
}
|
|
|
|
static void mx_pmic_irq_cb(void *cb_data, u8 intsts)
|
|
{
|
|
u8 jack_cur_status, jack_prev_state = 0;
|
|
struct mad_jack *mjack = NULL;
|
|
unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0;
|
|
time_t timediff;
|
|
struct snd_intelmad *intelmaddata = cb_data;
|
|
|
|
mjack = &intelmaddata->jack[0];
|
|
if (intsts & 0x2) {
|
|
jack_cur_status = mx_get_jack_status();
|
|
jack_prev_state = intelmaddata->jack_prev_state;
|
|
if ((jack_prev_state == 0xc0) && (jack_cur_status == 0x40)) {
|
|
/*headset insert detected. */
|
|
pr_debug("MAD headset inserted\n");
|
|
present = 1;
|
|
jack_event_flag = 1;
|
|
mjack->jack_status = 1;
|
|
mjack->jack.type = SND_JACK_HEADSET;
|
|
}
|
|
|
|
if ((jack_prev_state == 0xc0) && (jack_cur_status == 0x00)) {
|
|
/* headphone insert detected. */
|
|
pr_debug("MAD headphone inserted\n");
|
|
present = 1;
|
|
jack_event_flag = 1;
|
|
mjack->jack.type = SND_JACK_HEADPHONE;
|
|
}
|
|
|
|
if ((jack_prev_state == 0x40) && (jack_cur_status == 0xc0)) {
|
|
/* headset remove detected. */
|
|
pr_debug("MAD headset removed\n");
|
|
|
|
present = 0;
|
|
jack_event_flag = 1;
|
|
mjack->jack_status = 0;
|
|
mjack->jack.type = SND_JACK_HEADSET;
|
|
}
|
|
|
|
if ((jack_prev_state == 0x00) && (jack_cur_status == 0xc0)) {
|
|
/* headphone remove detected. */
|
|
pr_debug("MAD headphone removed\n");
|
|
present = 0;
|
|
jack_event_flag = 1;
|
|
mjack->jack.type = SND_JACK_HEADPHONE;
|
|
}
|
|
|
|
if ((jack_prev_state == 0x40) && (jack_cur_status == 0x00)) {
|
|
/* button pressed */
|
|
do_gettimeofday(&mjack->buttonpressed);
|
|
pr_debug("MAD button press detected\n");
|
|
}
|
|
|
|
if ((jack_prev_state == 0x00) && (jack_cur_status == 0x40)) {
|
|
if (mjack->jack_status) {
|
|
/*button pressed */
|
|
do_gettimeofday(
|
|
&mjack->buttonreleased);
|
|
/*button pressed */
|
|
pr_debug("MAD Button Released detected\n");
|
|
timediff = mjack->buttonreleased.tv_sec -
|
|
mjack->buttonpressed.tv_sec;
|
|
buttonpressflag = 1;
|
|
|
|
if (timediff > 1) {
|
|
pr_debug("MAD long press dtd\n");
|
|
/* send headphone detect/undetect */
|
|
present = 1;
|
|
jack_event_flag = 1;
|
|
mjack->jack.type =
|
|
MID_JACK_HS_LONG_PRESS;
|
|
} else {
|
|
pr_debug("MAD short press dtd\n");
|
|
/* send headphone detect/undetect */
|
|
present = 1;
|
|
jack_event_flag = 1;
|
|
mjack->jack.type =
|
|
MID_JACK_HS_SHORT_PRESS;
|
|
}
|
|
} else {
|
|
/***workaround for maxim
|
|
hw issue,0x00 t 0x40 is not
|
|
a valid transiton for Headset insertion */
|
|
/*headset insert detected. */
|
|
pr_debug("MAD headset inserted\n");
|
|
present = 1;
|
|
jack_event_flag = 1;
|
|
mjack->jack_status = 1;
|
|
mjack->jack.type = SND_JACK_HEADSET;
|
|
}
|
|
}
|
|
intelmaddata->jack_prev_state = jack_cur_status;
|
|
pr_debug("mx_pmic_irq_cb prv_state= 0x%x\n",
|
|
intelmaddata->jack_prev_state);
|
|
}
|
|
|
|
if (jack_event_flag)
|
|
sst_mad_send_jack_report(&mjack->jack,
|
|
buttonpressflag, present);
|
|
}
|
|
static int mx_jack_enable(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
struct snd_pmic_ops snd_pmic_ops_mx = {
|
|
.set_input_dev = mx_set_selected_input_dev,
|
|
.set_output_dev = mx_set_selected_output_dev,
|
|
.set_mute = mx_set_mute,
|
|
.get_mute = mx_get_mute,
|
|
.set_vol = mx_set_vol,
|
|
.get_vol = mx_get_vol,
|
|
.init_card = mx_init_card,
|
|
.set_pcm_audio_params = mx_set_pcm_audio_params,
|
|
.set_pcm_voice_params = mx_set_pcm_voice_params,
|
|
.set_voice_port = mx_set_voice_port,
|
|
.set_audio_port = mx_set_audio_port,
|
|
.power_up_pmic_pb = mx_power_up_pb,
|
|
.power_up_pmic_cp = mx_power_up_cp,
|
|
.power_down_pmic_pb = mx_power_down_pb,
|
|
.power_down_pmic_cp = mx_power_down_cp,
|
|
.power_down_pmic = mx_power_down,
|
|
.pmic_irq_cb = mx_pmic_irq_cb,
|
|
.pmic_irq_enable = mx_pmic_irq_enable,
|
|
.pmic_jack_enable = mx_jack_enable,
|
|
};
|
|
|