linux-stable/drivers/staging/intel_sst/intelmid_msic_control.c
Ramesh Babu K V 8a251ff2fc intel_sst: rework jack implementation
This patch fixes the below issues w.r.t jack implementation

a) The current jack implementation in driver is implemented
   in intelmid.c. It has moved to vendor files for better managebility
b) Cleaned up jack reporting per upstream comments
c) Implemented jack for msic, added code to read adc and deduce jack
   type based on mic bias
d) Support detection of american headset

Signed-off-by: Dharageswari R <dharageswari.r@intel.com>
Signed-off-by: Ramesh Babu K V <ramesh.babu@intel.com>
[Corrections]
Signed-off-by: Lu Guanqun <guanqun.lu@intel.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2011-05-10 12:58:09 -07:00

1047 lines
25 KiB
C

/*
* intelmid_vm_control.c - Intel Sound card driver for MID
*
* Copyright (C) 2010 Intel Corp
* Authors: Vinod Koul <vinod.koul@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 msic vendors
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
#include <linux/file.h>
#include <linux/delay.h>
#include <sound/control.h>
#include "intel_sst.h"
#include <linux/input.h>
#include "intelmid_snd_control.h"
#include "intelmid.h"
#define AUDIOMUX12 0x24c
#define AUDIOMUX34 0x24d
static int msic_init_card(void)
{
struct sc_reg_access sc_access[] = {
/* dmic configuration */
{0x241, 0x85, 0},
{0x242, 0x02, 0},
/* audio paths config */
{0x24C, 0x10, 0},
{0x24D, 0x32, 0},
/* PCM2 interface slots */
/* preconfigured slots for 0-5 both tx, rx */
{0x272, 0x10, 0},
{0x273, 0x32, 0},
{0x274, 0xFF, 0},
{0x275, 0x10, 0},
{0x276, 0x32, 0},
{0x277, 0x54, 0},
/*Sinc5 decimator*/
{0x24E, 0x28, 0},
/*TI vibra w/a settings*/
{0x384, 0x80, 0},
{0x385, 0x80, 0},
{0x267, 0x00, 0},
{0x261, 0x00, 0},
/* pcm port setting */
{0x278, 0x00, 0},
{0x27B, 0x01, 0},
{0x27C, 0x0a, 0},
/* Set vol HSLRVOLCTRL, IHFVOL */
{0x259, 0x08, 0},
{0x25A, 0x08, 0},
{0x25B, 0x08, 0},
{0x25C, 0x08, 0},
/* HSEPRXCTRL Enable the headset left and right FIR filters */
{0x250, 0x30, 0},
/* HSMIXER */
{0x256, 0x11, 0},
/* amic configuration */
{0x249, 0x01, 0x0},
{0x24A, 0x01, 0x0},
/* unmask ocaudio/accdet interrupts */
{0x1d, 0x00, 0x00},
{0x1e, 0x00, 0x00},
};
snd_msic_ops.card_status = SND_CARD_INIT_DONE;
sst_sc_reg_access(sc_access, PMIC_WRITE, 28);
snd_msic_ops.pb_on = 0;
snd_msic_ops.pbhs_on = 0;
snd_msic_ops.cap_on = 0;
snd_msic_ops.input_dev_id = DMIC; /*def dev*/
snd_msic_ops.output_dev_id = STEREO_HEADPHONE;
snd_msic_ops.jack_interrupt_status = false;
pr_debug("msic init complete!!\n");
return 0;
}
static int msic_line_out_restore(u8 value)
{
struct sc_reg_access hs_drv_en[] = {
{0x25d, 0x03, 0x03},
};
struct sc_reg_access ep_drv_en[] = {
{0x25d, 0x40, 0x40},
};
struct sc_reg_access ihf_drv_en[] = {
{0x25d, 0x0c, 0x0c},
};
struct sc_reg_access vib1_drv_en[] = {
{0x25d, 0x10, 0x10},
};
struct sc_reg_access vib2_drv_en[] = {
{0x25d, 0x20, 0x20},
};
struct sc_reg_access pmode_enable[] = {
{0x381, 0x10, 0x10},
};
int retval = 0;
pr_debug("msic_lineout_restore_lineout_dev:%d\n", value);
switch (value) {
case HEADSET:
pr_debug("Selecting Lineout-HEADSET-restore\n");
if (snd_msic_ops.output_dev_id == STEREO_HEADPHONE)
retval = sst_sc_reg_access(hs_drv_en,
PMIC_READ_MODIFY, 1);
else
retval = sst_sc_reg_access(ep_drv_en,
PMIC_READ_MODIFY, 1);
break;
case IHF:
pr_debug("Selecting Lineout-IHF-restore\n");
retval = sst_sc_reg_access(ihf_drv_en, PMIC_READ_MODIFY, 1);
if (retval)
return retval;
retval = sst_sc_reg_access(pmode_enable, PMIC_READ_MODIFY, 1);
break;
case VIBRA1:
pr_debug("Selecting Lineout-Vibra1-restore\n");
retval = sst_sc_reg_access(vib1_drv_en, PMIC_READ_MODIFY, 1);
break;
case VIBRA2:
pr_debug("Selecting Lineout-VIBRA2-restore\n");
retval = sst_sc_reg_access(vib2_drv_en, PMIC_READ_MODIFY, 1);
break;
case NONE:
pr_debug("Selecting Lineout-NONE-restore\n");
break;
default:
return -EINVAL;
}
return retval;
}
static int msic_get_lineout_prvstate(void)
{
struct sc_reg_access hs_ihf_drv[2] = {
{0x257, 0x0, 0x0},
{0x25d, 0x0, 0x0},
};
struct sc_reg_access vib1drv[2] = {
{0x264, 0x0, 0x0},
{0x25D, 0x0, 0x0},
};
struct sc_reg_access vib2drv[2] = {
{0x26A, 0x0, 0x0},
{0x25D, 0x0, 0x0},
};
int retval = 0, drv_en, dac_en, dev_id, mask;
for (dev_id = 0; dev_id < snd_msic_ops.line_out_names_cnt; dev_id++) {
switch (dev_id) {
case HEADSET:
pr_debug("msic_get_lineout_prvs_state: HEADSET\n");
sst_sc_reg_access(hs_ihf_drv, PMIC_READ, 2);
mask = (MASK0|MASK1);
dac_en = (hs_ihf_drv[0].value) & mask;
mask = ((MASK0|MASK1)|MASK6);
drv_en = (hs_ihf_drv[1].value) & mask;
if (dac_en && (!drv_en)) {
snd_msic_ops.prev_lineout_dev_id = HEADSET;
return retval;
}
break;
case IHF:
pr_debug("msic_get_lineout_prvstate: IHF\n");
sst_sc_reg_access(hs_ihf_drv, PMIC_READ, 2);
mask = (MASK2 | MASK3);
dac_en = (hs_ihf_drv[0].value) & mask;
mask = (MASK2 | MASK3);
drv_en = (hs_ihf_drv[1].value) & mask;
if (dac_en && (!drv_en)) {
snd_msic_ops.prev_lineout_dev_id = IHF;
return retval;
}
break;
case VIBRA1:
pr_debug("msic_get_lineout_prvstate: vibra1\n");
sst_sc_reg_access(vib1drv, PMIC_READ, 2);
mask = MASK1;
dac_en = (vib1drv[0].value) & mask;
mask = MASK4;
drv_en = (vib1drv[1].value) & mask;
if (dac_en && (!drv_en)) {
snd_msic_ops.prev_lineout_dev_id = VIBRA1;
return retval;
}
break;
case VIBRA2:
pr_debug("msic_get_lineout_prvstate: vibra2\n");
sst_sc_reg_access(vib2drv, PMIC_READ, 2);
mask = MASK1;
dac_en = (vib2drv[0].value) & mask;
mask = MASK5;
drv_en = ((vib2drv[1].value) & mask);
if (dac_en && (!drv_en)) {
snd_msic_ops.prev_lineout_dev_id = VIBRA2;
return retval;
}
break;
case NONE:
pr_debug("msic_get_lineout_prvstate: NONE\n");
snd_msic_ops.prev_lineout_dev_id = NONE;
return retval;
default:
pr_debug("Invalid device id\n");
snd_msic_ops.prev_lineout_dev_id = NONE;
return -EINVAL;
}
}
return retval;
}
static int msic_set_selected_lineout_dev(u8 value)
{
struct sc_reg_access lout_hs[] = {
{0x25e, 0x33, 0xFF},
{0x25d, 0x0, 0x43},
};
struct sc_reg_access lout_ihf[] = {
{0x25e, 0x55, 0xff},
{0x25d, 0x0, 0x0c},
};
struct sc_reg_access lout_vibra1[] = {
{0x25e, 0x61, 0xff},
{0x25d, 0x0, 0x10},
};
struct sc_reg_access lout_vibra2[] = {
{0x25e, 0x16, 0xff},
{0x25d, 0x0, 0x20},
};
struct sc_reg_access lout_def[] = {
{0x25e, 0x66, 0x0},
};
struct sc_reg_access pmode_disable[] = {
{0x381, 0x00, 0x10},
};
struct sc_reg_access pmode_enable[] = {
{0x381, 0x10, 0x10},
};
int retval = 0;
pr_debug("msic_set_selected_lineout_dev:%d\n", value);
msic_get_lineout_prvstate();
msic_line_out_restore(snd_msic_ops.prev_lineout_dev_id);
snd_msic_ops.lineout_dev_id = value;
switch (value) {
case HEADSET:
pr_debug("Selecting Lineout-HEADSET\n");
if (snd_msic_ops.pb_on)
retval = sst_sc_reg_access(lout_hs,
PMIC_READ_MODIFY, 2);
if (retval)
return retval;
retval = sst_sc_reg_access(pmode_disable,
PMIC_READ_MODIFY, 1);
break;
case IHF:
pr_debug("Selecting Lineout-IHF\n");
if (snd_msic_ops.pb_on)
retval = sst_sc_reg_access(lout_ihf,
PMIC_READ_MODIFY, 2);
if (retval)
return retval;
retval = sst_sc_reg_access(pmode_enable,
PMIC_READ_MODIFY, 1);
break;
case VIBRA1:
pr_debug("Selecting Lineout-Vibra1\n");
if (snd_msic_ops.pb_on)
retval = sst_sc_reg_access(lout_vibra1,
PMIC_READ_MODIFY, 2);
if (retval)
return retval;
retval = sst_sc_reg_access(pmode_disable,
PMIC_READ_MODIFY, 1);
break;
case VIBRA2:
pr_debug("Selecting Lineout-VIBRA2\n");
if (snd_msic_ops.pb_on)
retval = sst_sc_reg_access(lout_vibra2,
PMIC_READ_MODIFY, 2);
if (retval)
return retval;
retval = sst_sc_reg_access(pmode_disable,
PMIC_READ_MODIFY, 1);
break;
case NONE:
pr_debug("Selecting Lineout-NONE\n");
retval = sst_sc_reg_access(lout_def,
PMIC_WRITE, 1);
if (retval)
return retval;
retval = sst_sc_reg_access(pmode_disable,
PMIC_READ_MODIFY, 1);
break;
default:
return -EINVAL;
}
return retval;
}
static int msic_power_up_pb(unsigned int device)
{
struct sc_reg_access vaud[] = {
/* turn on the audio power supplies */
{0x0DB, 0x07, 0},
};
struct sc_reg_access pll[] = {
/* turn on PLL */
{0x240, 0x20, 0},
};
struct sc_reg_access vhs[] = {
/* VHSP */
{0x0DC, 0x3D, 0},
/* VHSN */
{0x0DD, 0x3F, 0},
};
struct sc_reg_access hsdac[] = {
{0x382, 0x40, 0x40},
/* disable driver */
{0x25D, 0x0, 0x43},
/* DAC CONFIG ; both HP, LP on */
{0x257, 0x03, 0x03},
};
struct sc_reg_access hs_filter[] = {
/* HSEPRXCTRL Enable the headset left and right FIR filters */
{0x250, 0x30, 0},
/* HSMIXER */
{0x256, 0x11, 0},
};
struct sc_reg_access hs_enable[] = {
/* enable driver */
{0x25D, 0x3, 0x3},
{0x26C, 0x0, 0x2},
/* unmute the headset */
{ 0x259, 0x80, 0x80},
{ 0x25A, 0x80, 0x80},
};
struct sc_reg_access vihf[] = {
/* VIHF ON */
{0x0C9, 0x27, 0x00},
};
struct sc_reg_access ihf_filter[] = {
/* disable driver */
{0x25D, 0x00, 0x0C},
/*Filer DAC enable*/
{0x251, 0x03, 0x03},
{0x257, 0x0C, 0x0C},
};
struct sc_reg_access ihf_en[] = {
/*enable drv*/
{0x25D, 0x0C, 0x0c},
};
struct sc_reg_access ihf_unmute[] = {
/*unmute headset*/
{0x25B, 0x80, 0x80},
{0x25C, 0x80, 0x80},
};
struct sc_reg_access epdac[] = {
/* disable driver */
{0x25D, 0x0, 0x43},
/* DAC CONFIG ; both HP, LP on */
{0x257, 0x03, 0x03},
};
struct sc_reg_access ep_enable[] = {
/* enable driver */
{0x25D, 0x40, 0x40},
/* unmute the headset */
{ 0x259, 0x80, 0x80},
{ 0x25A, 0x80, 0x80},
};
struct sc_reg_access vib1_en[] = {
/* enable driver, ADC */
{0x25D, 0x10, 0x10},
{0x264, 0x02, 0x82},
};
struct sc_reg_access vib2_en[] = {
/* enable driver, ADC */
{0x25D, 0x20, 0x20},
{0x26A, 0x02, 0x82},
};
struct sc_reg_access pcm2_en[] = {
/* enable pcm 2 */
{0x27C, 0x1, 0x1},
};
int retval = 0;
if (snd_msic_ops.card_status == SND_CARD_UN_INIT) {
retval = msic_init_card();
if (retval)
return retval;
}
pr_debug("powering up pb.... Device %d\n", device);
sst_sc_reg_access(vaud, PMIC_WRITE, 1);
msleep(1);
sst_sc_reg_access(pll, PMIC_WRITE, 1);
msleep(1);
switch (device) {
case SND_SST_DEVICE_HEADSET:
snd_msic_ops.pb_on = 1;
snd_msic_ops.pbhs_on = 1;
if (snd_msic_ops.output_dev_id == STEREO_HEADPHONE) {
sst_sc_reg_access(vhs, PMIC_WRITE, 2);
sst_sc_reg_access(hsdac, PMIC_READ_MODIFY, 3);
sst_sc_reg_access(hs_filter, PMIC_WRITE, 2);
sst_sc_reg_access(hs_enable, PMIC_READ_MODIFY, 4);
} else {
sst_sc_reg_access(epdac, PMIC_READ_MODIFY, 2);
sst_sc_reg_access(hs_filter, PMIC_WRITE, 2);
sst_sc_reg_access(ep_enable, PMIC_READ_MODIFY, 3);
}
if (snd_msic_ops.lineout_dev_id == HEADSET)
msic_set_selected_lineout_dev(HEADSET);
break;
case SND_SST_DEVICE_IHF:
snd_msic_ops.pb_on = 1;
sst_sc_reg_access(vihf, PMIC_WRITE, 1);
sst_sc_reg_access(ihf_filter, PMIC_READ_MODIFY, 3);
sst_sc_reg_access(ihf_en, PMIC_READ_MODIFY, 1);
sst_sc_reg_access(ihf_unmute, PMIC_READ_MODIFY, 2);
if (snd_msic_ops.lineout_dev_id == IHF)
msic_set_selected_lineout_dev(IHF);
break;
case SND_SST_DEVICE_VIBRA:
snd_msic_ops.pb_on = 1;
sst_sc_reg_access(vib1_en, PMIC_READ_MODIFY, 2);
if (snd_msic_ops.lineout_dev_id == VIBRA1)
msic_set_selected_lineout_dev(VIBRA1);
break;
case SND_SST_DEVICE_HAPTIC:
snd_msic_ops.pb_on = 1;
sst_sc_reg_access(vib2_en, PMIC_READ_MODIFY, 2);
if (snd_msic_ops.lineout_dev_id == VIBRA2)
msic_set_selected_lineout_dev(VIBRA2);
break;
default:
pr_warn("Wrong Device %d, selected %d\n",
device, snd_msic_ops.output_dev_id);
}
return sst_sc_reg_access(pcm2_en, PMIC_READ_MODIFY, 1);
}
static int msic_power_up_cp(unsigned int device)
{
struct sc_reg_access vaud[] = {
/* turn on the audio power supplies */
{0x0DB, 0x07, 0},
};
struct sc_reg_access pll[] = {
/* turn on PLL */
{0x240, 0x20, 0},
};
struct sc_reg_access dmic_bias[] = {
/* Turn on AMIC supply */
{0x247, 0xA0, 0xA0},
};
struct sc_reg_access dmic[] = {
/* mic demux enable */
{0x245, 0x3F, 0x3F},
{0x246, 0x07, 0x07},
};
struct sc_reg_access amic_bias[] = {
/* Turn on AMIC supply */
{0x247, 0xFC, 0xFC},
};
struct sc_reg_access amic[] = {
/*MIC EN*/
{0x249, 0x01, 0x01},
{0x24A, 0x01, 0x01},
/*ADC EN*/
{0x248, 0x05, 0x0F},
};
struct sc_reg_access pcm2[] = {
/* enable pcm 2 */
{0x27C, 0x1, 0x1},
};
struct sc_reg_access tx_on[] = {
/*wait for mic to stabalize before turning on audio channels*/
{0x24F, 0x3C, 0x0},
};
int retval = 0;
if (snd_msic_ops.card_status == SND_CARD_UN_INIT) {
retval = msic_init_card();
if (retval)
return retval;
}
pr_debug("powering up cp....%d\n", snd_msic_ops.input_dev_id);
sst_sc_reg_access(vaud, PMIC_WRITE, 1);
msleep(500);/*FIXME need optimzed value here*/
sst_sc_reg_access(pll, PMIC_WRITE, 1);
msleep(1);
snd_msic_ops.cap_on = 1;
if (snd_msic_ops.input_dev_id == AMIC) {
sst_sc_reg_access(amic_bias, PMIC_READ_MODIFY, 1);
msleep(1);
sst_sc_reg_access(amic, PMIC_READ_MODIFY, 3);
} else {
sst_sc_reg_access(dmic_bias, PMIC_READ_MODIFY, 1);
msleep(1);
sst_sc_reg_access(dmic, PMIC_READ_MODIFY, 2);
}
msleep(1);
sst_sc_reg_access(tx_on, PMIC_WRITE, 1);
return sst_sc_reg_access(pcm2, PMIC_READ_MODIFY, 1);
}
static int msic_power_down(void)
{
struct sc_reg_access power_dn[] = {
/* VHSP */
{0x0DC, 0xC4, 0},
/* VHSN */
{0x0DD, 0x04, 0},
/* VIHF */
{0x0C9, 0x24, 0},
};
struct sc_reg_access pll[] = {
/* turn off PLL*/
{0x240, 0x00, 0x0},
};
struct sc_reg_access vaud[] = {
/* turn off VAUD*/
{0x0DB, 0x04, 0},
};
pr_debug("powering dn msic\n");
snd_msic_ops.pbhs_on = 0;
snd_msic_ops.pb_on = 0;
snd_msic_ops.cap_on = 0;
sst_sc_reg_access(power_dn, PMIC_WRITE, 3);
msleep(1);
sst_sc_reg_access(pll, PMIC_WRITE, 1);
msleep(1);
sst_sc_reg_access(vaud, PMIC_WRITE, 1);
return 0;
}
static int msic_power_down_pb(unsigned int device)
{
struct sc_reg_access drv_enable[] = {
{0x25D, 0x00, 0x00},
};
struct sc_reg_access hs_mute[] = {
{0x259, 0x80, 0x80},
{0x25A, 0x80, 0x80},
{0x26C, 0x02, 0x02},
};
struct sc_reg_access hs_off[] = {
{0x257, 0x00, 0x03},
{0x250, 0x00, 0x30},
{0x382, 0x00, 0x40},
};
struct sc_reg_access ihf_mute[] = {
{0x25B, 0x80, 0x80},
{0x25C, 0x80, 0x80},
};
struct sc_reg_access ihf_off[] = {
{0x257, 0x00, 0x0C},
{0x251, 0x00, 0x03},
};
struct sc_reg_access vib1_off[] = {
{0x264, 0x00, 0x82},
};
struct sc_reg_access vib2_off[] = {
{0x26A, 0x00, 0x82},
};
struct sc_reg_access lout_off[] = {
{0x25e, 0x66, 0x00},
};
struct sc_reg_access pmode_disable[] = {
{0x381, 0x00, 0x10},
};
pr_debug("powering dn pb for device %d\n", device);
switch (device) {
case SND_SST_DEVICE_HEADSET:
snd_msic_ops.pbhs_on = 0;
sst_sc_reg_access(hs_mute, PMIC_READ_MODIFY, 3);
drv_enable[0].mask = 0x43;
sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1);
sst_sc_reg_access(hs_off, PMIC_READ_MODIFY, 3);
if (snd_msic_ops.lineout_dev_id == HEADSET)
sst_sc_reg_access(lout_off, PMIC_WRITE, 1);
break;
case SND_SST_DEVICE_IHF:
sst_sc_reg_access(ihf_mute, PMIC_READ_MODIFY, 2);
drv_enable[0].mask = 0x0C;
sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1);
sst_sc_reg_access(ihf_off, PMIC_READ_MODIFY, 2);
if (snd_msic_ops.lineout_dev_id == IHF) {
sst_sc_reg_access(lout_off, PMIC_WRITE, 1);
sst_sc_reg_access(pmode_disable, PMIC_READ_MODIFY, 1);
}
break;
case SND_SST_DEVICE_VIBRA:
sst_sc_reg_access(vib1_off, PMIC_READ_MODIFY, 1);
drv_enable[0].mask = 0x10;
sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1);
if (snd_msic_ops.lineout_dev_id == VIBRA1)
sst_sc_reg_access(lout_off, PMIC_WRITE, 1);
break;
case SND_SST_DEVICE_HAPTIC:
sst_sc_reg_access(vib2_off, PMIC_READ_MODIFY, 1);
drv_enable[0].mask = 0x20;
sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1);
if (snd_msic_ops.lineout_dev_id == VIBRA2)
sst_sc_reg_access(lout_off, PMIC_WRITE, 1);
break;
}
return 0;
}
static int msic_power_down_cp(unsigned int device)
{
struct sc_reg_access dmic[] = {
{0x247, 0x00, 0xA0},
{0x245, 0x00, 0x38},
{0x246, 0x00, 0x07},
};
struct sc_reg_access amic[] = {
{0x248, 0x00, 0x05},
{0x249, 0x00, 0x01},
{0x24A, 0x00, 0x01},
{0x247, 0x00, 0xA3},
};
struct sc_reg_access tx_off[] = {
{0x24F, 0x00, 0x3C},
};
pr_debug("powering dn cp....\n");
snd_msic_ops.cap_on = 0;
sst_sc_reg_access(tx_off, PMIC_READ_MODIFY, 1);
if (snd_msic_ops.input_dev_id == DMIC)
sst_sc_reg_access(dmic, PMIC_READ_MODIFY, 3);
else
sst_sc_reg_access(amic, PMIC_READ_MODIFY, 4);
return 0;
}
static int msic_set_selected_output_dev(u8 value)
{
int retval = 0;
pr_debug("msic set selected output:%d\n", value);
snd_msic_ops.output_dev_id = value;
if (snd_msic_ops.pbhs_on)
msic_power_up_pb(SND_SST_DEVICE_HEADSET);
return retval;
}
static int msic_set_selected_input_dev(u8 value)
{
struct sc_reg_access sc_access_dmic[] = {
{0x24C, 0x10, 0x0},
};
struct sc_reg_access sc_access_amic[] = {
{0x24C, 0x76, 0x0},
};
int retval = 0;
pr_debug("msic_set_selected_input_dev:%d\n", value);
snd_msic_ops.input_dev_id = value;
switch (value) {
case AMIC:
pr_debug("Selecting AMIC1\n");
retval = sst_sc_reg_access(sc_access_amic, PMIC_WRITE, 1);
break;
case DMIC:
pr_debug("Selecting DMIC1\n");
retval = sst_sc_reg_access(sc_access_dmic, PMIC_WRITE, 1);
break;
default:
return -EINVAL;
}
if (snd_msic_ops.cap_on)
retval = msic_power_up_cp(SND_SST_DEVICE_CAPTURE);
return retval;
}
static int msic_set_hw_dmic_route(u8 hw_ch_index)
{
struct sc_reg_access sc_access_router;
int retval = -EINVAL;
switch (hw_ch_index) {
case HW_CH0:
sc_access_router.reg_addr = AUDIOMUX12;
sc_access_router.value = snd_msic_ops.hw_dmic_map[0];
sc_access_router.mask = (MASK2 | MASK1 | MASK0);
pr_debug("hw_ch0. value = 0x%x\n",
sc_access_router.value);
retval = sst_sc_reg_access(&sc_access_router,
PMIC_READ_MODIFY, 1);
break;
case HW_CH1:
sc_access_router.reg_addr = AUDIOMUX12;
sc_access_router.value = (snd_msic_ops.hw_dmic_map[1]) << 4;
sc_access_router.mask = (MASK6 | MASK5 | MASK4);
pr_debug("### hw_ch1. value = 0x%x\n",
sc_access_router.value);
retval = sst_sc_reg_access(&sc_access_router,
PMIC_READ_MODIFY, 1);
break;
case HW_CH2:
sc_access_router.reg_addr = AUDIOMUX34;
sc_access_router.value = snd_msic_ops.hw_dmic_map[2];
sc_access_router.mask = (MASK2 | MASK1 | MASK0);
pr_debug("hw_ch2. value = 0x%x\n",
sc_access_router.value);
retval = sst_sc_reg_access(&sc_access_router,
PMIC_READ_MODIFY, 1);
break;
case HW_CH3:
sc_access_router.reg_addr = AUDIOMUX34;
sc_access_router.value = (snd_msic_ops.hw_dmic_map[3]) << 4;
sc_access_router.mask = (MASK6 | MASK5 | MASK4);
pr_debug("hw_ch3. value = 0x%x\n",
sc_access_router.value);
retval = sst_sc_reg_access(&sc_access_router,
PMIC_READ_MODIFY, 1);
break;
}
return retval;
}
static int msic_set_pcm_voice_params(void)
{
return 0;
}
static int msic_set_pcm_audio_params(int sfreq, int word_size, int num_channel)
{
return 0;
}
static int msic_set_audio_port(int status)
{
return 0;
}
static int msic_set_voice_port(int status)
{
return 0;
}
static int msic_set_mute(int dev_id, u8 value)
{
return 0;
}
static int msic_set_vol(int dev_id, int value)
{
return 0;
}
static int msic_get_mute(int dev_id, u8 *value)
{
return 0;
}
static int msic_get_vol(int dev_id, int *value)
{
return 0;
}
static int msic_set_headset_state(int state)
{
struct sc_reg_access hs_enable[] = {
{0x25D, 0x03, 0x03},
};
if (state)
/*enable*/
sst_sc_reg_access(hs_enable, PMIC_READ_MODIFY, 1);
else {
hs_enable[0].value = 0;
sst_sc_reg_access(hs_enable, PMIC_READ_MODIFY, 1);
}
return 0;
}
static int msic_enable_mic_bias(void)
{
struct sc_reg_access jack_interrupt_reg[] = {
{0x0DB, 0x07, 0x00},
};
struct sc_reg_access jack_bias_reg[] = {
{0x247, 0x0C, 0x0C},
};
sst_sc_reg_access(jack_interrupt_reg, PMIC_WRITE, 1);
sst_sc_reg_access(jack_bias_reg, PMIC_READ_MODIFY, 1);
return 0;
}
static int msic_disable_mic_bias(void)
{
if (snd_msic_ops.jack_interrupt_status == true)
return 0;
if (!(snd_msic_ops.pb_on || snd_msic_ops.cap_on))
msic_power_down();
return 0;
}
static int msic_disable_jack_btn(void)
{
struct sc_reg_access btn_disable[] = {
{0x26C, 0x00, 0x01}
};
if (!(snd_msic_ops.pb_on || snd_msic_ops.cap_on))
msic_power_down();
snd_msic_ops.jack_interrupt_status = false;
return sst_sc_reg_access(btn_disable, PMIC_READ_MODIFY, 1);
}
static int msic_enable_jack_btn(void)
{
struct sc_reg_access btn_enable[] = {
{0x26b, 0x77, 0x00},
{0x26C, 0x01, 0x00},
};
return sst_sc_reg_access(btn_enable, PMIC_WRITE, 2);
}
static int msic_convert_adc_to_mvolt(unsigned int mic_bias)
{
return (ADC_ONE_LSB_MULTIPLIER * mic_bias) / 1000;
}
int msic_get_headset_state(int mic_bias)
{
struct sc_reg_access msic_hs_toggle[] = {
{0x070, 0x00, 0x01},
};
if (mic_bias >= 0 && mic_bias < 400) {
pr_debug("Detected Headphone!!!\n");
sst_sc_reg_access(msic_hs_toggle, PMIC_READ_MODIFY, 1);
} else if (mic_bias > 400 && mic_bias < 650) {
pr_debug("Detected American headset\n");
msic_hs_toggle[0].value = 0x01;
sst_sc_reg_access(msic_hs_toggle, PMIC_READ_MODIFY, 1);
} else if (mic_bias >= 650 && mic_bias < 2000) {
pr_debug("Detected Headset!!!\n");
sst_sc_reg_access(msic_hs_toggle, PMIC_READ_MODIFY, 1);
/*power on jack and btn*/
snd_msic_ops.jack_interrupt_status = true;
msic_enable_jack_btn();
msic_enable_mic_bias();
return SND_JACK_HEADSET;
} else
pr_debug("Detected Open Cable!!!\n");
return SND_JACK_HEADPHONE;
}
static int msic_get_mic_bias(void *arg)
{
struct snd_intelmad *intelmad_drv = (struct snd_intelmad *)arg;
u16 adc_adr = intelmad_drv->adc_address;
u16 adc_val;
int ret;
struct sc_reg_access adc_ctrl3[2] = {
{0x1C2, 0x05, 0x0},
};
struct sc_reg_access audio_adc_reg1 = {0,};
struct sc_reg_access audio_adc_reg2 = {0,};
msic_enable_mic_bias();
/* Enable the msic for conversion before reading */
ret = sst_sc_reg_access(adc_ctrl3, PMIC_WRITE, 1);
if (ret)
return ret;
adc_ctrl3[0].value = 0x04;
/* Re-toggle the RRDATARD bit */
ret = sst_sc_reg_access(adc_ctrl3, PMIC_WRITE, 1);
if (ret)
return ret;
audio_adc_reg1.reg_addr = adc_adr;
/* Read the higher bits of data */
msleep(1000);
ret = sst_sc_reg_access(&audio_adc_reg1, PMIC_READ, 1);
if (ret)
return ret;
pr_debug("adc read value %x", audio_adc_reg1.value);
/* Shift bits to accomodate the lower two data bits */
adc_val = (audio_adc_reg1.value << 2);
adc_adr++;
audio_adc_reg2. reg_addr = adc_adr;
ret = sst_sc_reg_access(&audio_adc_reg2, PMIC_READ, 1);
if (ret)
return ret;
pr_debug("adc read value %x", audio_adc_reg2.value);
/* Adding lower two bits to the higher bits */
audio_adc_reg2.value &= 03;
adc_val += audio_adc_reg2.value;
pr_debug("ADC value 0x%x", adc_val);
msic_disable_mic_bias();
return adc_val;
}
static void msic_pmic_irq_cb(void *cb_data, u8 intsts)
{
struct mad_jack *mjack = NULL;
unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0;
struct snd_intelmad *intelmaddata = cb_data;
int retval = 0;
pr_debug("value returned = 0x%x\n", intsts);
if (snd_msic_ops.card_status == SND_CARD_UN_INIT) {
retval = msic_init_card();
if (retval)
return;
}
mjack = &intelmaddata->jack[0];
if (intsts & 0x1) {
pr_debug("MAD short_push detected\n");
present = SND_JACK_BTN_0;
jack_event_flag = buttonpressflag = 1;
mjack->jack.type = SND_JACK_BTN_0;
mjack->jack.key[0] = BTN_0 ;
}
if (intsts & 0x2) {
pr_debug(":MAD long_push detected\n");
jack_event_flag = buttonpressflag = 1;
mjack->jack.type = present = SND_JACK_BTN_1;
mjack->jack.key[1] = BTN_1;
}
if (intsts & 0x4) {
unsigned int mic_bias;
jack_event_flag = 1;
buttonpressflag = 0;
mic_bias = msic_get_mic_bias(intelmaddata);
pr_debug("mic_bias = %d\n", mic_bias);
mic_bias = msic_convert_adc_to_mvolt(mic_bias);
pr_debug("mic_bias after conversion = %d mV\n", mic_bias);
mjack->jack_dev_state = msic_get_headset_state(mic_bias);
mjack->jack.type = present = mjack->jack_dev_state;
}
if (intsts & 0x8) {
mjack->jack.type = mjack->jack_dev_state;
present = 0;
jack_event_flag = 1;
buttonpressflag = 0;
msic_disable_jack_btn();
msic_disable_mic_bias();
}
if (jack_event_flag)
sst_mad_send_jack_report(&mjack->jack,
buttonpressflag, present);
}
struct snd_pmic_ops snd_msic_ops = {
.set_input_dev = msic_set_selected_input_dev,
.set_output_dev = msic_set_selected_output_dev,
.set_lineout_dev = msic_set_selected_lineout_dev,
.set_hw_dmic_route = msic_set_hw_dmic_route,
.set_mute = msic_set_mute,
.get_mute = msic_get_mute,
.set_vol = msic_set_vol,
.get_vol = msic_get_vol,
.init_card = msic_init_card,
.set_pcm_audio_params = msic_set_pcm_audio_params,
.set_pcm_voice_params = msic_set_pcm_voice_params,
.set_voice_port = msic_set_voice_port,
.set_audio_port = msic_set_audio_port,
.power_up_pmic_pb = msic_power_up_pb,
.power_up_pmic_cp = msic_power_up_cp,
.power_down_pmic_pb = msic_power_down_pb,
.power_down_pmic_cp = msic_power_down_cp,
.power_down_pmic = msic_power_down,
.pmic_irq_cb = msic_pmic_irq_cb,
.pmic_jack_enable = msic_enable_mic_bias,
.pmic_get_mic_bias = msic_get_mic_bias,
.pmic_set_headset_state = msic_set_headset_state,
};