diff options
Diffstat (limited to 'drivers/staging/intel_sst/intelmid_msic_control.c')
-rw-r--r-- | drivers/staging/intel_sst/intelmid_msic_control.c | 833 |
1 files changed, 734 insertions, 99 deletions
diff --git a/drivers/staging/intel_sst/intelmid_msic_control.c b/drivers/staging/intel_sst/intelmid_msic_control.c index da093ed..70cdb16 100644 --- a/drivers/staging/intel_sst/intelmid_msic_control.c +++ b/drivers/staging/intel_sst/intelmid_msic_control.c @@ -28,9 +28,15 @@ #include <linux/pci.h> #include <linux/file.h> +#include <linux/delay.h> +#include <sound/control.h> #include "intel_sst.h" -#include "intel_sst_ioctl.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) { @@ -54,116 +60,359 @@ static int msic_init_card(void) /*TI vibra w/a settings*/ {0x384, 0x80, 0}, {0x385, 0x80, 0}, - /*vibra settings*/ {0x267, 0x00, 0}, - {0x26A, 0x10, 0}, {0x261, 0x00, 0}, - {0x264, 0x10, 0}, /* pcm port setting */ {0x278, 0x00, 0}, {0x27B, 0x01, 0}, {0x27C, 0x0a, 0}, /* Set vol HSLRVOLCTRL, IHFVOL */ - {0x259, 0x04, 0}, - {0x25A, 0x04, 0}, - {0x25B, 0x04, 0}, - {0x25C, 0x04, 0}, + {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, 0x09, 0x0}, - {0x24A, 0x09, 0x0}, + {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, 30); + 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 sc_access1[] = { + struct sc_reg_access vaud[] = { /* turn on the audio power supplies */ - {0x0DB, 0x05, 0}, + {0x0DB, 0x07, 0}, + }; + struct sc_reg_access pll[] = { + /* turn on PLL */ + {0x240, 0x20, 0}, + }; + struct sc_reg_access vhs[] = { /* VHSP */ - {0x0DC, 0xFF, 0}, + {0x0DC, 0x3D, 0}, /* VHSN */ {0x0DD, 0x3F, 0}, - /* turn on PLL */ - {0x240, 0x21, 0}, }; - struct sc_reg_access sc_access2[] = { + 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 sc_access3[] = { + 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 sc_access4[] = { + 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 sc_access_vihf[] = { + struct sc_reg_access vihf[] = { /* VIHF ON */ - {0x0C9, 0x2D, 0x00}, + {0x0C9, 0x27, 0x00}, }; - struct sc_reg_access sc_access22[] = { + struct sc_reg_access ihf_filter[] = { /* disable driver */ {0x25D, 0x00, 0x0C}, /*Filer DAC enable*/ {0x251, 0x03, 0x03}, {0x257, 0x0C, 0x0C}, }; - struct sc_reg_access sc_access32[] = { + struct sc_reg_access ihf_en[] = { /*enable drv*/ {0x25D, 0x0C, 0x0c}, }; - struct sc_reg_access sc_access42[] = { + struct sc_reg_access ihf_unmute[] = { /*unmute headset*/ {0x25B, 0x80, 0x80}, {0x25C, 0x80, 0x80}, }; - struct sc_reg_access sc_access23[] = { + struct sc_reg_access epdac[] = { /* disable driver */ {0x25D, 0x0, 0x43}, /* DAC CONFIG ; both HP, LP on */ {0x257, 0x03, 0x03}, }; - struct sc_reg_access sc_access43[] = { + struct sc_reg_access ep_enable[] = { /* enable driver */ {0x25D, 0x40, 0x40}, /* unmute the headset */ { 0x259, 0x80, 0x80}, { 0x25A, 0x80, 0x80}, }; - struct sc_reg_access sc_access_vib[] = { + struct sc_reg_access vib1_en[] = { /* enable driver, ADC */ {0x25D, 0x10, 0x10}, - {0x264, 0x02, 0x02}, + {0x264, 0x02, 0x82}, }; - struct sc_reg_access sc_access_hap[] = { + struct sc_reg_access vib2_en[] = { /* enable driver, ADC */ {0x25D, 0x20, 0x20}, - {0x26A, 0x02, 0x02}, + {0x26A, 0x02, 0x82}, }; - struct sc_reg_access sc_access_pcm2[] = { + struct sc_reg_access pcm2_en[] = { /* enable pcm 2 */ {0x27C, 0x1, 0x1}, }; @@ -176,89 +425,95 @@ static int msic_power_up_pb(unsigned int device) } pr_debug("powering up pb.... Device %d\n", device); - sst_sc_reg_access(sc_access1, PMIC_WRITE, 4); + 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(sc_access2, PMIC_READ_MODIFY, 2); - sst_sc_reg_access(sc_access3, PMIC_WRITE, 2); - sst_sc_reg_access(sc_access4, PMIC_READ_MODIFY, 3); + 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(sc_access23, PMIC_READ_MODIFY, 2); - sst_sc_reg_access(sc_access3, PMIC_WRITE, 2); - sst_sc_reg_access(sc_access43, PMIC_READ_MODIFY, 3); + 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); } - snd_msic_ops.pb_on = 1; + if (snd_msic_ops.lineout_dev_id == HEADSET) + msic_set_selected_lineout_dev(HEADSET); break; - case SND_SST_DEVICE_IHF: - sst_sc_reg_access(sc_access_vihf, PMIC_WRITE, 1); - sst_sc_reg_access(sc_access22, PMIC_READ_MODIFY, 3); - sst_sc_reg_access(sc_access32, PMIC_READ_MODIFY, 1); - sst_sc_reg_access(sc_access42, PMIC_READ_MODIFY, 2); + 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: - sst_sc_reg_access(sc_access_vib, PMIC_READ_MODIFY, 2); + 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: - sst_sc_reg_access(sc_access_hap, PMIC_READ_MODIFY, 2); + 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(sc_access_pcm2, PMIC_READ_MODIFY, 1); + return sst_sc_reg_access(pcm2_en, PMIC_READ_MODIFY, 1); } static int msic_power_up_cp(unsigned int device) { - struct sc_reg_access sc_access[] = { + struct sc_reg_access vaud[] = { /* turn on the audio power supplies */ - {0x0DB, 0x05, 0}, - /* VHSP */ - {0x0DC, 0xFF, 0}, - /* VHSN */ - {0x0DD, 0x3F, 0}, + {0x0DB, 0x07, 0}, + }; + struct sc_reg_access pll[] = { /* turn on PLL */ - {0x240, 0x21, 0}, - - /* Turn on DMIC supply */ - {0x247, 0xA0, 0x0}, - {0x240, 0x21, 0x0}, - {0x24C, 0x10, 0x0}, - + {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, 0x0}, - {0x246, 0x7, 0x0}, + {0x245, 0x3F, 0x3F}, + {0x246, 0x07, 0x07}, }; - struct sc_reg_access sc_access_amic[] = { - /* turn on the audio power supplies */ - {0x0DB, 0x05, 0}, - /* VHSP */ - {0x0DC, 0xFF, 0}, - /* VHSN */ - {0x0DD, 0x3F, 0}, - /* turn on PLL */ - {0x240, 0x21, 0}, - /*ADC EN*/ - {0x248, 0x05, 0x0}, - {0x24C, 0x76, 0x0}, - /*MIC EN*/ - {0x249, 0x09, 0x0}, - {0x24A, 0x09, 0x0}, + struct sc_reg_access amic_bias[] = { /* Turn on AMIC supply */ - {0x247, 0xFC, 0x0}, + {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 sc_access2[] = { + struct sc_reg_access pcm2[] = { /* enable pcm 2 */ {0x27C, 0x1, 0x1}, }; - struct sc_reg_access sc_access3[] = { + struct sc_reg_access tx_on[] = { /*wait for mic to stabalize before turning on audio channels*/ {0x24F, 0x3C, 0x0}, }; @@ -271,42 +526,161 @@ static int msic_power_up_cp(unsigned int device) } pr_debug("powering up cp....%d\n", snd_msic_ops.input_dev_id); - sst_sc_reg_access(sc_access2, PMIC_READ_MODIFY, 1); + 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(sc_access_amic, PMIC_WRITE, 9); - else - sst_sc_reg_access(sc_access, PMIC_WRITE, 9); - return sst_sc_reg_access(sc_access3, PMIC_WRITE, 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) { - int retval = 0; + 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; - return retval; + 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(void) +static int msic_power_down_pb(unsigned int device) { - int retval = 0; + 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....\n"); - snd_msic_ops.pb_on = 0; - return retval; + + + 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(void) +static int msic_power_down_cp(unsigned int device) { - int retval = 0; + 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; - return retval; + 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) @@ -315,7 +689,7 @@ static int msic_set_selected_output_dev(u8 value) pr_debug("msic set selected output:%d\n", value); snd_msic_ops.output_dev_id = value; - if (snd_msic_ops.pb_on) + if (snd_msic_ops.pbhs_on) msic_power_up_pb(SND_SST_DEVICE_HEADSET); return retval; } @@ -352,6 +726,57 @@ static int msic_set_selected_input_dev(u8 value) 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; @@ -392,9 +817,215 @@ 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, @@ -408,5 +1039,9 @@ struct snd_pmic_ops snd_msic_ops = { .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, + .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, }; |