Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb76781d authored by Yeleswarapu Nagaradhesh's avatar Yeleswarapu Nagaradhesh
Browse files

ASoC: wcd: add calibration data for tombak



Implement calibration data for tombak.
Use hwdep node to use calibration and at the same
time keep the code backward compatible. MBHC
driver can still use request_firmware to get the
calibration if hwdep is not available.

Change-Id: I1c5c5723827da911f8eaa5e5490757d329ec0b45
Signed-off-by: default avatarYeleswarapu Nagaradhesh <nagaradh@codeaurora.org>
parent 76da5756
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -80,7 +80,7 @@ snd-soc-msm8x16-wcd-objs += wcd_cpe_services.o wcd_cpe_core.o
snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o
snd-soc-msm8909-wcd-objs := msm8x16-wcd.o msm8x16-wcd-tables.o wcd-mbhc-v2.o
snd-soc-msm8909-wcd-objs += wcd9xxx-common.o wcd9xxx-resmgr.o wcd9xxx-mbhc.o
snd-soc-msm8909-wcd-objs += wcd9306.o wcd9306-tables.o
snd-soc-msm8909-wcd-objs += wcd9306.o wcd9306-tables.o wcdcal-hwdep.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm-adsp-objs := wm_adsp.o
snd-soc-wm0010-objs := wm0010.o
+48 −1
Original line number Diff line number Diff line
@@ -215,12 +215,34 @@ static void msm8x16_wcd_compute_impedance(s16 l, s16 r, uint32_t *zl,
	*zr = rr;
}

static struct firmware_cal *msm8x16_wcd_get_hwdep_fw_cal(
		struct snd_soc_codec *codec,
		enum wcd_cal_type type)
{
	struct msm8x16_wcd_priv *msm8x16_wcd;
	struct firmware_cal *hwdep_cal;

	if (!codec) {
		pr_err("%s: NULL codec pointer\n", __func__);
		return NULL;
	}
	msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
	hwdep_cal = wcdcal_get_fw_cal(msm8x16_wcd->fw_data, type);
	if (!hwdep_cal) {
		dev_err(codec->dev, "%s: cal not sent by %d\n",
				__func__, type);
		return NULL;
	}
	return hwdep_cal;
}

static const struct wcd_mbhc_cb mbhc_cb = {
	.enable_mb_source = msm8x16_wcd_enable_ext_mb_source,
	.trim_btn_reg = msm8x16_trim_btn_reg,
	.compute_impedance = msm8x16_wcd_compute_impedance,
	.set_micbias_value = msm8x16_wcd_set_micb_v,
	.set_auto_zeroing = msm8x16_wcd_set_auto_zeroing,
	.get_hwdep_fw_cal = msm8x16_wcd_get_hwdep_fw_cal,
};

int msm8x16_unregister_notifier(struct snd_soc_codec *codec,
@@ -3675,7 +3697,7 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec)
{
	struct msm8x16_wcd_priv *msm8x16_wcd_priv;
	struct msm8x16_wcd *msm8x16_wcd;
	int i;
	int i, ret;

	dev_dbg(codec->dev, "%s()\n", __func__);

@@ -3737,6 +3759,28 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec)

	BLOCKING_INIT_NOTIFIER_HEAD(&msm8x16_wcd_priv->notifier);

	msm8x16_wcd_priv->fw_data = kzalloc(sizeof(*(msm8x16_wcd_priv->fw_data))
			, GFP_KERNEL);
	if (!msm8x16_wcd_priv->fw_data) {
		dev_err(codec->dev, "Failed to allocate fw_data\n");
		iounmap(msm8x16_wcd->dig_base);
		kfree(msm8x16_wcd_priv);
		return -ENOMEM;
	}

	set_bit(WCD9XXX_ANC_CAL, msm8x16_wcd_priv->fw_data->cal_bit);
	set_bit(WCD9XXX_MAD_CAL, msm8x16_wcd_priv->fw_data->cal_bit);
	set_bit(WCD9XXX_MBHC_CAL, msm8x16_wcd_priv->fw_data->cal_bit);
	ret = wcd_cal_create_hwdep(msm8x16_wcd_priv->fw_data,
			WCD9XXX_CODEC_HWDEP_NODE, codec);
	if (ret < 0) {
		dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
		iounmap(msm8x16_wcd->dig_base);
		kfree(msm8x16_wcd_priv->fw_data);
		kfree(msm8x16_wcd_priv);
		return ret;
	}

	wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids,
			true);

@@ -3755,6 +3799,7 @@ static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec)
		dev_err(codec->dev, "Failed to register modem state notifier\n"
			);
		iounmap(msm8x16_wcd->dig_base);
		kfree(msm8x16_wcd_priv->fw_data);
		kfree(msm8x16_wcd_priv);
		registered_codec = NULL;
		return -ENOMEM;
@@ -3773,6 +3818,8 @@ static int msm8x16_wcd_codec_remove(struct snd_soc_codec *codec)
	msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL;
	atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0);
	iounmap(msm8x16_wcd->dig_base);
	kfree(msm8x16_wcd_priv->fw_data);
	kfree(msm8x16_wcd_priv);

	return 0;
}
+3 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <sound/q6afe-v2.h>
#include <linux/mfd/wcd9xxx/pdata.h>
#include "wcd-mbhc-v2.h"
#include "wcdcal-hwdep.h"

#define MSM8X16_WCD_NUM_REGISTERS	0x6FF
#define MSM8X16_WCD_MAX_REGISTER	(MSM8X16_WCD_NUM_REGISTERS-1)
@@ -231,6 +232,8 @@ struct msm8x16_wcd_priv {
	struct regulator *spkdrv_reg;
	/* mbhc module */
	struct wcd_mbhc mbhc;
	/* cal info for codec */
	struct fw_info *fw_data;
	struct blocking_notifier_head notifier;

};
+135 −3
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/mfd/wcd9xxx/pdata.h>
#include <linux/firmware.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -35,6 +36,7 @@
#include "msm8x16_wcd_registers.h"
#include "msm8916-wcd-irq.h"
#include "msm8x16-wcd.h"
#include "wcdcal-hwdep.h"

#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
			   SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
@@ -49,6 +51,8 @@
#define GND_MIC_SWAP_THRESHOLD 4
#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100
#define HS_VREF_MIN_VAL 1400
#define FW_READ_ATTEMPTS 15
#define FW_READ_TIMEOUT 4000000

static int det_extn_cable_en;
module_param(det_extn_cable_en, int,
@@ -164,7 +168,7 @@ static void wcd_program_hs_vref(const struct wcd_mbhc *mbhc)
	struct snd_soc_card *card = codec->card;
	u32 reg_val;

	plug_type_cfg = WCD_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
	plug_type_cfg = WCD_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
	reg_val = ((plug_type_cfg->v_hs_max - HS_VREF_MIN_VAL) / 100);

	dev_dbg(card->dev, "%s: reg_val  = %x\n", __func__, reg_val);
@@ -190,7 +194,8 @@ static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
	btn_det = WCD_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);

	if (micbias)
		btn_voltage = btn_det->_v_btn_high;
		btn_voltage = ((void *)&btn_det->_v_btn_low) +
			(sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn);
	else
		btn_voltage = btn_det->_v_btn_low;
	for (i = 0; i <  btn_det->num_btn; i++) {
@@ -1582,6 +1587,30 @@ static void wcd_btn_lpress_fn(struct work_struct *work)
	wcd9xxx_spmi_unlock_sleep();
}

static bool wcd_mbhc_fw_validate(const void *data, size_t size)
{
	u32 cfg_offset;
	struct wcd_mbhc_btn_detect_cfg *btn_cfg;
	struct firmware_cal fw;

	fw.data = (void *)data;
	fw.size = size;

	if (fw.size < WCD_MBHC_CAL_MIN_SIZE)
		return false;

	/*
	 * Previous check guarantees that there is enough fw data up
	 * to num_btn
	 */
	btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(fw.data);
	cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data);
	if (fw.size < (cfg_offset + WCD_MBHC_CAL_BTN_SZ(btn_cfg)))
		return false;

	return true;
}

irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
{
	struct wcd_mbhc *mbhc = data;
@@ -1801,6 +1830,87 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
	return ret;
}

static void wcd_mbhc_fw_read(struct work_struct *work)
{
	struct delayed_work *dwork;
	struct wcd_mbhc *mbhc;
	struct snd_soc_codec *codec;
	const struct firmware *fw;
	struct firmware_cal *fw_data = NULL;
	int ret = -1, retry = 0;
	bool use_default_cal = false;

	dwork = to_delayed_work(work);
	mbhc = container_of(dwork, struct wcd_mbhc, mbhc_firmware_dwork);
	codec = mbhc->codec;

	while (retry < FW_READ_ATTEMPTS) {
		retry++;
		pr_debug("%s:Attempt %d to request MBHC firmware\n",
			__func__, retry);
		if (mbhc->mbhc_cb->get_hwdep_fw_cal)
			fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(codec,
					WCD9XXX_MBHC_CAL);
		if (!fw_data)
			ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
				       codec->dev);
		/*
		 * if request_firmware and hwdep cal both fail then
		 * sleep for 4sec for the userspace to send data to kernel
		 * retry for few times before bailing out
		 */
		if ((ret != 0) && !fw_data) {
			usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT +
						WCD9XXX_USLEEP_RANGE_MARGIN_US);
		} else {
			pr_debug("%s: MBHC Firmware read succesful\n",
					__func__);
			break;
		}
	}
	if (!fw_data)
		pr_debug("%s: using request_firmware\n", __func__);
	else
		pr_debug("%s: using hwdep cal\n", __func__);

	if (ret != 0 && !fw_data) {
		pr_err("%s: Cannot load MBHC firmware use default cal\n",
		       __func__);
		use_default_cal = true;
	}
	if (!use_default_cal) {
		const void *data;
		size_t size;

		if (fw_data) {
			data = fw_data->data;
			size = fw_data->size;
		} else {
			data = fw->data;
			size = fw->size;
		}
		if (wcd_mbhc_fw_validate(data, size) == false) {
			pr_err("%s: Invalid MBHC cal data size use default cal\n",
				__func__);
			if (!fw_data)
				release_firmware(fw);
		} else {
			if (fw_data) {
				mbhc->mbhc_cfg->calibration =
					(void *)fw_data->data;
				mbhc->mbhc_cal = fw_data;
			} else {
				mbhc->mbhc_cfg->calibration =
					(void *)fw->data;
				mbhc->mbhc_fw = fw;
			}
		}

	}

	(void) wcd_mbhc_initialise(mbhc);
}

int wcd_mbhc_start(struct wcd_mbhc *mbhc,
		       struct wcd_mbhc_config *mbhc_cfg)
{
@@ -1809,7 +1919,19 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc,
	pr_debug("%s: enter\n", __func__);
	/* update the mbhc config */
	mbhc->mbhc_cfg = mbhc_cfg;

	if (!mbhc->mbhc_cfg->read_fw_bin ||
	    (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) ||
	    (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) {
		rc = wcd_mbhc_initialise(mbhc);
	} else {
		if (!mbhc->mbhc_fw || !mbhc->mbhc_cal)
			schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
				      usecs_to_jiffies(FW_READ_TIMEOUT));
		else
			pr_err("%s: Skipping to read mbhc fw, 0x%p %p\n",
				 __func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
	}
	pr_debug("%s: leave %d\n", __func__, rc);
	return rc;
}
@@ -1820,6 +1942,14 @@ void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
	pr_debug("%s: enter\n", __func__);
	wcd9xxx_spmi_disable_irq(mbhc->intr_ids->hph_left_ocp);
	wcd9xxx_spmi_disable_irq(mbhc->intr_ids->hph_right_ocp);

	if (mbhc->mbhc_fw || mbhc->mbhc_cal) {
		cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
		if (!mbhc->mbhc_cal)
			release_firmware(mbhc->mbhc_fw);
		mbhc->mbhc_fw = NULL;
		mbhc->mbhc_cal = NULL;
	}
	pr_debug("%s: leave\n", __func__);
}
EXPORT_SYMBOL(wcd_mbhc_stop);
@@ -1909,6 +2039,8 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec,
			return ret;
		}

		INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
				  wcd_mbhc_fw_read);
		INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn);
	}

+111 −7
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#define __WCD_MBHC_V2_H__

#include <linux/wait.h>
#include "wcdcal-hwdep.h"

#define TOMBAK_MBHC_NC	0
#define TOMBAK_MBHC_NO	1
@@ -41,6 +42,33 @@ enum wcd_mbhc_event_state {
	WCD_MBHC_EVENT_PA_HPHL,
	WCD_MBHC_EVENT_PA_HPHR,
};
struct wcd_mbhc_general_cfg {
	u8 t_ldoh;
	u8 t_bg_fast_settle;
	u8 t_shutdown_plug_rem;
	u8 mbhc_nsa;
	u8 mbhc_navg;
	u8 v_micbias_l;
	u8 v_micbias;
	u8 mbhc_reserved;
	u16 settle_wait;
	u16 t_micbias_rampup;
	u16 t_micbias_rampdown;
	u16 t_supply_bringup;
} __packed;

struct wcd_mbhc_plug_detect_cfg {
	u32 mic_current;
	u32 hph_current;
	u16 t_mic_pid;
	u16 t_ins_complete;
	u16 t_ins_retry;
	u16 v_removal_delta;
	u8 micbias_slow_ramp;
	u8 reserved0;
	u8 reserved1;
	u8 reserved2;
} __packed;

struct wcd_mbhc_plug_type_cfg {
	u8 av_detect;
@@ -56,9 +84,39 @@ struct wcd_mbhc_plug_type_cfg {
} __packed;

struct wcd_mbhc_btn_detect_cfg {
	s8 c[8];
	u8 nc;
	u8 n_meas;
	u8 mbhc_nsc;
	u8 n_btn_meas;
	u8 n_btn_con;
	u8 num_btn;
	s16 _v_btn_low[WCD_MBHC_DEF_BUTTONS];
	s16 _v_btn_high[WCD_MBHC_DEF_BUTTONS];
	u8 reserved0;
	u8 reserved1;
	u16 t_poll;
	u16 t_bounce_wait;
	u16 t_rel_timeout;
	s16 v_btn_press_delta_sta;
	s16 v_btn_press_delta_cic;
	u16 t_btn0_timeout;
	s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
	s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
	u8 _n_ready[2];
	u8 _n_cic[2];
	u8 _gain[2];
} __packed;

struct wcd_mbhc_imped_detect_cfg {
	u8 _hs_imped_detect;
	u8 _n_rload;
	u8 _hph_keep_on;
	u8 _repeat_rload_calc;
	u16 _t_dac_ramp_time;
	u16 _rhph_high;
	u16 _rhph_low;
	u16 _rload[0]; /* rload[n_rload] */
	u16 _alpha[0]; /* alpha[n_rload] */
	u16 _beta[3];
} __packed;

struct wcd_mbhc_config {
@@ -86,6 +144,8 @@ struct wcd_mbhc_cb {
	void (*compute_impedance) (s16 , s16 , uint32_t *, uint32_t *, bool);
	void (*set_micbias_value) (struct snd_soc_codec *);
	void (*set_auto_zeroing) (struct snd_soc_codec *, bool);
	struct firmware_cal * (*get_hwdep_fw_cal)(struct snd_soc_codec *,
			enum wcd_cal_type);
};

struct wcd_mbhc {
@@ -113,6 +173,10 @@ struct wcd_mbhc {
	bool is_hs_recording;

	struct snd_soc_codec *codec;
	/* Work to perform MBHC Firmware Read */
	struct delayed_work mbhc_firmware_dwork;
	const struct firmware *mbhc_fw;
	struct firmware_cal *mbhc_cal;

	/* track PA/DAC state to sync with userspace */
	unsigned long hph_pa_dac_state;
@@ -134,17 +198,57 @@ struct wcd_mbhc {
	struct work_struct correct_plug_swch;
	struct notifier_block nblock;
};

#define WCD_MBHC_CAL_SIZE ( \
#define WCD_MBHC_CAL_SIZE(buttons, rload) ( \
	sizeof(struct wcd_mbhc_general_cfg) + \
	sizeof(struct wcd_mbhc_plug_detect_cfg) + \
	((sizeof(s16) + sizeof(s16)) * buttons) + \
	    sizeof(struct wcd_mbhc_plug_type_cfg) + \
	    sizeof(struct wcd_mbhc_btn_detect_cfg) \
	sizeof(struct wcd_mbhc_btn_detect_cfg) + \
	sizeof(struct wcd_mbhc_imped_detect_cfg) + \
		((sizeof(u16) + sizeof(u16)) * rload) \
	)


#define WCD_MBHC_CAL_GENERAL_PTR(cali) ( \
	(struct wcd_mbhc_general_cfg *) cali)
#define WCD_MBHC_CAL_PLUG_DET_PTR(cali) ( \
	    (struct wcd_mbhc_plug_type_cfg *) cali)
	(struct wcd_mbhc_plug_detect_cfg *) \
	&(WCD_MBHC_CAL_GENERAL_PTR(cali)[1]))
#define WCD_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
	(struct wcd_mbhc_plug_type_cfg *) \
	&(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
#define WCD_MBHC_CAL_BTN_DET_PTR(cali) ( \
	    (struct wcd_mbhc_btn_detect_cfg *) \
	    &(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
	&(WCD_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
#define WCD_MBHC_CAL_IMPED_DET_PTR(cali) ( \
	(struct wcd_mbhc_imped_detect_cfg *) \
	(((void *)&WCD_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
	(WCD_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
	(sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
	sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
	)

#define WCD_MBHC_CAL_MIN_SIZE ( \
	sizeof(struct wcd_mbhc_general_cfg) + \
	sizeof(struct wcd_mbhc_plug_detect_cfg) + \
	sizeof(struct wcd_mbhc_plug_type_cfg) + \
	sizeof(struct wcd_mbhc_btn_detect_cfg) + \
	sizeof(struct wcd_mbhc_imped_detect_cfg) + \
	(sizeof(u16)*2)  \
	)

#define WCD_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
	sizeof(struct wcd_mbhc_btn_detect_cfg) + \
	(cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
			sizeof(cfg_ptr->_v_btn_high[0]))))

#define WCD_MBHC_CAL_IMPED_MIN_SZ ( \
	sizeof(struct wcd_mbhc_imped_detect_cfg) + sizeof(u16) * 2)

#define WCD_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
	sizeof(struct wcd_mbhc_imped_detect_cfg) + \
	(cfg_ptr->_n_rload * \
	(sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0]))))

int wcd_mbhc_start(struct wcd_mbhc *mbhc,
		       struct wcd_mbhc_config *mbhc_cfg);
Loading