#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;
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)
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)
{
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);
/* 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
};
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
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 */
};
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;
}
}
/* 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");