diff --git a/asoc/codecs/aqt1000/aqt1000-api.h b/asoc/codecs/aqt1000/aqt1000-api.h new file mode 100644 index 000000000000..ef2e5267fe77 --- /dev/null +++ b/asoc/codecs/aqt1000/aqt1000-api.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef AQT1000_API_H +#define AQT1000_API_H + +#include +#include +#include +#include + +extern int aqt_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int volt, int micb_num); +extern int aqt_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable); +extern int aqt_get_micb_vout_ctl_val(u32 micb_mv); +extern int aqt_micbias_control(struct snd_soc_codec *codec, int micb_num, + int req, bool is_dapm); + +#endif /* AQT1000_API_H */ diff --git a/asoc/codecs/aqt1000/aqt1000.c b/asoc/codecs/aqt1000/aqt1000.c index b1af218d8379..8ed297fe2dc8 100644 --- a/asoc/codecs/aqt1000/aqt1000.c +++ b/asoc/codecs/aqt1000/aqt1000.c @@ -35,6 +35,7 @@ #include #include "aqt1000-registers.h" #include "aqt1000.h" +#include "aqt1000-api.h" #include "aqt1000-routing.h" #include "../wcdcal-hwdep.h" #include "aqt1000-internal.h" @@ -46,6 +47,7 @@ #define CF_MIN_3DB_150HZ 0x2 #define AQT_VERSION_ENTRY_SIZE 17 +#define AQT_VOUT_CTL_TO_MICB(x) (1000 + x *50) static struct interp_sample_rate sr_val_tbl[] = { {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5}, @@ -682,6 +684,81 @@ static int aqt_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, return 0; } +/* + * aqt_mbhc_micb_adjust_voltage: adjust specific micbias voltage + * @codec: handle to snd_soc_codec * + * @req_volt: micbias voltage to be set + * @micb_num: micbias to be set, e.g. micbias1 or micbias2 + * + * return 0 if adjustment is success or error code in case of failure + */ +int aqt_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, int micb_num) +{ + struct aqt1000 *aqt; + int cur_vout_ctl, req_vout_ctl; + int micb_reg, micb_val, micb_en; + int ret = 0; + + if (!codec) { + pr_err("%s: Invalid codec pointer\n", __func__); + return -EINVAL; + } + + if (micb_num != MIC_BIAS_1) + return -EINVAL; + else + micb_reg = AQT1000_ANA_MICB1; + + aqt = snd_soc_codec_get_drvdata(codec); + mutex_lock(&aqt->micb_lock); + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_val = snd_soc_read(codec, micb_reg); + micb_en = (micb_val & 0xC0) >> 6; + cur_vout_ctl = micb_val & 0x3F; + + req_vout_ctl = aqt_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = -EINVAL; + goto exit; + } + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, AQT_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == 0x1) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + + snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl); + + if (micb_en == 0x1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&aqt->micb_lock); + + return ret; +} +EXPORT_SYMBOL(aqt_mbhc_micb_adjust_voltage); + /* * aqt_micbias_control: enable/disable micbias * @codec: handle to snd_soc_codec *