ASoC: sst_platform: sn95031: Support for BCU event handling
authorDharageswari.R <Dharageswari.R@intel.com>
Thu, 6 Oct 2011 15:18:33 +0000 (16:18 +0100)
committermgross <mark.gross@intel.com>
Wed, 9 Nov 2011 21:16:50 +0000 (13:16 -0800)
Audio driver was not handling the BCU events

This patch handles the BCU event for IHF speakers

The impact is that, when the audio driver gets an Over current interrupt
it restores the IHF audio volume back to the original level.

Change-Id: I8eb94b37e5104c731dd630c1ce041d7581af0924
Signed-off-by: Dharageswari.R <Dharageswari.R@intel.com>
sound/soc/codecs/sn95031.c
sound/soc/codecs/sn95031.h
sound/soc/mid-x86/mfld_machine.c

index 3d24575..263dfa7 100644 (file)
 #define SN95031_RATES (SNDRV_PCM_RATE_8000_96000)
 #define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 
+struct sn95031_work {
+       struct delayed_work work;
+       struct snd_soc_codec *codec;
+};
 /* codec private data */
 struct sn95031_priv {
        uint8_t clk_src;
        enum sn95031_pll_status  pll_state;
+       struct sn95031_work oc_work;
 };
 void *audio_adc_handle;
 unsigned int sn95031_lp_flag;
 
 /* This Function reads the voltage level from the ADC Driver*/
-static unsigned int sn95031_read_voltage()
+static unsigned int sn95031_read_voltage(void)
 {
        unsigned int mic_bias;
 
@@ -1105,7 +1110,6 @@ void sn95031_jack_report(struct mfld_jack_data *jack_data, unsigned int status)
        if (status & SND_JACK_BTN_0)
                snd_soc_jack_report(jack_data->mfld_jack,
                                SND_JACK_HEADSET, mask);
-       return;
 }
 
 void sn95031_jack_detection(struct mfld_jack_data *jack_data)
@@ -1156,10 +1160,70 @@ void sn95031_jack_detection(struct mfld_jack_data *jack_data)
                                                SN95031_ACCDETMASK, BIT(2), 0);
                sn95031_jack_report(jack_data, status);
        }
-       return;
 }
 EXPORT_SYMBOL_GPL(sn95031_jack_detection);
 
+void sn95031_restore_ihf_vol(struct snd_soc_codec *codec,
+                               unsigned int vol_addr, int crush_volume)
+{
+       u8 ihf_volume;
+
+       pr_debug("In %s\n", __func__);
+       ihf_volume = SN95031_IHF_VOLUME_MASK & snd_soc_read(codec, vol_addr);
+
+       if (!crush_volume)
+               ihf_volume -= SN95031_BCU_VOLUME_RECOVERY_3DB;
+       else
+               ihf_volume -= SN95031_BCU_VOLUME_RECOVERY_6DB;
+
+       snd_soc_update_bits(codec, vol_addr,
+                               SN95031_IHF_VOLUME_MASK, ihf_volume);
+}
+
+
+void sn95031_oc_workqueue(struct work_struct *work)
+{
+       int crush_volume;
+       struct sn95031_work *oc_wq =
+               container_of(work, struct sn95031_work, work.work);
+       struct snd_soc_codec *codec = oc_wq->codec;
+
+       pr_debug("In %s\n", __func__);
+       if (!codec) {
+               pr_debug("codec value null");
+               return;
+       }
+       crush_volume =
+               SN95031_BCU_CRUSH_VOL & snd_soc_read(codec, SN95031_IHFRXCTRL);
+
+       sn95031_restore_ihf_vol(codec, SN95031_IHFLVOLCTRL, crush_volume);
+       sn95031_restore_ihf_vol(codec, SN95031_IHFRVOLCTRL, crush_volume);
+}
+
+void sn95031_oc_handler(struct snd_soc_codec *codec, int oc_interrupt_value)
+{
+       int value;
+       struct sn95031_priv *sn95031_ctx;
+
+       pr_debug("In %s\n", __func__);
+       if (!codec) {
+               pr_debug("codec value null\n");
+               return;
+       }
+       if (oc_interrupt_value & 0x01) {
+               sn95031_ctx = snd_soc_codec_get_drvdata(codec);
+
+               value = snd_soc_read(codec, SN95031_BURST_CNTRL);
+               if (value & BIT(5))
+                       pr_debug("TXPACTEN set to'1'<5 percent BCU setting\n");
+
+               schedule_delayed_work(&sn95031_ctx->oc_work.work,
+                                       msecs_to_jiffies(SN95031_BCU_DELAY));
+       } else
+               pr_debug("unhandled interrupt: %x..\n", oc_interrupt_value);
+}
+EXPORT_SYMBOL_GPL(sn95031_oc_handler);
+
 /* codec registration */
 static int sn95031_codec_probe(struct snd_soc_codec *codec)
 {
@@ -1177,6 +1241,8 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
        sn95031_ctx->clk_src = SN95031_INVALID;
        sn95031_ctx->pll_state = PLL_DISABLED;
 
+       INIT_DELAYED_WORK(&sn95031_ctx->oc_work.work, sn95031_oc_workqueue);
+       sn95031_ctx->oc_work.codec = codec;
 
        /* PCM1 slot configurations*/
        snd_soc_write(codec, SN95031_NOISEMUX, 0x0);
index 3a1db8f..3e54cf3 100644 (file)
 /* multipier to convert to mV */
 #define SN95031_ADC_ONE_LSB_MULTIPLIER 2346
 
+/*BCU related values*/
+#define SN95031_BCU_VOLUME_RECOVERY_3DB 0x3
+#define SN95031_BCU_VOLUME_RECOVERY_6DB 0x6
+#define SN95031_BCU_CRUSH_VOL 0x40
+#define SN95031_IHF_VOLUME_MASK 0x7f
+#define SN95031_BURST_CNTRL 0x107
+#define SN95031_BCU_DELAY 1000
+
 #define SN95031_PLLIN 0x0
 #define SN95031_MODMCLK 0x1
 #define SN95031_PCM1BCLK 0x2
@@ -146,6 +154,9 @@ struct mfld_jack_data {
 };
 
 extern void sn95031_jack_detection(struct mfld_jack_data *jack_data);
+extern void sn95031_oc_handler(struct snd_soc_codec *codec,
+                                       int oc_interrupt_value);
+
 #ifdef CONFIG_SWITCH_MID
 extern void mid_headset_report(int state);
 #endif
index a7ef350..de5d153 100644 (file)
@@ -57,7 +57,8 @@ struct mfld_mc_private {
        struct platform_device *socdev;
        void __iomem *int_base;
        struct snd_soc_codec *codec;
-       u8 interrupt_status;
+       u8 jack_interrupt_status;
+       u8 oc_interrupt_status;
        spinlock_t lock; /* lock for interrupt status and jack debounce */
 };
 
@@ -330,43 +331,78 @@ static struct snd_soc_card snd_soc_card_mfld = {
 static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
 {
        struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
-       u8 intr_status;
+       u16 intr_status;
 
        memcpy_fromio(&intr_status, ((void *)(mc_private->int_base)),
-                       sizeof(u8));
+                       sizeof(u16));
        /* not overwrite status here */
        spin_lock(&mc_private->lock);
-       mc_private->interrupt_status |= intr_status;
+       /*To retrieve the jack_interrupt_status value (MSB)*/
+       mc_private->jack_interrupt_status |= 0x0F & (intr_status >> 8);
+       /*To retrieve the oc_interrupt_status value (LSB)*/
+       mc_private->oc_interrupt_status |= 0x1F & intr_status;
        spin_unlock(&mc_private->lock);
        return IRQ_WAKE_THREAD;
 }
 
-static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
+static void jack_int_handler(struct mfld_mc_private *mc_drv_ctx)
 {
-       struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
        u8 status;
 
+       spin_lock(&mc_drv_ctx->lock);
+       if (mc_drv_ctx->jack_interrupt_status) {
+               status = mc_drv_ctx->jack_interrupt_status;
+               mc_drv_ctx->jack_interrupt_status = 0;
+               spin_unlock(&mc_drv_ctx->lock);
+               pr_debug("jack_status:%x\n", status);
+               mfld_jack_check(status);
+               return;
+       }
+       spin_unlock(&mc_drv_ctx->lock);
+       return;
+}
+
+static void oc_int_handler(struct mfld_mc_private *mc_drv_ctx)
+{
+       u8 status;
+
+       spin_lock(&mc_drv_ctx->lock);
+       if (mc_drv_ctx->oc_interrupt_status) {
+               status = mc_drv_ctx->oc_interrupt_status;
+               mc_drv_ctx->oc_interrupt_status = 0;
+               spin_unlock(&mc_drv_ctx->lock);
+               pr_debug("oc_current\n");
+               sn95031_oc_handler(mc_drv_ctx->codec, status);
+               return;
+       }
+       spin_unlock(&mc_drv_ctx->lock);
+       return;
+}
+
+static irqreturn_t snd_mfld_codec_intr_detection(int irq, void *data)
+{
+       struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
+
        if (mfld_jack.codec == NULL) {
+               pr_debug("codec NULL returning..");
                spin_lock(&mc_drv_ctx->lock);
-               mc_drv_ctx->interrupt_status = 0;
-               pr_debug("codec Null returning..\n");
+               mc_drv_ctx->jack_interrupt_status = 0;
+               mc_drv_ctx->oc_interrupt_status = 0;
                spin_unlock(&mc_drv_ctx->lock);
                return IRQ_HANDLED;
        }
        spin_lock(&mc_drv_ctx->lock);
-       if (!mc_drv_ctx->interrupt_status) {
-               pr_err("Intr with status 0, return....\n");
+       if (!((mc_drv_ctx->jack_interrupt_status) ||
+                       (mc_drv_ctx->oc_interrupt_status))) {
                spin_unlock(&mc_drv_ctx->lock);
-               return IRQ_HANDLED;
+               pr_err("OC and Jack Intr with status 0, return....\n");
+               goto ret;
        }
-       pr_debug("Intr status %d\n", mc_drv_ctx->interrupt_status);
-       /* copy the intr status and clear it here */
-       status = mc_drv_ctx->interrupt_status;
-       mc_drv_ctx->interrupt_status = 0;
        spin_unlock(&mc_drv_ctx->lock);
-
-       mfld_jack_check(status);
-
+       mc_drv_ctx->codec = mfld_jack.codec;
+       oc_int_handler(mc_drv_ctx);
+       jack_int_handler(mc_drv_ctx);
+ret:
        return IRQ_HANDLED;
 }
 
@@ -406,7 +442,7 @@ static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)
        }
        /* register for interrupt */
        ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
-                       snd_mfld_jack_detection,
+                       snd_mfld_codec_intr_detection,
                        IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
        if (ret_val) {
                pr_err("cannot register IRQ\n");