/*
 * Copyright (C) 2014 NXP Semiconductors, 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 as
 * published by the Free Software Foundation.
 *
 */

#include "dbgprint.h"
#include "tfa_container.h"
#include "tfa.h"
#include "tfa98xx_tfafieldnames.h"
#include "tfa_internal.h"

#ifdef OPLUS_ARCH_EXTENDS
extern int ftm_mode;
extern char ftm_SpeakerCalibration[17];
extern char ftm_spk_resistance[24];

#ifndef BOOT_MODE_FACTORY
#define BOOT_MODE_FACTORY 3
#endif
#endif /* OPLUS_ARCH_EXTENDS */

#ifdef OPLUS_ARCH_EXTENDS
extern bool aging_flag;
#endif /* OPLUS_ARCH_EXTENDS */

/* handle macro for bitfield */
#define TFA_MK_BF(reg, pos, len) ((reg<<8)|(pos<<4)|(len-1))

/* abstract family for register */
#define FAM_TFA98XX_CF_CONTROLS (TFA_FAM(tfa,RST) >> 8)
#define FAM_TFA98XX_CF_MEM      (TFA_FAM(tfa,MEMA)>> 8)
#define FAM_TFA98XX_MTP0        (TFA_FAM(tfa,MTPOTC) >> 8)
#define FAM_TFA98xx_INT_EN      (TFA_FAM(tfa,INTENVDDS) >> 8)

#define CF_STATUS_I2C_CMD_ACK 0x01

/* Defines below are used for irq function (this removed the genregs include) */
#define TFA98XX_INTERRUPT_ENABLE_REG1		0x48
#define TFA98XX_INTERRUPT_IN_REG1		0x44
#define TFA98XX_INTERRUPT_OUT_REG1		0x40
#define TFA98XX_STATUS_POLARITY_REG1		0x4c
#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK	0x2
#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK	0x1
#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS	1
#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS	0

/*To support tfa9873*/
#define MIN_BATT_LEVEL 640
#define MAX_BATT_LEVEL 670
void tfanone_ops(struct tfa_device_ops *ops);
void tfa9872_ops(struct tfa_device_ops *ops);
/*To support tfa9873*/
void tfa9873_ops(struct tfa_device_ops *ops);
void tfa9874_ops(struct tfa_device_ops *ops);
void tfa9912_ops(struct tfa_device_ops *ops);
void tfa9888_ops_v6(struct tfa_device_ops *ops);
void tfa9891_ops_v6(struct tfa_device_ops *ops);
void tfa9897_ops_v6(struct tfa_device_ops *ops);
void tfa9896_ops(struct tfa_device_ops *ops);
void tfa9890_ops_v6(struct tfa_device_ops *ops);
void tfa9895_ops(struct tfa_device_ops *ops);
void tfa9894_ops(struct tfa_device_ops *ops);

#ifndef MIN
#define MIN(A,B) (A<B?A:B)
#endif

/* retry values */
#define CFSTABLE_TRIES		10
#define AMPOFFWAIT_TRIES	50
#define MTPBWAIT_TRIES		50
#define MTPEX_WAIT_NTRIES	50

/* calibration done executed */
#define TFA_MTPEX_POS           TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS /**/

#ifdef OPLUS_ARCH_EXTENDS
bool g_speaker_resistance_fail = false;
#endif /* OPLUS_ARCH_EXTENDS */

int tfa_get_calibration_info_v6(struct tfa_device *tfa, int channel)
{
	return tfa->mohm[channel];
}

/* return sign extended tap pattern */
int tfa_get_tap_pattern(struct tfa_device *tfa)
{
	int value = tfa_get_bf_v6(tfa, TFA9912_BF_CFTAPPAT );
	int bitshift;
	uint8_t field_len = 1 + (TFA9912_BF_CFTAPPAT & 0x0f); /* length of bitfield */

	bitshift = 8*sizeof(int)-field_len;
	/* signextend */
	value = (value << bitshift) >> bitshift;

	return value;
}
/*
 * interrupt bit function to clear
 */
int tfa_irq_clear(struct tfa_device *tfa, enum tfa9912_irq bit)
{
	unsigned char reg;

	/* make bitfield enum */
	if ( bit == tfa9912_irq_all) {
		/* operate on all bits */
		for(reg=TFA98XX_INTERRUPT_IN_REG1; reg<TFA98XX_INTERRUPT_IN_REG1+3; reg++)
			tfa_reg_write(tfa, reg, 0xffff); /* all bits */
	} else if (bit < tfa9912_irq_max) {
		reg = (unsigned char)(TFA98XX_INTERRUPT_IN_REG1 + (bit>>4));
		tfa_reg_write(tfa, reg,  1<<(bit & 0x0f)); /* only this bit */
	} else
		return -1;

	return 0;
}
/*
 * return state of irq or -1 if illegal bit
 */
int tfa_irq_get(struct tfa_device *tfa, enum tfa9912_irq bit)
{
	uint16_t value;
	int reg, mask;

	if (bit < tfa9912_irq_max) {
		/* only this bit */
		reg = TFA98XX_INTERRUPT_OUT_REG1 + (bit>>4);
		mask = 1<<(bit & 0x0f);
		tfa_reg_read(tfa, (unsigned char)reg, &value);
	} else
		return -1;

	return (value & mask) !=0 ;
}
/*
 * interrupt bit function that operates on the shadow regs in the handle
 */

int tfa_irq_ena(struct tfa_device *tfa, enum tfa9912_irq bit, int state)
{
	uint16_t value, new_value;
	int reg=0, mask;
	/* */
	if ( bit == tfa9912_irq_all) {
		/* operate on all bits */
		for(reg=TFA98XX_INTERRUPT_ENABLE_REG1; reg<=TFA98XX_INTERRUPT_ENABLE_REG1+tfa9912_irq_max/16; reg++) {
			tfa_reg_write(tfa, (unsigned char)reg, state ? 0xffff : 0); /* all bits */
			tfa->interrupt_enable[reg-TFA98XX_INTERRUPT_ENABLE_REG1] = state ? 0xffff : 0; /* all bits */
		}
	} else if (bit < tfa9912_irq_max) {
		 /* only this bit */
			reg = TFA98XX_INTERRUPT_ENABLE_REG1 + (bit>>4);
			mask = 1<<(bit & 0x0f);
			tfa_reg_read(tfa, (unsigned char)reg, &value);
			if (state) //set
				new_value = (uint16_t)(value | mask);
			else 		// clear
				new_value = value & ~mask;
			if ( new_value != value) {
				tfa_reg_write(tfa, (unsigned char)reg,  new_value); /* only this bit */
				tfa->interrupt_enable[reg-TFA98XX_INTERRUPT_ENABLE_REG1] = new_value;
			}
	} else
		return -1;

	return 0;
}

/*
 * mask interrupts by disabling them
 */
int tfa_irq_mask(struct tfa_device *tfa)
{
	int reg;

	/* operate on all bits */
	for (reg=TFA98XX_INTERRUPT_ENABLE_REG1; reg<=TFA98XX_INTERRUPT_ENABLE_REG1+tfa9912_irq_max/16; reg++)
		tfa_reg_write(tfa, (unsigned char)reg, 0);

	return 0;
}

/*
 * unmask interrupts by enabling them again
 */
int tfa_irq_unmask(struct tfa_device *tfa)
{
	int reg;

	/* operate on all bits */
	for (reg=TFA98XX_INTERRUPT_ENABLE_REG1; reg<=TFA98XX_INTERRUPT_ENABLE_REG1+tfa9912_irq_max/16; reg++)
		tfa_reg_write(tfa, (unsigned char)reg, tfa->interrupt_enable[reg-TFA98XX_INTERRUPT_ENABLE_REG1]);

	return 0;
}

/*
 * interrupt bit function that sets the polarity
 */

int tfa_irq_set_pol(struct tfa_device *tfa, enum tfa9912_irq bit, int state)
{
	uint16_t value, new_value;
	int reg=0, mask;

	if (bit == tfa9912_irq_all) {
		/* operate on all bits */
		for(reg=TFA98XX_STATUS_POLARITY_REG1; reg<=TFA98XX_STATUS_POLARITY_REG1+tfa9912_irq_max/16; reg++) {
			tfa_reg_write(tfa, (unsigned char)reg, state ? 0xffff : 0); /* all bits */
		}
	} else if (bit < tfa9912_irq_max) {
		 /* only this bit */
		reg = TFA98XX_STATUS_POLARITY_REG1 + (bit>>4);
		mask = 1<<(bit & 0x0f);
		tfa_reg_read(tfa, (unsigned char)reg, &value);
		if (state) /* Active High */
			new_value = (uint16_t)(value | mask);
		else       /* Active Low */
			new_value = value & ~mask;
		if ( new_value != value) {
			tfa_reg_write(tfa, (unsigned char)reg,  new_value); /* only this bit */
		}
	} else
		return -1;

	return 0;
}

/*
 *  set device info and register device ops
 */
void tfa_set_query_info(struct tfa_device *tfa)
{
	/* invalidate device struct cached values */
	tfa->hw_feature_bits = -1;
	tfa->sw_feature_bits[0] = -1;
	tfa->sw_feature_bits[1] = -1;
	tfa->profile = -1;
	tfa->vstep = -1;
	/* defaults */
	tfa->is_probus_device = 0;
	/*To support tfa9873*/
	tfa->advance_keys_handling = 0; /*artf65038*/
	tfa->tfa_family = 1;
	tfa->daimap = Tfa98xx_DAI_I2S;		/* all others */
	tfa->spkr_count = 1;
	tfa->spkr_select = 0;
	tfa->support_tcoef = supportYes;
	tfa->supportDrc = supportNotSet;
	tfa->support_saam = supportNotSet;
	tfa->ext_dsp = -1;	/* respond to external DSP: -1:none, 0:no_dsp, 1:cold, 2:warm */
	tfa->bus=0;
	tfa->partial_enable = 0;
	tfa->convert_dsp32 = 0;
	tfa->sync_iv_delay = 0;

	/* TODO use the getfeatures() for retrieving the features [artf103523]
	tfa->supportDrc = supportNotSet;*/

	switch (tfa->rev & 0xff) {
	case 0: /* tfanone : non-i2c external DSP device */
		/* e.g. qc adsp */
		tfa->supportDrc = supportYes;
		tfa->tfa_family = 0;
		tfa->spkr_count = 0;
		tfa->daimap = 0;
		tfanone_ops(&tfa->dev_ops); /* register device operations via tfa hal*/
		tfa->bus=1;
		break;
	case 0x72:
		/* tfa9872 */
		tfa->supportDrc = supportYes;
		tfa->tfa_family = 2;
		tfa->spkr_count = 1;
		tfa->is_probus_device = 1;
		tfa->daimap = Tfa98xx_DAI_TDM;
		tfa9872_ops(&tfa->dev_ops); /* register device operations */
		break;
	/*To support tfa9873*/
	case 0x73:
		/* tfa9873 */
		tfa->supportDrc = supportYes;
		tfa->tfa_family = 2;
		tfa->spkr_count = 1;
		tfa->is_probus_device = 1;
		tfa->has_msg = 1;
		tfa->advance_keys_handling = 1; /*artf65038*/
		tfa->daimap = Tfa98xx_DAI_TDM;
		tfa9873_ops(&tfa->dev_ops); /* register device operations */
		break;
	case 0x74:
		/* tfa9874 */
		tfa->supportDrc = supportYes;
		tfa->tfa_family = 2;
		tfa->spkr_count = 1;
		tfa->is_probus_device = 1;
		tfa->has_msg = 1;
		tfa->daimap = Tfa98xx_DAI_TDM;
		tfa9874_ops(&tfa->dev_ops); /* register device operations */
		break;
	case 0x88:
		/* tfa9888 */
		tfa->tfa_family = 2;
		tfa->spkr_count = 2;
		tfa->daimap = Tfa98xx_DAI_TDM;
		tfa9888_ops_v6(&tfa->dev_ops); /* register device operations */
		break;
	case 0x97:
		/* tfa9897 */
		tfa->supportDrc = supportNo;
		tfa->spkr_count = 1;
		tfa->daimap = Tfa98xx_DAI_TDM;
		tfa9897_ops_v6(&tfa->dev_ops); /* register device operations */
		break;
	case 0x96:
		/* tfa9896 */
		tfa->supportDrc = supportNo;
		tfa->spkr_count = 1;
		tfa->daimap = Tfa98xx_DAI_TDM;
		tfa9896_ops(&tfa->dev_ops); /* register device operations */
		break;
	case 0x92:
		/* tfa9891 */
		tfa->spkr_count = 1;
		tfa->daimap = ( Tfa98xx_DAI_PDM | Tfa98xx_DAI_I2S );
		tfa9891_ops_v6(&tfa->dev_ops); /* register device operations */
		break;
	case 0x91:
		/* tfa9890B */
		tfa->spkr_count = 1;
		tfa->daimap = ( Tfa98xx_DAI_PDM | Tfa98xx_DAI_I2S );
		break;
	case 0x80:
	case 0x81:
		/* tfa9890 */
		tfa->spkr_count = 1;
		tfa->daimap = Tfa98xx_DAI_I2S;
		tfa->supportDrc = supportNo;
		tfa->supportFramework = supportNo;
		tfa9890_ops_v6(&tfa->dev_ops); /* register device operations */
		break;
	case 0x12:
		/* tfa9895 */
		tfa->spkr_count = 1;
		tfa->daimap = Tfa98xx_DAI_I2S;
		tfa9895_ops(&tfa->dev_ops); /* register device operations */
		break;
	case 0x13:
		/* tfa9912 */
		tfa->tfa_family = 2;
		tfa->spkr_count = 1;
		tfa->daimap = Tfa98xx_DAI_TDM;
		tfa9912_ops(&tfa->dev_ops); /* register device operations */
		break;
	case 0x94:
		/* tfa9894 */
		tfa->tfa_family = 2;
		tfa->spkr_count = 1;
		tfa->daimap = Tfa98xx_DAI_TDM;
		tfa9894_ops(&tfa->dev_ops); /* register device operations */
		break;

	default:
		pr_err("unknown device type : 0x%02x\n", tfa->rev);
		_ASSERT(0);
		break;
	}
}

/*
 * lookup the device type and return the family type
 */
int tfa98xx_dev2family_v6(int dev_type)
{
	/* only look at the die ID part (lsb byte) */
	switch(dev_type & 0xff) {
	case 0x12:
	case 0x80:
	case 0x81:
	case 0x91:
	case 0x92:
	case 0x97:
	case 0x96:
		return 1;
	case 0x88:
	case 0x72:
	case 0x73:
	case 0x13:
	case 0x74:
	case 0x94:
		return 2;
	case 0x50:
		return 3;
	default:
		return 0;
	}
}

/*
 * 	return the target address for the filter on this device

  filter_index:
	[0..9] reserved for EQ (not deployed, calc. is available)
	[10..12] anti-alias filter
	[13]  integrator filter

 */
enum Tfa98xx_DMEM tfa98xx_filter_mem_v6(struct tfa_device *tfa, int filter_index, unsigned short *address, int channel)
{
	enum Tfa98xx_DMEM dmem=-1;
	int idx;
	unsigned short bq_table[7][4] ={
	/* index: 10, 11, 12, 13 */
			{346,351,356,288}, //87 BRA_MAX_MRA4-2_7.00
			{346,351,356,288}, //90 BRA_MAX_MRA6_9.02
			{467,472,477,409}, //95 BRA_MAX_MRA7_10.02
			{406,411,416,348}, //97 BRA_MAX_MRA9_12.01
			{467,472,477,409}, //91 BRA_MAX_MRAA_13.02
			{8832, 8837, 8842, 8847}, //88 part1
			{8853, 8858, 8863, 8868}  //88 part2
			/* Since the 88 is stereo we have 2 parts.
			 * Every index has 5 values except index 13 this one has 6 values
			 */
	};

	if ( (10 <= filter_index) && (filter_index <= 13) ) {
		dmem = Tfa98xx_DMEM_YMEM; /* for all devices */
		idx = filter_index-10;

		switch (tfa->rev & 0xff ) { // only compare lower byte
		case 0x12:
			*address = bq_table[2][idx];
			break;
		case 0x97:
			*address = bq_table[3][idx];
			break;
		case 0x96:
			*address = bq_table[3][idx];
			break;
		case 0x80:
		case 0x81: // for the RAM version
		case 0x91:
			*address = bq_table[1][idx];
			break;
		case 0x92:
			*address = bq_table[4][idx];
			break;
		case 0x88:
			/* Channel 1 = primary, 2 = secondary */
			if(channel == 1)
				*address = bq_table[5][idx];
			else
				*address = bq_table[6][idx];
			break;
		case 0x72:
		case 0x73:
		case 0x74:
		case 0x13:
		default:
			/* unsupported case, possibly intermediate version */
			return -1;
			_ASSERT(0);
		}
	}
	return dmem;
}

/************************ query functions ********************************************************/
/**
* return revision
* Used by the LTT
*/
void tfa98xx_rev_v6(int *major, int *minor, int *revision)
{
	char version_str[] = TFA98XX_API_REV_STR;
	sscanf(version_str, "v%d.%d.%d", major, minor, revision);
}

/**
 * tfa_supported_speakers
 *  returns the number of the supported speaker count
 */
enum Tfa98xx_Error tfa_supported_speakers(struct tfa_device *tfa, int* spkr_count)
{
	if (tfa->in_use == 0)
		return Tfa98xx_Error_NotOpen;
	else
		*spkr_count = tfa->spkr_count;

	return Tfa98xx_Error_Ok;
}

/*
 * tfa98xx_supported_saam_v6
 *  returns the supportedspeaker as microphone feature
 */
enum Tfa98xx_Error tfa98xx_supported_saam_v6(struct tfa_device *tfa, enum Tfa98xx_saam *saam)
{
	int features;
	enum Tfa98xx_Error error;

	if (tfa->support_saam == supportNotSet) {
		error = tfa98xx_dsp_get_hw_feature_bits_v6(tfa, &features);
		if (error!=Tfa98xx_Error_Ok)
			return error;
		tfa->support_saam =
				(features & 0x8000)? supportYes : supportNo; /* SAAM is bit15 */
	}
	*saam = tfa->support_saam == supportYes ? Tfa98xx_saam : Tfa98xx_saam_none ;

	return Tfa98xx_Error_Ok;
}

/*
 * tfa98xx_compare_features_v6
 *  Obtains features_from_MTP and features_from_cnt
 */
enum Tfa98xx_Error tfa98xx_compare_features_v6(struct tfa_device *tfa, int features_from_MTP[3], int features_from_cnt[3])
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	uint32_t value;
	uint16_t mtpbf;
	unsigned char bytes[3 * 2];
	int status;

	tfa98xx_dsp_system_stable_v6(tfa, &status);
	if (!status)
		return Tfa98xx_Error_NoClock; // Only test when we have a clock.

	/* Set proper MTP location per device: */
	if (tfa->tfa_family == 1) {
		mtpbf=0x850f;  /* MTP5 for tfa1,16 bits */
	} else {
		mtpbf=0xf907;  /* MTP9 for tfa2, 8 bits */
	}

	/* Read HW features from MTP: */
	value = tfa_read_reg_v6(tfa, mtpbf) & 0xffff;
	features_from_MTP[0] = tfa->hw_feature_bits = value;

    /* Read SW features: */
    error = tfa_dsp_cmd_id_write_read_v6(tfa, MODULE_FRAMEWORK, FW_PAR_ID_GET_FEATURE_INFO, sizeof(bytes), bytes);
	if (error != Tfa98xx_Error_Ok)
	        return error; /* old ROM code may respond with Tfa98xx_Error_RpcParamId */

	tfa98xx_convert_bytes2data_v6(sizeof(bytes), bytes, &features_from_MTP[1]);

	/* check if feature bits from MTP match feature bits from cnt file: */
	get_hw_features_from_cnt_v6(tfa, &features_from_cnt[0]);
	get_sw_features_from_cnt_v6(tfa, &features_from_cnt[1]);

	return error;
}

/********************************* device specific ops ************************************************/
/* the wrapper for DspReset, in case of full */
enum Tfa98xx_Error tfa98xx_dsp_reset_v6(struct tfa_device *tfa, int state)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;

	error = (tfa->dev_ops.dsp_reset)(tfa, state);

	return error;
}

/* the ops wrapper for tfa98xx_dsp_SystemStable */
enum Tfa98xx_Error tfa98xx_dsp_system_stable_v6(struct tfa_device *tfa, int *ready)
{
	return (tfa->dev_ops.dsp_system_stable)(tfa, ready);
}

/* the ops wrapper for tfa98xx_dsp_system_stable_v6 */
enum Tfa98xx_Error tfa98xx_auto_copy_mtp_to_iic(struct tfa_device *tfa)
{
	return (tfa->dev_ops.auto_copy_mtp_to_iic)(tfa);
}

/* the ops wrapper for tfa98xx_faim_protect */
enum Tfa98xx_Error tfa98xx_faim_protect(struct tfa_device *tfa, int state)
{
	return (tfa->dev_ops.faim_protect)(tfa, state);
}

/*
 * bring the device into a state similar to reset
 */
enum Tfa98xx_Error tfa98xx_init_v6(struct tfa_device *tfa)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	uint16_t value=0;

	/* reset all i2C registers to default
	 *  Write the register directly to avoid the read in the bitfield function.
	 *  The I2CR bit may overwrite the full register because it is reset anyway.
	 *  This will save a reg read transaction.
	 */
	TFA_SET_BF_VALUE(tfa, I2CR, 1, &value );
	TFA_WRITE_REG(tfa, I2CR, value);

	/* Put DSP in reset */
	tfa98xx_dsp_reset_v6(tfa, 1); /* in pair of tfaRunStartDSP_v6() */

	/* some other registers must be set for optimal amplifier behaviour
	 * This is implemented in a file specific for the type number
	 */
	if (tfa->dev_ops.tfa_init)
		error = (tfa->dev_ops.tfa_init)(tfa);

	return error;
}

enum Tfa98xx_Error tfa98xx_dsp_write_tables_v6(struct tfa_device *tfa, int sample_rate)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;

	error = (tfa->dev_ops.dsp_write_tables)(tfa, sample_rate);

	return error;
}

/** Set internal oscillator into power down mode.
*
*  @param[in] tfa device description structure
*  @param[in] state new state 0 - oscillator is on, 1 oscillator is off.
*
*  @return Tfa98xx_Error_Ok when successfull, error otherwise.
*/
enum Tfa98xx_Error tfa98xx_set_osc_powerdown(struct tfa_device *tfa, int state)
{
	if (tfa->dev_ops.set_osc_powerdown) {
		return tfa->dev_ops.set_osc_powerdown(tfa, state);
	}

	return Tfa98xx_Error_Not_Implemented;
}

/*To support tfa9873*/
/** update low power mode of the device.
*
*  @param[in] tfa device description structure
*  @param[in] state new state 0 - LPMODE is on, 1 LPMODE is off.
*
*  @return Tfa98xx_Error_Ok when successfull, error otherwise.
*/
enum Tfa98xx_Error tfa98xx_update_lpm(struct tfa_device *tfa, int state)
{
	if (tfa->dev_ops.update_lpm) {
		return tfa->dev_ops.update_lpm(tfa, state);
	}

	return Tfa98xx_Error_Not_Implemented;
}
/** Check presence of powerswitch=1 in configuration and optimal setting.
*
*  @param[in] tfa device description structure
*
*  @return -1 when error, 0 or 1 depends on switch settings.
*/
int tfa98xx_powerswitch_is_enabled(struct tfa_device *tfa)
{
	uint16_t value;
	enum Tfa98xx_Error ret;

	if (((tfa->rev & 0xff) == 0x13) || ((tfa->rev & 0xff) == 0x88)) {
		ret = tfa_reg_read(tfa, 0xc6, &value);
		if (ret != Tfa98xx_Error_Ok) {
			return -1;
		}
		/* PLMA5539: Check actual value of powerswitch. TODO: regmap v1.40 should make this bit public. */

		return (int)(value & (1u << 6));
	}

	return 1;
}

/********************* new tfa2 *********************************************************************/
/* newly added messaging for tfa2 tfa1? */
enum Tfa98xx_Error tfa98xx_dsp_get_memory_v6(struct tfa_device *tfa, int memoryType,
		int offset, int length, unsigned char bytes[])
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	char msg[4*3];
	int nr = 0;

	msg[nr++] = 8;
	msg[nr++] = MODULE_FRAMEWORK + 128;
	msg[nr++] = FW_PAR_ID_GET_MEMORY;

	msg[nr++] = 0;
	msg[nr++] = 0;
	msg[nr++] = (char)memoryType;

	msg[nr++] = 0;
	msg[nr++] = (offset>>8) & 0xff;
	msg[nr++] = offset & 0xff;

	msg[nr++] = 0;
	msg[nr++] = (length>>8) & 0xff;
	msg[nr++] = length & 0xff;

	/* send msg */
	error = dsp_msg(tfa, nr, (char *)msg);

	if (error != Tfa98xx_Error_Ok)
		return error;

	/* read the data from the device (length * 3) */
	error = dsp_msg_read(tfa, length * 3, bytes);

	return error;
}

enum Tfa98xx_Error tfa98xx_dsp_set_memory_v6(struct tfa_device *tfa, int memoryType,
		int offset, int length, int value)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	int nr = 0;
	char msg[5*3];

	msg[nr++] = 8;
	msg[nr++] = MODULE_FRAMEWORK + 128;
	msg[nr++] = FW_PAR_ID_SET_MEMORY;

	msg[nr++] = 0;
	msg[nr++] = 0;
	msg[nr++] = (char)memoryType;

	msg[nr++] = 0;
	msg[nr++] = (offset>>8) & 0xff;
	msg[nr++] = offset & 0xff;

	msg[nr++] = 0;
	msg[nr++] = (length>>8) & 0xff;
	msg[nr++] = length & 0xff;

	msg[nr++] = (value>>16) & 0xff;
	msg[nr++] = (value>>8) & 0xff;
	msg[nr++] = value & 0xff;

	/* send msg */
	error = dsp_msg(tfa, nr, (char *)msg);

	return error;
}
/****************************** calibration support **************************/
/*
 * get/set the mtp with user controllable values
 *
 *  check if the relevant clocks are available
 */
enum Tfa98xx_Error tfa98xx_get_mtp_v6(struct tfa_device *tfa, uint16_t *value)
{
	int status;
	int result;

	/* not possible if PLL in powerdown */
	if ( TFA_GET_BF(tfa, PWDN) ) {
		pr_info("PLL in powerdown\n");
		return Tfa98xx_Error_NoClock;
	}

	tfa98xx_dsp_system_stable_v6(tfa, &status);
	if (status==0) {
		pr_info("PLL not running\n");
		return Tfa98xx_Error_NoClock;
	}

	result = TFA_READ_REG(tfa, MTP0);
	if (result <  0) {
		return -result;
	}
	*value = (uint16_t)result;

	return Tfa98xx_Error_Ok;
}

/*
 * lock or unlock KEY2
 *  lock = 1 will lock
 *  lock = 0 will unlock
 *
 *  note that on return all the hidden key will be off
 */
void tfa98xx_key2_v6(struct tfa_device *tfa, int lock)
{
	/* unhide lock registers */
	tfa_reg_write(tfa, (tfa->tfa_family == 1) ? 0x40 :0x0F, 0x5A6B);
	/* lock/unlock key2 MTPK */
	TFA_WRITE_REG(tfa, MTPKEY2, lock? 0 :0x5A );
	/* unhide lock registers */
	/*To support tfa9873*/
	if (!tfa->advance_keys_handling) /*artf65038*/
		tfa_reg_write(tfa, (tfa->tfa_family == 1) ? 0x40 : 0x0F, 0);
}

void tfa2_manual_mtp_cpy(struct tfa_device *tfa, uint16_t reg_row_to_keep, uint16_t reg_row_to_set, uint8_t row)///MCH_TO_TEST
{
	uint16_t value;
	int loop = 0;
	enum Tfa98xx_Error error;
/* Assure FAIM is enabled (enable it when neccesery) */
	if (tfa->is_probus_device)
	{
		error = tfa98xx_faim_protect(tfa, 1);
		if (tfa->verbose) {
			pr_debug("FAIM enabled (err:%d).\n", error);
		}
	}
	tfa_reg_read(tfa, (unsigned char)reg_row_to_keep, &value);
	if(!row)
	{
		tfa_reg_write(tfa, 0xA7 , value);
		tfa_reg_write(tfa, 0xA8 , reg_row_to_set);
	}
	else
	{
		tfa_reg_write(tfa, 0xA7 , reg_row_to_set);
		tfa_reg_write(tfa, 0xA8 , value);
	}
	tfa_reg_write(tfa, 0xA3 , 0x10 | row);
	if (tfa->is_probus_device)
	{
		/* Assure FAIM is enabled (enable it when neccesery) */
		for (loop = 0; loop < 100 /*x10ms*/; loop++) {
			msleep_interruptible(10); 			/* wait 10ms to avoid busload */
			if (tfa_dev_get_mtpb(tfa) == 0)
				break;
		}
		error = tfa98xx_faim_protect(tfa, 0);
		if (tfa->verbose) {
			pr_debug("FAIM disabled (err:%d).\n", error);
		}
	}
}

enum Tfa98xx_Error tfa98xx_set_mtp_v6(struct tfa_device *tfa, uint16_t value, uint16_t mask)
{
	unsigned short mtp_old, mtp_new;
	int loop, status;
	enum Tfa98xx_Error error;

	error = tfa98xx_get_mtp_v6(tfa, &mtp_old);

	if (error != Tfa98xx_Error_Ok)
		return error;

	mtp_new = (value & mask) | (mtp_old & ~mask);

	if ( mtp_old == mtp_new) /* no change */ {
		if (tfa->verbose)
			pr_info("No change in MTP. Value not written! \n");
		return Tfa98xx_Error_Ok;
	}
	error = tfa98xx_update_lpm(tfa, 1);
	if (error) {
		return error;
	}
	/* Assure FAIM is enabled (enable it when neccesery) */
	error = tfa98xx_faim_protect(tfa, 1);
	if (error) {
		return error;
	}
	if (tfa->verbose) {
		pr_debug("MTP clock enabled.\n");
	}

	/* assure that the clock is up, else we can't write MTP */
	error = tfa98xx_dsp_system_stable_v6(tfa, &status);
	if (error){
		return error;
	}
	if (status==0){
		return Tfa98xx_Error_NoClock;
	}

	tfa98xx_key2_v6(tfa, 0); /* unlock */
	TFA_WRITE_REG(tfa, MTP0, mtp_new); 	/* write to i2c shadow reg */
	/* CIMTP=1 start copying all the data from i2c regs_mtp to mtp*/
	if (tfa->tfa_family == 2)
		tfa2_manual_mtp_cpy(tfa, 0xF1, mtp_new, 0);
	else
		TFA_SET_BF(tfa, CIMTP, 1);
	/* wait until MTP write is done */
	error = Tfa98xx_Error_StateTimedOut;
	for(loop=0; loop<100 /*x10ms*/ ;loop++) {
		msleep_interruptible(10); 			/* wait 10ms to avoid busload */
		if (tfa_dev_get_mtpb(tfa) == 0) {
			error = Tfa98xx_Error_Ok;
			break;
		}
	}
	tfa98xx_key2_v6(tfa, 1); /* lock */
	/* MTP setting failed due to timeout ?*/
	if (error) {
		tfa98xx_faim_protect(tfa, 0);
		return error;
	}

	/* Disable the FAIM, if this is neccessary */
	error = tfa98xx_faim_protect(tfa, 0);
	if (error) {
		return error;
	}
	if (tfa->verbose) {
		pr_debug("MTP clock disabled.\n");
	}
	error = tfa98xx_update_lpm(tfa, 0);
	if (error) {
		return error;
	}
	return error;
}
/*
 * clear mtpex
 * set ACS
 * start tfa
 */
int tfa_calibrate_v6(struct tfa_device *tfa)
{
	enum Tfa98xx_Error error;

	/* clear mtpex */
	error = tfa98xx_set_mtp_v6(tfa, 0, TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK);
	if (error)
		return error ;

	/* set RST=1 to put the DSP in Reset */
	TFA_SET_BF(tfa, RST, 1);

	/* set ACS/coldboot state */
	error = tfaRunColdboot_v6(tfa, 1);

	/* start tfa by playing */
	return error;
}

static short twos(short x)
{
	 return (x<0)? x+512 : x;
}

void tfa98xx_set_exttemp_v6(struct tfa_device *tfa, short ext_temp)
{
	if ((-256 <= ext_temp) && (ext_temp <= 255)) {
		/* make twos complement */
		pr_debug("Using ext temp %d C\n", twos(ext_temp));
		TFA_SET_BF(tfa, TROS, 1);
		TFA_SET_BF(tfa, EXTTS, twos(ext_temp));
	} else {
		pr_debug("Clearing ext temp settings\n");
		TFA_SET_BF(tfa, TROS, 0);
	}
}
short tfa98xx_get_exttemp_v6(struct tfa_device *tfa)
{
	short ext_temp = (short)TFA_GET_BF(tfa, EXTTS);
	return (twos(ext_temp));
}

/************************** tfa simple bitfield interfacing ***************************************/
/* convenience functions */
enum Tfa98xx_Error tfa98xx_set_volume_level_v6(struct tfa_device *tfa, unsigned short vol)
{
	if(tfa->in_use == 0)
		return Tfa98xx_Error_NotOpen;

	if (vol > 255)	/* restricted to 8 bits */
		vol = 255;

	/* 0x00 -> 0.0 dB
	 * 0x01 -> -0.5 dB
	 * ...
	 * 0xFE -> -127dB
	 * 0xFF -> muted
	 */

	/* volume value is in the top 8 bits of the register */
	return -TFA_SET_BF(tfa, VOL, (uint16_t)vol);
}

#ifdef OPLUS_ARCH_EXTENDS
enum Tfa98xx_Error tfa98xx_set_ana_volume_v6(struct tfa_device *tfa, unsigned int vol)
{
	if(tfa->in_use == 0)
		return Tfa98xx_Error_NotOpen;

	if (vol > 15)	/* restricted to 8 bits */
		vol = 15;

	pr_info("%s: register: reg 0x%x\n", __func__, tfa->rev & 0xff);

	switch (tfa->rev & 0xff) {
	case 0x73: /* tfa9873 */
		return -tfa_set_bf_v6(tfa, TFA9873_BF_TDMSPKG, (uint16_t)vol);
	case 0x74: /* tfa9874 */
		return -tfa_set_bf_v6(tfa, TFA9874_BF_TDMSPKG, (uint16_t)vol);
	default:
		return Tfa98xx_Error_NotOpen;
	}
}

enum Tfa98xx_Error tfa98xx_get_ana_volume_v6(struct tfa_device *tfa, unsigned int *vol)
{
	if(tfa->in_use == 0)
		return Tfa98xx_Error_NotOpen;

	pr_info("%s: register: reg 0x%x\n", __func__, tfa->rev & 0xff);

	switch (tfa->rev & 0xff) {
	case 0x73: /* tfa9873 */
		*vol = tfa_get_bf_v6(tfa, TFA9873_BF_TDMSPKG);
		break;
	case 0x74: /* tfa9874 */
		*vol = tfa_get_bf_v6(tfa, TFA9874_BF_TDMSPKG);
		break;
	default:
		return Tfa98xx_Error_NotOpen;
	}

	return Tfa98xx_Error_Ok;
}
#endif /* OPLUS_ARCH_EXTENDS */

static enum Tfa98xx_Error
tfa98xx_set_mute_v6_tfa2(struct tfa_device *tfa, enum Tfa98xx_Mute mute)
{
	enum Tfa98xx_Error error;

	if (tfa->dev_ops.set_mute == NULL)
		return Tfa98xx_Error_Not_Supported;

	switch (mute) {
	case Tfa98xx_Mute_Off:
		error = tfa->dev_ops.set_mute(tfa, 0);
		TFA_SET_BF(tfa, AMPE, 1);
		break;
	case Tfa98xx_Mute_Amplifier:
	case Tfa98xx_Mute_Digital:
		error = tfa->dev_ops.set_mute(tfa, 1);
		TFA_SET_BF(tfa, AMPE, 0);
		break;
	default:
		return Tfa98xx_Error_Bad_Parameter;
	}

	return error;
}

static enum Tfa98xx_Error
tfa98xx_set_mute_v6_tfa1(struct tfa_device *tfa, enum Tfa98xx_Mute mute)
{
	enum Tfa98xx_Error error;
	unsigned short audioctrl_value;
	unsigned short sysctrl_value;
	int value;

	value = TFA_READ_REG(tfa, CFSM); /* audio control register */
	if (value < 0)
		return -value;
	audioctrl_value = (unsigned short)value;
	value = TFA_READ_REG(tfa, AMPE); /* system control register */
	if (value < 0)
		return -value;
	sysctrl_value = (unsigned short)value;

	switch (mute) {
	case Tfa98xx_Mute_Off:
		/* previous state can be digital or amplifier mute,
		 * clear the cf_mute and set the enbl_amplifier bits
		 *
		 * To reduce PLOP at power on it is needed to switch the
		 * amplifier on with the DCDC in follower mode
		 * (enbl_boost = 0 ?).
		 * This workaround is also needed when toggling the
		 * powerdown bit!
		 */
		TFA_SET_BF_VALUE(tfa, CFSM, 0, &audioctrl_value);
		TFA_SET_BF_VALUE(tfa, AMPE, 1, &sysctrl_value);
		TFA_SET_BF_VALUE(tfa, DCA, 1, &sysctrl_value);
		break;
	case Tfa98xx_Mute_Digital:
		/* expect the amplifier to run */
		/* set the cf_mute bit */
		TFA_SET_BF_VALUE(tfa, CFSM, 1, &audioctrl_value);
		/* set the enbl_amplifier bit */
		TFA_SET_BF_VALUE(tfa, AMPE, 1, &sysctrl_value);
		/* clear active mode */
		TFA_SET_BF_VALUE(tfa, DCA, 0, &sysctrl_value);
		break;
	case Tfa98xx_Mute_Amplifier:
		/* clear the cf_mute bit */
		TFA_SET_BF_VALUE(tfa, CFSM, 0, &audioctrl_value);
		/* clear the enbl_amplifier bit and active mode */
		TFA_SET_BF_VALUE(tfa, AMPE, 0, &sysctrl_value);
		TFA_SET_BF_VALUE(tfa, DCA, 0, &sysctrl_value);
		break;
	default:
		return Tfa98xx_Error_Bad_Parameter;
	}

	error = -TFA_WRITE_REG(tfa, CFSM, audioctrl_value);
	if (error)
		return error;
	error = -TFA_WRITE_REG(tfa, AMPE, sysctrl_value);
	return error;
}

enum Tfa98xx_Error
tfa98xx_set_mute_v6(struct tfa_device *tfa, enum Tfa98xx_Mute mute)
{
	if (tfa->in_use == 0) {
		pr_err("device is not opened \n");
		return Tfa98xx_Error_NotOpen;
	}

	if (tfa->tfa_family == 1)
		return tfa98xx_set_mute_v6_tfa1(tfa, mute);
	else
		return tfa98xx_set_mute_v6_tfa2(tfa, mute);
}

/****************** patching **********************************************************/
static enum Tfa98xx_Error
tfa98xx_process_patch_file(struct tfa_device *tfa, int length,
		 const unsigned char *bytes)
{
	unsigned short size;
	int index = 0;
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;

	while (index < length) {
		size = bytes[index] + bytes[index + 1] * 256;
		index += 2;
		if ((index + size) > length) {
			/* outside the buffer, error in the input data */
			return Tfa98xx_Error_Bad_Parameter;
		}

		if (size > tfa->buffer_size) {
			/* too big, must fit buffer */
			return Tfa98xx_Error_Bad_Parameter;
		}

		error = tfa98xx_write_raw_v6(tfa, size, &bytes[index]);
		if (error != Tfa98xx_Error_Ok)
			break;
		index += size;
	}
	return  error;
}



/* the patch contains a header with the following
 * IC revision register: 1 byte, 0xFF means don't care
 * XMEM address to check: 2 bytes, big endian, 0xFFFF means don't care
 * XMEM value to expect: 3 bytes, big endian
 */
static enum Tfa98xx_Error
tfa98xx_check_ic_rom_version(struct tfa_device *tfa, const unsigned char patchheader[])
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	unsigned short checkrev, revid;
	unsigned char lsb_revid;
	unsigned short checkaddress;
	int checkvalue;
	int value = 0;
	int status;
	checkrev = patchheader[0];
	lsb_revid = tfa->rev & 0xff; /* only compare lower byte */

	if ((checkrev != 0xFF) && (checkrev != lsb_revid))
		return Tfa98xx_Error_Not_Supported;

	checkaddress = (patchheader[1] << 8) + patchheader[2];
	checkvalue =
	    (patchheader[3] << 16) + (patchheader[4] << 8) + patchheader[5];
	if (checkaddress != 0xFFFF) {
		/* before reading XMEM, check if we can access the DSP */
		error = tfa98xx_dsp_system_stable_v6(tfa, &status);
		if (error == Tfa98xx_Error_Ok) {
			if (!status) {
				/* DSP subsys not running */
				error = Tfa98xx_Error_DSP_not_running;
			}
		}
		/* read register to check the correct ROM version */
		if (error == Tfa98xx_Error_Ok) {
			error = mem_read(tfa, checkaddress, 1, &value);
		}
		if (error == Tfa98xx_Error_Ok) {
			if (value != checkvalue) {
				pr_err("patch file romid type check failed [0x%04x]: expected 0x%02x, actual 0x%02x\n",
						checkaddress, value, checkvalue);
				error = Tfa98xx_Error_Not_Supported;
			}
		}
	} else { /* == 0xffff */
		/* check if the revid subtype is in there */
		if ( checkvalue != 0xFFFFFF && checkvalue != 0) {
			revid = patchheader[5]<<8 | patchheader[0]; /* full revid */
			if ( revid != tfa->rev) {
				pr_err("patch file device type check failed: expected 0x%02x, actual 0x%02x\n",
						tfa->rev, revid);
				return Tfa98xx_Error_Not_Supported;
			}
		}
	}

	return error;
}


#define PATCH_HEADER_LENGTH 6
enum Tfa98xx_Error
tfa_dsp_patch_v6(struct tfa_device *tfa, int patchLength,
		 const unsigned char *patchBytes)
{
	#ifndef OPLUS_ARCH_EXTENDS
	enum Tfa98xx_Error error;
	#else /* OPLUS_ARCH_EXTENDS */
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	int status;
	#endif /* OPLUS_ARCH_EXTENDS */
	if(tfa->in_use == 0)
		return Tfa98xx_Error_NotOpen;

	if (patchLength < PATCH_HEADER_LENGTH)
		return Tfa98xx_Error_Bad_Parameter;

	error = tfa98xx_check_ic_rom_version(tfa, patchBytes);
	if (Tfa98xx_Error_Ok != error) {
		return error;
	}
	#ifdef OPLUS_ARCH_EXTENDS
	tfa98xx_dsp_system_stable_v6(tfa, &status);
	if (!status) {
		pr_err("tfa no clock\n");
		return Tfa98xx_Error_NoClock;
	}
	/******MCH_TO_TEST**************/
	if ((error == Tfa98xx_Error_Ok) && (!tfa->is_probus_device)) {
		pr_info("tfa cold boot patch\n");
		error = tfaRunColdboot_v6(tfa, 1);
		if (error) {
			pr_err("tfa_dsp_patch DSP_not_running\n");
			return Tfa98xx_Error_DSP_not_running;
		}
	}
	/**************************/
	#endif /* OPLUS_ARCH_EXTENDS */

	error =
	    tfa98xx_process_patch_file(tfa, patchLength - PATCH_HEADER_LENGTH,
			     patchBytes + PATCH_HEADER_LENGTH);

	return error;
}

/******************  end patching **********************************************************/

TFA_INTERNAL enum Tfa98xx_Error
tfa98xx_wait_result_v6(struct tfa_device *tfa, int wait_retry_count)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	int cf_status; /* the contents of the CF_STATUS register */
	int tries = 0;
	do {
		cf_status = TFA_GET_BF(tfa, ACK);
		if (cf_status < 0)
			error = -cf_status;
		tries++;
	}
	// i2c_cmd_ack
	/* don't wait forever, DSP is pretty quick to respond (< 1ms) */
	while ((error == Tfa98xx_Error_Ok) && ((cf_status & CF_STATUS_I2C_CMD_ACK) == 0)
			&& (tries < wait_retry_count));
	if (tries >= wait_retry_count) {
		/* something wrong with communication with DSP */
		error = Tfa98xx_Error_DSP_not_running;
	}
	return error;
}

/*
 * *  support functions for data conversion
 */
/**
 convert memory bytes to signed 24 bit integers
   input:  bytes contains "num_bytes" byte elements
   output: data contains "num_bytes/3" int24 elements
*/
void tfa98xx_convert_bytes2data_v6(int num_bytes, const unsigned char bytes[],
			       int data[])
{
	int i;			/* index for data */
	int k;			/* index for bytes */
	int d;
	int num_data = num_bytes / 3;
	_ASSERT((num_bytes % 3) == 0);
	for (i = 0, k = 0; i < num_data; ++i, k += 3) {
		d = (bytes[k] << 16) | (bytes[k + 1] << 8) | (bytes[k + 2]);
		_ASSERT(d >= 0);
		_ASSERT(d < (1 << 24));	/* max 24 bits in use */
		if (bytes[k] & 0x80)	/* sign bit was set */
			d = -((1 << 24) - d);

		data[i] = d;
	}
}


/**
 convert signed 32 bit integers to 24 bit aligned bytes
   input:   data contains "num_data" int elements
   output:  bytes contains "3 * num_data" byte elements
*/
void tfa98xx_convert_data2bytes_v6(int num_data, const int data[],
			        unsigned char bytes[])
{
	int i;			/* index for data */
	int k;			/* index for bytes */
	int d;
	/* note: cannot just take the lowest 3 bytes from the 32 bit
	 * integer, because also need to take care of clipping any
	 * value > 2&23 */
	for (i = 0, k = 0; i < num_data; ++i, k += 3) {
		if (data[i] >= 0)
			d = MIN(data[i], (1 << 23) - 1);
		else {
			/* 2's complement */
			d = (1 << 24) - MIN(-data[i], 1 << 23);
		}
		_ASSERT(d >= 0);
		_ASSERT(d < (1 << 24));	/* max 24 bits in use */
		bytes[k] = (d >> 16) & 0xFF;	/* MSB */
		bytes[k + 1] = (d >> 8) & 0xFF;
		bytes[k + 2] = (d) & 0xFF;	/* LSB */
	}
}

/*
 *  DSP RPC message support functions
 *   depending on framework to be up and running
 *   need base i2c of memaccess (tfa1=0x70/tfa2=0x90)
 */


/* write dsp messages in function tfa_dsp_msg_v6() */
/*  note the 'old' write_parameter() was more efficient because all i2c was in one burst transaction */

//TODO properly handle bitfields: state should be restored! (now it will change eg dmesg field to xmem)
enum Tfa98xx_Error tfa_dsp_msg_v6_write_v6(struct tfa_device *tfa, int length, const char *buffer)
{
	int offset = 0;
	int chunk_size = ROUND_DOWN(tfa->buffer_size, 3);  /* XMEM word size */
	int remaining_bytes = length;
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	uint16_t cfctl;
	int value;

	value = TFA_READ_REG(tfa, DMEM);
	if (value < 0) {
		error = -value;
		return error;
	}
	cfctl = (uint16_t)value;
	/* assume no I2C errors from here */

	TFA_SET_BF_VALUE(tfa, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM, &cfctl); /* set cf ctl to DMEM  */
	TFA_SET_BF_VALUE(tfa, AIF, 0, &cfctl ); /* set to autoincrement */
	TFA_WRITE_REG(tfa, DMEM, cfctl);

	/* xmem[1] is start of message
		*  direct write to register to save cycles avoiding read-modify-write
		*/
	TFA_WRITE_REG(tfa, MADD, 1);

	/* due to autoincrement in cf_ctrl, next write will happen at
	 * the next address */
	while ((error == Tfa98xx_Error_Ok) && (remaining_bytes > 0)) {
		if (remaining_bytes < chunk_size)
			chunk_size = remaining_bytes;
		/* else chunk_size remains at initialize value above */
		error = tfa98xx_write_data_v6(tfa, FAM_TFA98XX_CF_MEM,
				      chunk_size, (const unsigned char *)buffer + offset);
		remaining_bytes -= chunk_size;
		offset += chunk_size;
	}

	/* notify the DSP */
	if (error == Tfa98xx_Error_Ok) {
		/* cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */
		/* set the cf_req1 and cf_int bit */
		TFA_SET_BF_VALUE(tfa, REQCMD, 0x01, &cfctl ); /* bit 0 */
		TFA_SET_BF_VALUE(tfa, CFINT, 1, &cfctl );
		error = -TFA_WRITE_REG(tfa, CFINT, cfctl);
	}

	return error;
}

enum Tfa98xx_Error tfa_dsp_msg_v6_write_v6_id_v6(struct tfa_device *tfa, int length, const char *buffer, uint8_t cmdid[3])
{
        int offset = 0;
	int chunk_size = ROUND_DOWN(tfa->buffer_size, 3);  /* XMEM word size */
	int remaining_bytes = length;
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	uint16_t cfctl;
	int value;

	value = TFA_READ_REG(tfa, DMEM);
	if (value < 0) {
		error = -value;
		return error;
	}
	cfctl = (uint16_t)value;
	/* assume no I2C errors from here */

	TFA_SET_BF_VALUE(tfa, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM, &cfctl); /* set cf ctl to DMEM  */
	TFA_SET_BF_VALUE(tfa, AIF, 0, &cfctl ); /* set to autoincrement */
	TFA_WRITE_REG(tfa, DMEM, cfctl);

	/* xmem[1] is start of message
	 *  direct write to register to save cycles avoiding read-modify-write
	 */
	TFA_WRITE_REG(tfa, MADD, 1);

	/* write cmd-id */
	error = tfa98xx_write_data_v6(tfa, FAM_TFA98XX_CF_MEM, 3, (const unsigned char *)cmdid);

	/* due to autoincrement in cf_ctrl, next write will happen at
	 * the next address */
	while ((error == Tfa98xx_Error_Ok) && (remaining_bytes > 0)) {
		if (remaining_bytes < chunk_size)
			chunk_size = remaining_bytes;
		/* else chunk_size remains at initialize value above */
		error = tfa98xx_write_data_v6(tfa, FAM_TFA98XX_CF_MEM,
				      chunk_size, (const unsigned char *)buffer + offset);
		remaining_bytes -= chunk_size;
		offset += chunk_size;
	}

	/* notify the DSP */
	if (error == Tfa98xx_Error_Ok) {
		/* cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */
		/* set the cf_req1 and cf_int bit */
		TFA_SET_BF_VALUE(tfa, REQCMD, 0x01, &cfctl ); /* bit 0 */
		TFA_SET_BF_VALUE(tfa, CFINT, 1, &cfctl );
		error = -TFA_WRITE_REG(tfa, CFINT, cfctl);
	}

	return error;
}

/*
* status function used by tfa_dsp_msg_v6() to retrieve command/msg status:
* return a <0 status of the DSP did not ACK.
*/
enum Tfa98xx_Error tfa_dsp_msg_v6_status(struct tfa_device *tfa, int *pRpcStatus)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;

	error = tfa98xx_wait_result_v6(tfa, 2); /* 2 is only one try */
	if (error == Tfa98xx_Error_DSP_not_running) {
		*pRpcStatus = -1;
		return Tfa98xx_Error_Ok;
	}
	else if (error != Tfa98xx_Error_Ok)
		return error;

	error = tfa98xx_check_rpc_status_v6(tfa, pRpcStatus);

	return error;
}

const char* tfa98xx_get_i2c_status_id_string_v6(int status)
{
        const char* p_id_str;

        switch (status)
        {
                case Tfa98xx_DSP_Not_Running:
                        p_id_str = "No response from DSP";
                        break;
                case Tfa98xx_I2C_Req_Done:
                        p_id_str = "Ok";
                        break;
                case Tfa98xx_I2C_Req_Busy:
                        p_id_str = "Request is being processed";
                        break;
                case Tfa98xx_I2C_Req_Invalid_M_ID:
                        p_id_str = "Provided M-ID does not fit in valid rang [0..2]";
                        break;
                case Tfa98xx_I2C_Req_Invalid_P_ID:
                        p_id_str = "Provided P-ID is not valid in the given M-ID context";
                        break;
                case Tfa98xx_I2C_Req_Invalid_CC:
                        p_id_str = "Invalid channel configuration bits (SC|DS|DP|DC) combination";
                        break;
                case Tfa98xx_I2C_Req_Invalid_Seq:
                        p_id_str = "Invalid sequence of commands, in case the DSP expects some commands in a specific order";
                        break;
                case Tfa98xx_I2C_Req_Invalid_Param:
                        p_id_str = "Generic error, invalid parameter";
                        break;
                case Tfa98xx_I2C_Req_Buffer_Overflow:
                        p_id_str = "I2C buffer has overflowed: host has sent too many parameters, memory integrity is not guaranteed";
                        break;
                case Tfa98xx_I2C_Req_Calib_Busy:
                        p_id_str = "Calibration not completed";
                        break;
                case Tfa98xx_I2C_Req_Calib_Failed:
                        p_id_str = "Calibration failed";
                        break;

                default:
                        p_id_str = "Unspecified error";
        }

        return p_id_str;
}

enum Tfa98xx_Error tfa_dsp_msg_v6_read_v6(struct tfa_device *tfa, int length, unsigned char *bytes)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	int burst_size;		/* number of words per burst size */
	int bytes_per_word = 3;
	int num_bytes;
	int offset = 0;
	unsigned short start_offset=2; /* msg starts @xmem[2] ,[1]=cmd */

	if ( length > TFA2_MAX_PARAM_SIZE)
		return Tfa98xx_Error_Bad_Parameter;

	TFA_SET_BF(tfa, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM);
	error = -TFA_WRITE_REG(tfa, MADD, start_offset);
	if (error != Tfa98xx_Error_Ok)
		return error;

	num_bytes = length; /* input param */
	while (num_bytes > 0) {
		burst_size = ROUND_DOWN(tfa->buffer_size, bytes_per_word);
		if (num_bytes < burst_size)
			burst_size = num_bytes;
		error = tfa98xx_read_data_v6(tfa, FAM_TFA98XX_CF_MEM, burst_size, bytes + offset);
		if (error != Tfa98xx_Error_Ok)
			return error;

		num_bytes -= burst_size;
		offset += burst_size;
	}

	return error;
}

enum Tfa98xx_Error dsp_msg(struct tfa_device *tfa, int length24, const char *buf24)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	int lastmessage=0;
	uint8_t *blob;
	int i;
	int *intbuf = NULL;
	char* buf = (char *)buf24;
	int length = length24;

	if (tfa->convert_dsp32) {
		int idx = 0;

		length = 4 * length24 / 3;
		intbuf = kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
		if (!intbuf) {
			pr_err("%s: kmem alloc fail! \n", __func__);
			return Tfa98xx_Error_Fail;
		}
		buf = (char *)intbuf;

		/* convert 24 bit DSP messages to a 32 bit integer */
		for (i=0; i<length24; i+=3) {
			int tmp = (buf24[i] << 16) + (buf24[i+1] << 8) + buf24[i+2];
			/* Sign extend to 32-bit from 24-bit */
			intbuf[idx++] = ((int32_t)tmp << 8) >> 8;
		}
	}

	/* Only create multi-msg when the dsp is cold */
	if(tfa->ext_dsp == 1) {
		/* Creating the multi-msg */
		error = tfa_tib_dsp_msgmulti(tfa, length, buf);
		if (error == Tfa98xx_Error_Fail) {
			if (intbuf) {
				kmem_cache_free(tfa->cachep, intbuf);
				intbuf = NULL;
			}
			return Tfa98xx_Error_Fail;
		}

		/* if the buffer is full we need to send the existing message and add the current message */
		if(error == Tfa98xx_Error_Buffer_too_small) {
			int len;

			/* (a) send the existing (full) message */
			blob = kmalloc(64*1024, GFP_KERNEL); // max length is 64k
			len = tfa_tib_dsp_msgmulti(tfa, -1, (const char*)blob);
			if (tfa->verbose) {
				pr_debug("Multi-message buffer full. Sending multi-message, length=%d \n", len);
			}
			if (tfa->has_msg==0 ) /* via i2c */ {
				/* Send tot the target selected */
				error = (tfa->dev_ops.dsp_msg)(tfa, len, (const char*)blob);
			} else { /* via msg hal */
				error = tfa98xx_write_dsp(tfa, len, (const char*)blob);
			}
			kfree(blob);

			/* (b) add the current DSP message to a new multi-message */
			error = tfa_tib_dsp_msgmulti(tfa, length, buf);
			if (error == Tfa98xx_Error_Fail) {
				if (intbuf) {
					kmem_cache_free(tfa->cachep, intbuf);
					intbuf = NULL;
				}
				return Tfa98xx_Error_Fail;
			}
		}

		lastmessage = error;

		/* When the lastmessage is done we can send the multi-msg to the target */
		if(lastmessage == 1) {

			/* Get the full multi-msg data */
			blob = kmalloc(64*1024, GFP_KERNEL); //max length is 64k
			length = tfa_tib_dsp_msgmulti(tfa, -1, (const char*)blob);

			if (tfa->verbose)
				pr_debug("Last message for the multi-message received. Multi-message length=%d \n", length);

			if (tfa->has_msg==0 ) /* via i2c */ {
				/* Send tot the target selected */
				error = (tfa->dev_ops.dsp_msg)(tfa, length, (const char*)blob);
			} else { /* via msg hal */
				error = tfa98xx_write_dsp(tfa, length, (const char*)blob);
			}

			kfree(blob); /* Free the kmalloc blob */
			lastmessage = 0; /* reset to be able to re-start */
		}
	} else {
		if (tfa->has_msg==0 ) /* via i2c */ {
			error = (tfa->dev_ops.dsp_msg)(tfa, length, buf);
		} else { /* via msg hal */
			error = tfa98xx_write_dsp(tfa, length, (const char*)buf);
		}
	}

	if(error != Tfa98xx_Error_Ok)
		error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */

	/* DSP verbose has argument 0x04 */
	if((tfa->verbose & 0x04)!=0) {
		pr_debug("DSP w [%d]: ", length);
		for(i=0; i<length; i++)
			pr_debug("0x%02x ", (uint8_t)buf[i]);
		pr_debug("\n");
	}

	if (intbuf) {
		kmem_cache_free(tfa->cachep, intbuf);
		intbuf = NULL;
	}

	return error;
}

enum Tfa98xx_Error dsp_msg_read(struct tfa_device *tfa, int length24, unsigned char *bytes24)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	int i;
	int length = length24;
	unsigned char *bytes = bytes24;

	if (tfa->convert_dsp32) {
		length = 4 * length24 / 3;
		bytes = kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
		if (!bytes) {
			pr_err("%s: kmem alloc for bytes fail \n", __func__);
			return Tfa98xx_Error_Fail;
		}
	}

	if (tfa->has_msg==0) /* via i2c */ {
		error = (tfa->dev_ops.dsp_msg_read)(tfa, length, bytes);
	} else { /* via msg hal */
		error = tfa98xx_read_dsp(tfa, length, bytes);
	}

	if(error != Tfa98xx_Error_Ok)
		error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */

	/* DSP verbose has argument 0x04 */
	if((tfa->verbose & 0x04)!=0) {
		pr_debug("DSP R [%d]: ", length);
		for(i=0; i<length; i++)
			pr_debug("0x%02x ", (uint8_t)bytes[i]);
		pr_debug("\n");
	}

	if (tfa->convert_dsp32) {
		int idx = 0;

		/* convert 32 bit LE to 24 bit BE */
		for (i=0; i<length; i+=4) {
			bytes24[idx++] = bytes[i + 2];
			bytes24[idx++] = bytes[i + 1];
			bytes24[idx++] = bytes[i + 0];
		}

		kmem_cache_free(tfa->cachep, bytes);
	}

	return error;
}

enum Tfa98xx_Error tfa_reg_read(struct tfa_device *tfa, unsigned char subaddress, unsigned short *value)
{
	enum Tfa98xx_Error error;

	#ifdef OPLUS_ARCH_EXTENDS
	if (tfa->dev_ops.tfa_reg_read == NULL) {
		pr_err("%s: not init yet!", __func__);
		return Tfa98xx_Error_Fail;
	}
	#endif /* OPLUS_ARCH_EXTENDS */

	error = (tfa->dev_ops.tfa_reg_read)(tfa, subaddress, value);
	if(error != Tfa98xx_Error_Ok)
		error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */

	return error;
}

enum Tfa98xx_Error tfa_reg_write(struct tfa_device *tfa, unsigned char subaddress, unsigned short value)
{
	enum Tfa98xx_Error error;

	#ifdef OPLUS_ARCH_EXTENDS
	if (tfa->dev_ops.tfa_reg_write == NULL) {
		pr_err("%s: not init yet!", __func__);
		return Tfa98xx_Error_Fail;
	}
	#endif /* OPLUS_ARCH_EXTENDS */

	error = (tfa->dev_ops.tfa_reg_write)(tfa, subaddress, value);
	if(error != Tfa98xx_Error_Ok)
		error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */

	return error;
}

enum Tfa98xx_Error mem_read(struct tfa_device *tfa, unsigned int start_offset, int num_words, int *pValues)
{
	enum Tfa98xx_Error error;

	error = (tfa->dev_ops.mem_read)(tfa, start_offset, num_words, pValues);
	if(error != Tfa98xx_Error_Ok)
		error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */

	return error;
}

enum Tfa98xx_Error mem_write(struct tfa_device *tfa, unsigned short address, int value, int memtype)
{
	enum Tfa98xx_Error error;

	error = (tfa->dev_ops.mem_write)(tfa, address, value, memtype);
	if(error != Tfa98xx_Error_Ok)
		error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */

	return error;
}


/*
 *  write/read raw msg functions :
 *  the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes.
 *  The functions will return immediately and do not not wait for DSP reponse.
 */
#define MAX_WORDS (300)
enum Tfa98xx_Error tfa_dsp_msg_v6(struct tfa_device *tfa, int length, const char *buf)
{
	enum Tfa98xx_Error error;
	int tries, rpc_status = Tfa98xx_I2C_Req_Done;

	/* write the message and notify the DSP */
	error = tfa_dsp_msg_v6_write_v6(tfa, length, buf);
	if( error != Tfa98xx_Error_Ok)
		return error;

	/* get the result from the DSP (polling) */
	for(tries=TFA98XX_WAITRESULT_NTRIES; tries>0;tries--) {
		error = tfa_dsp_msg_v6_status(tfa, &rpc_status);
                if (error == Tfa98xx_Error_Ok && rpc_status == Tfa98xx_I2C_Req_Done)
			break;
		/* If the rpc status is a specific error we want to know it.
		 * If it is busy or not running it should retry
		 */
		if(rpc_status != Tfa98xx_I2C_Req_Busy && rpc_status != Tfa98xx_DSP_Not_Running)
			break;
	}

	if (rpc_status != Tfa98xx_I2C_Req_Done) {
		/* DSP RPC call returned an error */
		error = (enum Tfa98xx_Error) (rpc_status + Tfa98xx_Error_RpcBase);
                pr_debug("DSP msg status: %d (%s)\n", rpc_status, tfa98xx_get_i2c_status_id_string_v6(rpc_status));
	}
	return error;
}

/**
 *  write/read raw msg functions:
 *  the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes.
 *  The functions will return immediately and do not not wait for DSP reponse.
 *  An ID is added to modify the command-ID
 */
enum Tfa98xx_Error tfa_dsp_msg_v6_id_v6(struct tfa_device *tfa, int length, const char *buf, uint8_t cmdid[3])
{
	enum Tfa98xx_Error error;
	int tries, rpc_status = Tfa98xx_I2C_Req_Done;

	/* write the message and notify the DSP */
	error = tfa_dsp_msg_v6_write_v6_id_v6(tfa, length, buf, cmdid);
	if( error != Tfa98xx_Error_Ok)
		return error;

	/* get the result from the DSP (polling) */
	for(tries=TFA98XX_WAITRESULT_NTRIES; tries>0;tries--) {
		error = tfa_dsp_msg_v6_status(tfa, &rpc_status);
                if (error == Tfa98xx_Error_Ok && rpc_status == Tfa98xx_I2C_Req_Done)
			break;
	}

	if (rpc_status != Tfa98xx_I2C_Req_Done) {
		/* DSP RPC call returned an error */
		error = (enum Tfa98xx_Error) (rpc_status + Tfa98xx_Error_RpcBase);
                pr_debug("DSP msg status: %d (%s)\n", rpc_status, tfa98xx_get_i2c_status_id_string_v6(rpc_status));
	}
	return error;
}

/* read the return code for the RPC call */
TFA_INTERNAL enum Tfa98xx_Error
tfa98xx_check_rpc_status_v6(struct tfa_device *tfa, int *pRpcStatus)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	/* the value to sent to the * CF_CONTROLS register: cf_req=00000000,
	 * cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */
	unsigned short cf_ctrl = 0x0002;
	/* memory address to be accessed (0: Status, 1: ID, 2: parameters) */
	unsigned short cf_mad = 0x0000;

	if(tfa->in_use == 0)
		return Tfa98xx_Error_NotOpen;
	if (pRpcStatus == NULL)
		return Tfa98xx_Error_Bad_Parameter;

	/* 1) write DMEM=XMEM to the DSP XMEM */
	{
		/* minimize the number of I2C transactions by making use of the autoincrement in I2C */
		unsigned char buffer[4];
		/* first the data for CF_CONTROLS */
		buffer[0] = (unsigned char)((cf_ctrl >> 8) & 0xFF);
		buffer[1] = (unsigned char)(cf_ctrl & 0xFF);
		/* write the contents of CF_MAD which is the subaddress following CF_CONTROLS */
		buffer[2] = (unsigned char)((cf_mad >> 8) & 0xFF);
		buffer[3] = (unsigned char)(cf_mad & 0xFF);
		error = tfa98xx_write_data_v6(tfa, FAM_TFA98XX_CF_CONTROLS, sizeof(buffer), buffer);
	}
	if (error == Tfa98xx_Error_Ok) {
		/* read 1 word (24 bit) from XMEM */
		error = tfa98xx_dsp_read_mem_v6(tfa, 0, 1, pRpcStatus);
	}

	return error;
}

/***************************** xmem only **********************************/
enum Tfa98xx_Error
tfa98xx_dsp_read_mem_v6(struct tfa_device *tfa,
		unsigned int start_offset, int num_words, int *pValues)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	unsigned char *bytes;
	int burst_size;		/* number of words per burst size */
	const int bytes_per_word = 3;
	int dmem;
	int num_bytes;
	int *p;

	bytes = (unsigned char *)kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
	if (bytes == NULL)
		return Tfa98xx_Error_Fail;

	/* If no offset is given, assume XMEM! */
	if(((start_offset>>16) & 0xf) > 0 )
		dmem = (start_offset>>16) & 0xf;
	else
		dmem = Tfa98xx_DMEM_XMEM;

	/* Remove offset from adress */
	start_offset = start_offset & 0xffff;
	num_bytes = num_words * bytes_per_word;
	p = pValues;

	TFA_SET_BF(tfa, DMEM, (uint16_t)dmem);
	error = -TFA_WRITE_REG(tfa, MADD, (unsigned short)start_offset);
	if (error != Tfa98xx_Error_Ok)
		goto tfa98xx_dsp_read_mem_v6_exit;

	for (; num_bytes > 0;) {
		burst_size = ROUND_DOWN(tfa->buffer_size, bytes_per_word);
		if (num_bytes < burst_size)
			burst_size = num_bytes;

		_ASSERT(burst_size <= sizeof(bytes));
		error = tfa98xx_read_data_v6(tfa, FAM_TFA98XX_CF_MEM, burst_size, bytes);
		if (error != Tfa98xx_Error_Ok)
			goto tfa98xx_dsp_read_mem_v6_exit;

		tfa98xx_convert_bytes2data_v6(burst_size, bytes, p);

		num_bytes -= burst_size;
		p += burst_size / bytes_per_word;
	}

tfa98xx_dsp_read_mem_v6_exit:
	kmem_cache_free(tfa->cachep, bytes);

	return error;
}


enum Tfa98xx_Error
tfa98xx_dsp_write_mem_word_v6(struct tfa_device *tfa, unsigned short address, int value, int memtype)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	unsigned char bytes[3];

	TFA_SET_BF(tfa, DMEM, (uint16_t)memtype);

	error = -TFA_WRITE_REG(tfa, MADD, address);
	if (error != Tfa98xx_Error_Ok)
		return error;

	tfa98xx_convert_data2bytes_v6(1, &value, bytes);
	error = tfa98xx_write_data_v6(tfa, FAM_TFA98XX_CF_MEM, 3, bytes);

	return error;
}

enum Tfa98xx_Error tfa_cont_write_filterbank_v6(struct tfa_device *tfa, nxpTfaFilter_t *filter)
{
	unsigned char biquad_index;
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;

	for(biquad_index=0;biquad_index<10;biquad_index++) {
		if (filter[biquad_index].enabled ) {
			error = tfa_dsp_cmd_id_write_v6(tfa, MODULE_BIQUADFILTERBANK,
					biquad_index+1, //start @1
					sizeof(filter[biquad_index].biquad.bytes),
						filter[biquad_index].biquad.bytes);
		} else {
			error = Tfa98xx_DspBiquad_Disable_v6(tfa, biquad_index+1);
		}
		if (error) return error;

	}

	return error;
}

enum Tfa98xx_Error
Tfa98xx_DspBiquad_Disable_v6(struct tfa_device *tfa, int biquad_index)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	int coeff_buffer[BIQUAD_COEFF_SIZE];
	unsigned char bytes[3 + BIQUAD_COEFF_SIZE * 3];
	int nr = 0;

	if (biquad_index > TFA98XX_BIQUAD_NUM)
		return Tfa98xx_Error_Bad_Parameter;
	if (biquad_index < 1)
		return Tfa98xx_Error_Bad_Parameter;

	/* make opcode */
	bytes[nr++] = 0;
	bytes[nr++] = MODULE_BIQUADFILTERBANK+128;
	bytes[nr++] = (unsigned char)biquad_index;


	/* set in correct order and format for the DSP */
	coeff_buffer[0] = (int) - 8388608;	/* -1.0f */
	coeff_buffer[1] = 0;
	coeff_buffer[2] = 0;
	coeff_buffer[3] = 0;
	coeff_buffer[4] = 0;
	coeff_buffer[5] = 0;

	/* convert to packed 24 */
	tfa98xx_convert_data2bytes_v6(BIQUAD_COEFF_SIZE, coeff_buffer, &bytes[nr]);
	nr += BIQUAD_COEFF_SIZE * 3;

	error = dsp_msg(tfa, nr, (char *)bytes);

	return error;
}

/* wrapper for dsp_msg that adds opcode */
enum Tfa98xx_Error tfa_dsp_cmd_id_write_v6(struct tfa_device *tfa,
			   unsigned char module_id,
			   unsigned char param_id, int num_bytes,
                           const unsigned char data[])
{
	enum Tfa98xx_Error error;
	unsigned char *buffer;
	int nr = 0;

	buffer = kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
	if (buffer == NULL)
		return Tfa98xx_Error_Fail;

	buffer[nr++] = tfa->spkr_select;
	buffer[nr++] = module_id + 128;
	buffer[nr++] = param_id;

	memcpy(&buffer[nr], data, num_bytes);
	nr += num_bytes;

	error = dsp_msg(tfa, nr, (char *)buffer);

	kmem_cache_free(tfa->cachep, buffer);

	return error;
}

/* wrapper for dsp_msg that adds opcode */
/* this is as the former tfa98xx_dsp_get_param() */
enum Tfa98xx_Error tfa_dsp_cmd_id_write_read_v6(struct tfa_device *tfa,
			   unsigned char module_id,
			   unsigned char param_id, int num_bytes,
                           unsigned char data[])
{
	enum Tfa98xx_Error error;
	unsigned char buffer[3];
	int nr = 0;

	if (num_bytes <= 0) {
		pr_debug("Error: The number of READ bytes is smaller or equal to 0! \n");
		return Tfa98xx_Error_Fail;
	}

	if ((tfa->is_probus_device) && (tfa->cnt->ndev == 1) &&
		(param_id == SB_PARAM_GET_RE25C ||
		 param_id == SB_PARAM_GET_LSMODEL ||
		 param_id == SB_PARAM_GET_ALGO_PARAMS)) {
		/* Modifying the ID for GetRe25C */
		buffer[nr++] = 4;
	} else {
		buffer[nr++] = tfa->spkr_select;
	}
	buffer[nr++] = module_id + 128;
	buffer[nr++] = param_id;

	error = dsp_msg(tfa, nr, (char *)buffer);
	if (error != Tfa98xx_Error_Ok)
		return error;

	/* read the data from the dsp */
	error = dsp_msg_read(tfa, num_bytes, data);
	return error;
}

/* wrapper for dsp_msg that adds opcode and 3 bytes required for coefs */
enum Tfa98xx_Error tfa_dsp_cmd_id_coefs_v6(struct tfa_device *tfa,
			   unsigned char module_id,
			   unsigned char param_id, int num_bytes,
			   unsigned char data[])
{
	enum Tfa98xx_Error error;
	unsigned char buffer[2*3];
	int nr = 0;

	buffer[nr++] = tfa->spkr_select;
	buffer[nr++] = module_id + 128;
	buffer[nr++] = param_id;

        buffer[nr++] = 0;
        buffer[nr++] = 0;
        buffer[nr++] = 0;

	error = dsp_msg(tfa, nr, (char *)buffer);
	if (error != Tfa98xx_Error_Ok)
		return error;

	/* read the data from the dsp */
	error = dsp_msg_read(tfa, num_bytes, data);

	return error;
}

/* wrapper for dsp_msg that adds opcode and 3 bytes required for MBDrcDynamics */
enum Tfa98xx_Error tfa_dsp_cmd_id_MBDrc_dynamics_v6(struct tfa_device *tfa,
			   unsigned char module_id,
			   unsigned char param_id, int index_subband,
			   int num_bytes, unsigned char data[])
{
	enum Tfa98xx_Error error;
	unsigned char buffer[2 * 3];
	int nr = 0;

	buffer[nr++] = tfa->spkr_select;
	buffer[nr++] = module_id + 128;
	buffer[nr++] = param_id;

        buffer[nr++] = 0;
        buffer[nr++] = 0;
        buffer[nr++] = (unsigned char)index_subband;

	error = dsp_msg(tfa, nr, (char *)buffer);
	if(error != Tfa98xx_Error_Ok)
		return error;

	/* read the data from the dsp */
	error = dsp_msg_read(tfa, num_bytes, data);

	return error;
}

enum Tfa98xx_Error
tfa98xx_dsp_write_preset_v6_v6(struct tfa_device *tfa, int length,
		       const unsigned char *p_preset_bytes)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	if (p_preset_bytes != NULL) {
		/* by design: keep the data opaque and no
		 * interpreting/calculation */
		error = tfa_dsp_cmd_id_write_v6(tfa, MODULE_SPEAKERBOOST,
					SB_PARAM_SET_PRESET, length,
					p_preset_bytes);
	} else {
		error = Tfa98xx_Error_Bad_Parameter;
	}
	return error;
}

/*
 * get features from MTP
 */
enum Tfa98xx_Error
tfa98xx_dsp_get_hw_feature_bits_v6(struct tfa_device *tfa, int *features)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	uint32_t value;
	uint16_t mtpbf;

	/* return the cache data if it's valid */
	if (tfa->hw_feature_bits != -1) {
		*features = tfa->hw_feature_bits;
	} else {
		/* for tfa1 check if we have clock */
		if (tfa->tfa_family == 1) {
			int status;
			tfa98xx_dsp_system_stable_v6(tfa, &status);
			if (!status) {
				get_hw_features_from_cnt_v6(tfa, features);
				/* skip reading MTP: */
				return (*features == -1) ? Tfa98xx_Error_Fail : Tfa98xx_Error_Ok;
			}
			mtpbf=0x850f;  /* MTP5 for tfa1,16 bits */
		} else
			mtpbf=0xf907;  /* MTP9 for tfa2, 8 bits */
		value = tfa_read_reg_v6(tfa, mtpbf) & 0xffff;
		*features = tfa->hw_feature_bits = value;
	}

	return error;
}

enum Tfa98xx_Error
tfa98xx_dsp_get_sw_feature_bits_v6(struct tfa_device *tfa, int features[2])
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	const int byte_size = 2 * 3;
	unsigned char bytes[2 * 3];

	/* return the cache data if it's valid */
	if (tfa->sw_feature_bits[0] != -1) {
		features[0] = tfa->sw_feature_bits[0];
		features[1] = tfa->sw_feature_bits[1];
	} else {
		/* for tfa1 check if we have clock */
		if (tfa->tfa_family == 1) {
			int status;
			tfa98xx_dsp_system_stable_v6(tfa, &status);
			if (!status) {
				get_sw_features_from_cnt_v6(tfa, features);
				/* skip reading MTP: */
				return (features[0] == -1) ? Tfa98xx_Error_Fail : Tfa98xx_Error_Ok;
			}
		}
		error = tfa_dsp_cmd_id_write_read_v6(tfa, MODULE_FRAMEWORK,
				FW_PAR_ID_GET_FEATURE_INFO, byte_size, bytes);

		if (error != Tfa98xx_Error_Ok) {
			/* old ROM code may respond with Tfa98xx_Error_RpcParamId */
			return error;
		}

		tfa98xx_convert_bytes2data_v6(byte_size, bytes, features);
	}
	return error;
}

enum Tfa98xx_Error tfa98xx_dsp_get_state_info_v6(struct tfa_device *tfa, unsigned char bytes[], unsigned int *statesize)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
        int bSupportFramework = 0;
        unsigned int stateSize = 9;

	err = tfa98xx_dsp_support_framework_v6(tfa, &bSupportFramework);
	if (err == Tfa98xx_Error_Ok) {
		if (bSupportFramework) {
			err = tfa_dsp_cmd_id_write_read_v6(tfa, MODULE_FRAMEWORK,
				FW_PARAM_GET_STATE, 3 * stateSize, bytes);
		} else {
			/* old ROM code, ask SpeakerBoost and only do first portion */
			stateSize = 8;
			err = tfa_dsp_cmd_id_write_read_v6(tfa, MODULE_SPEAKERBOOST,
				SB_PARAM_GET_STATE, 3 * stateSize, bytes);
		}
	}

	*statesize = stateSize;

	return err;
}

enum Tfa98xx_Error tfa98xx_dsp_support_drc_v6(struct tfa_device *tfa, int *pbSupportDrc)
{
    enum Tfa98xx_Error error = Tfa98xx_Error_Ok;

    *pbSupportDrc = 0;

    if(tfa->in_use == 0)
        return Tfa98xx_Error_NotOpen;
    if (tfa->supportDrc != supportNotSet) {
        *pbSupportDrc = (tfa->supportDrc == supportYes);
    } else {
        int featureBits[2];

        error = tfa98xx_dsp_get_sw_feature_bits_v6(tfa, featureBits);
        if (error == Tfa98xx_Error_Ok) {
            /* easy case: new API available */
            /* bit=0 means DRC enabled */
            *pbSupportDrc = (featureBits[0] & FEATURE1_DRC) == 0;
        } else if (error == Tfa98xx_Error_RpcParamId) {
            /* older ROM code, doesn't support it */
            *pbSupportDrc = 0;
            error = Tfa98xx_Error_Ok;
        }
        /* else some other error, return transparently */
        /* pbSupportDrc only changed when error == Tfa98xx_Error_Ok */

        if (error == Tfa98xx_Error_Ok) {
        	tfa->supportDrc = *pbSupportDrc ? supportYes : supportNo;
        }
    }
    return error;
}

enum Tfa98xx_Error
tfa98xx_dsp_support_framework_v6(struct tfa_device *tfa, int *pbSupportFramework)
{
    int featureBits[2] = { 0, 0 };
    enum Tfa98xx_Error error = Tfa98xx_Error_Ok;

    _ASSERT(pbSupportFramework != 0);

    if(tfa->in_use == 0)
        return Tfa98xx_Error_NotOpen;

    if (tfa->supportFramework != supportNotSet) {
	    if(tfa->supportFramework == supportNo)
		    *pbSupportFramework = 0;
	    else
		    *pbSupportFramework = 1;
    } else {
        error = tfa98xx_dsp_get_sw_feature_bits_v6(tfa, featureBits);
        if (error == Tfa98xx_Error_Ok) {
            *pbSupportFramework = 1;
            tfa->supportFramework = supportYes;
        } else {
            *pbSupportFramework = 0;
            tfa->supportFramework = supportNo;
            error = Tfa98xx_Error_Ok;
        }
    }

    /* *pbSupportFramework only changed when error == Tfa98xx_Error_Ok */
    return error;
}

enum Tfa98xx_Error
tfa98xx_dsp_write_speaker_parameters_v6(struct tfa_device *tfa,
		int length, const unsigned char *p_speaker_bytes)
{
	enum Tfa98xx_Error error;
	int bSupportDrc;

	if (p_speaker_bytes != NULL) {
		/* by design: keep the data opaque and no
		 * interpreting/calculation */
		/* Use long WaitResult retry count */
		error = tfa_dsp_cmd_id_write_v6(
					tfa,
					MODULE_SPEAKERBOOST,
					SB_PARAM_SET_LSMODEL, length,
					p_speaker_bytes);
	} else {
		error = Tfa98xx_Error_Bad_Parameter;
	}

        if (error != Tfa98xx_Error_Ok)
            return error;

        error = tfa98xx_dsp_support_drc_v6(tfa, &bSupportDrc);
        if (error != Tfa98xx_Error_Ok)
            return error;

        if (bSupportDrc) {
            /* Need to set AgcGainInsert back to PRE,
             * as the SetConfig forces it to POST */
        	uint8_t bytes[3] = {0, 0, 0};

        	error = tfa_dsp_cmd_id_write_v6(tfa,
        				     MODULE_SPEAKERBOOST,
        				     SB_PARAM_SET_AGCINS,
        				     3,
        				     bytes);
        }

	return error;
}

enum Tfa98xx_Error
tfa98xx_dsp_write_config_v6(struct tfa_device *tfa, int length,
               const unsigned char *p_config_bytes)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	int bSupportDrc;

	error = tfa_dsp_cmd_id_write_v6(tfa,
				      MODULE_SPEAKERBOOST,
				      SB_PARAM_SET_CONFIG, length,
				      p_config_bytes);
	if (error != Tfa98xx_Error_Ok)
            return error;

        error = tfa98xx_dsp_support_drc_v6(tfa, &bSupportDrc);
        if (error != Tfa98xx_Error_Ok)
            return error;

        if (bSupportDrc) {
            /* Need to set AgcGainInsert back to PRE,
             * as the SetConfig forces it to POST */
        	uint8_t bytes[3] = {0, 0, 0};

        	error = tfa_dsp_cmd_id_write_v6(tfa,
        				     MODULE_SPEAKERBOOST,
        				     SB_PARAM_SET_AGCINS,
        				     3,
        				     bytes);
        }

	return error;
}

/* load all the parameters for the DRC settings from a file */
enum Tfa98xx_Error tfa98xx_dsp_write_drc_v6(struct tfa_device *tfa,
                    int length, const unsigned char *p_drc_bytes)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	if (p_drc_bytes != NULL) {
		error = tfa_dsp_cmd_id_write_v6(tfa,
		                             MODULE_SPEAKERBOOST,
		                             SB_PARAM_SET_DRC, length,
		                             p_drc_bytes);

	} else {
		error = Tfa98xx_Error_Bad_Parameter;
	}
	return error;
}

enum Tfa98xx_Error tfa98xx_powerdown_v6(struct tfa_device *tfa, int powerdown)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;

	if (tfa->in_use == 0)
		return Tfa98xx_Error_NotOpen;

	error = TFA_SET_BF(tfa, PWDN, (uint16_t)powerdown);

	if (powerdown) {
		/* Workaround for ticket PLMA5337 */
		if (tfa->tfa_family == 2) {
			TFA_SET_BF_VOLATILE(tfa, AMPE, 0);
		}
	}

	return error;
}

enum Tfa98xx_Error
tfa98xx_select_mode_v6(struct tfa_device *tfa, enum Tfa98xx_Mode mode)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;

	if(tfa->in_use == 0)
		return Tfa98xx_Error_NotOpen;

	if (error == Tfa98xx_Error_Ok) {
		switch (mode) {

		default:
			error = Tfa98xx_Error_Bad_Parameter;
		}
	}

	return error;
}

int tfa_set_bf_v6(struct tfa_device *tfa, const uint16_t bf, const uint16_t value)
{
	enum Tfa98xx_Error err;
	uint16_t regvalue, msk, oldvalue;

	/*
	 * bitfield enum:
	 * - 0..3  : len
	 * - 4..7  : pos
	 * - 8..15 : address
	 */
	uint8_t len = bf & 0x0f;
	uint8_t pos = (bf >> 4) & 0x0f;
	uint8_t address = (bf >> 8) & 0xff;

	err = tfa_reg_read(tfa, address, &regvalue);
	if (err) {
              pr_err("Error getting bf :%d \n", -err);
              return -err;
        }

	oldvalue = regvalue;
	msk = ((1<<(len+1))-1)<<pos;
	regvalue &= ~msk;
	regvalue |= value<<pos;

	/* Only write when the current register value is not the same as the new value */
	if(oldvalue != regvalue) {
		err = tfa_reg_write(tfa, address, regvalue);
		if (err) {
			pr_err("Error setting bf :%d \n", -err);
			return -err;
		}
	}

	return 0;
}

int tfa_set_bf_v6_volatile(struct tfa_device *tfa, const uint16_t bf, const uint16_t value)
{
	enum Tfa98xx_Error err;
	uint16_t regvalue, msk;

	/*
	 * bitfield enum:
	 * - 0..3  : len
	 * - 4..7  : pos
	 * - 8..15 : address
	 */
	uint8_t len = bf & 0x0f;
	uint8_t pos = (bf >> 4) & 0x0f;
	uint8_t address = (bf >> 8) & 0xff;

	err = tfa_reg_read(tfa, address, &regvalue);
	if (err) {
              pr_err("Error getting bf :%d \n", -err);
              return -err;
        }

	msk = ((1<<(len+1))-1)<<pos;
	regvalue &= ~msk;
	regvalue |= value<<pos;

    err = tfa_reg_write(tfa, address, regvalue);
	if (err) {
                pr_err("Error setting bf :%d \n", -err);
                return -err;
        }

	return 0;
}

int tfa_get_bf_v6(struct tfa_device *tfa, const uint16_t bf)
{
	enum Tfa98xx_Error err;
	uint16_t regvalue, msk;
	uint16_t value;

	/*
	 * bitfield enum:
	 * - 0..3  : len
	 * - 4..7  : pos
	 * - 8..15 : address
	 */
	uint8_t len = bf & 0x0f;
	uint8_t pos = (bf >> 4) & 0x0f;
	uint8_t address = (bf >> 8) & 0xff;

	err = tfa_reg_read(tfa, address, &regvalue);
        if (err) {
              pr_err("Error getting bf :%d \n", -err);
              return -err;
        }

	msk = ((1<<(len+1))-1)<<pos;
	regvalue &= msk;
	value = regvalue>>pos;

	return value;
}

int tfa_set_bf_v6_value(const uint16_t bf, const uint16_t bf_value, uint16_t *p_reg_value)
{
	uint16_t regvalue, msk;

	/*
	 * bitfield enum:
	 * - 0..3  : len
	 * - 4..7  : pos
	 * - 8..15 : address
	 */
	uint8_t len = bf & 0x0f;
	uint8_t pos = (bf >> 4) & 0x0f;

	regvalue = *p_reg_value;

	msk = ((1<<(len+1))-1)<<pos;
	regvalue &= ~msk;
	regvalue |= bf_value<<pos;

	*p_reg_value = regvalue;

	return 0;
}

uint16_t tfa_get_bf_v6_value_v6(const uint16_t bf, const uint16_t reg_value)
{
	uint16_t msk, value;

	/*
	 * bitfield enum:
	 * - 0..3  : len
	 * - 4..7  : pos
	 * - 8..15 : address
	 */
	uint8_t len = bf & 0x0f;
	uint8_t pos = (bf >> 4) & 0x0f;

	msk = ((1<<(len+1))-1)<<pos;
	value = (reg_value & msk) >> pos;

	return value;
}


int tfa_write_reg_v6(struct tfa_device *tfa, const uint16_t bf, const uint16_t reg_value)
{
	enum Tfa98xx_Error err;

	/* bitfield enum - 8..15 : address */
	uint8_t address = (bf >> 8) & 0xff;

	err = tfa_reg_write(tfa, address, reg_value);
	if (err)
		return -err;

	return 0;
}

int tfa_read_reg_v6(struct tfa_device *tfa, const uint16_t bf)
{
	enum Tfa98xx_Error err;
	uint16_t regvalue;

	/* bitfield enum - 8..15 : address */
	uint8_t address = (bf >> 8) & 0xff;

	err = tfa_reg_read(tfa, address, &regvalue);
	if (err)
		return -err;

	return regvalue;
}

/*
 * powerup the coolflux subsystem and wait for it
 */
enum Tfa98xx_Error tfa_cf_powerup_v6(struct tfa_device *tfa)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	int tries, status;

	/* power on the sub system */
	TFA_SET_BF_VOLATILE(tfa, PWDN, 0);

	// wait until everything is stable, in case clock has been off
	if (tfa->verbose)
		pr_info("Waiting for DSP system stable...\n");
	for ( tries=CFSTABLE_TRIES; tries > 0; tries-- ) {
		err = tfa98xx_dsp_system_stable_v6(tfa, &status);
		_ASSERT(err == Tfa98xx_Error_Ok);
		if ( status )
			break;
		else
			msleep_interruptible(10); /* wait 10ms to avoid busload */
	}
	if (tries==0) {// timedout
		pr_err("DSP subsystem start timed out\n");
		return Tfa98xx_Error_StateTimedOut;
	}

	return err;
}

/*
 * Enable/Disable the I2S output for TFA1 devices
 * without TDM interface
 */
static enum Tfa98xx_Error tfa98xx_aec_output(struct tfa_device *tfa, int enable)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;

	if ((tfa->daimap & Tfa98xx_DAI_TDM) == Tfa98xx_DAI_TDM)
		return err;

	if (tfa->tfa_family == 1)
		err = -tfa_set_bf_v6(tfa, TFA1_BF_I2SDOE, (enable!=0));
	else {
		pr_err("I2SDOE on unsupported family\n");
		err = Tfa98xx_Error_Not_Supported;
	}

	return err;
}

/*
 * Print the current state of the hardware manager
 * Device manager status information, man_state from TFA9888_N1B_I2C_regmap_V12
 */
int is_94_N2_device(struct tfa_device *tfa)
{
	return ((((tfa->rev) & 0xff) == 0x94) && (((tfa->rev >> 8) & 0xff) > 0x1a));
}

enum Tfa98xx_Error show_current_state_v6(struct tfa_device *tfa)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
        int manstate = -1;

	if (tfa->tfa_family == 2 && tfa->verbose) {
		if (is_94_N2_device(tfa))
			manstate = tfa_get_bf_v6(tfa, TFA9894N2_BF_MANSTATE);
		else
			manstate = TFA_GET_BF(tfa, MANSTATE);
		if (manstate < 0)
        		return -manstate;

		pr_debug("Current HW manager state: ");

		switch(manstate) {
			case 0: pr_debug("power_down_state \n");
				break;
			case 1: pr_debug("wait_for_source_settings_state \n");
				break;
			case 2: pr_debug("connnect_pll_input_state \n");
				break;
			case 3: pr_debug("disconnect_pll_input_state \n");
				break;
			case 4: pr_debug("enable_pll_state \n");
				break;
			case 5: pr_debug("enable_cgu_state \n");
				break;
			case 6: pr_debug("init_cf_state \n");
				break;
			case 7: pr_debug("enable_amplifier_state \n");
				break;
			case 8: pr_debug("alarm_state \n");
				break;
			case 9: pr_debug("operating_state \n");
				break;
			case 10: pr_debug("mute_audio_state \n");
				break;
			case 11: pr_debug("disable_cgu_pll_state \n");
				break;
			default:
				pr_debug("Unable to find current state \n");
				break;
		}
	}

	return err;
}

enum Tfa98xx_Error tfaGetFwApiVersion(struct tfa_device *tfa, unsigned char *pFirmwareVersion)
{
	enum Tfa98xx_Error err = 0;
	char cmd_buf[4];
	int cmd_len, res_len;

	if (tfa == NULL)
		return Tfa98xx_Error_Bad_Parameter;
	if (!tfa->is_probus_device)
	{
		err = mem_read(tfa, FW_VAR_API_VERSION, 1, (int *)pFirmwareVersion);
		if (err) {
			pr_debug("%s Error: Unable to get API Version from DSP \n", __FUNCTION__);
			return err;
		}
	}
	else
	{
		cmd_len = 0x03;

		/* GetAPI: Command is 0x00 0x80 0xFE */
		cmd_buf[0] = 0x00;
		cmd_buf[1] = 0x80;
		cmd_buf[2] = 0xFE;

		/* Write the command.*/

		err = tfa98xx_write_dsp(tfa, cmd_len, (const char *)cmd_buf);

		/* Read the API Value.*/
		if (err == 0)
		{
			res_len = 3;
			err = tfa98xx_read_dsp(tfa, res_len, (unsigned char *)pFirmwareVersion);

		}
	}
	return err;

}


/*
 *  start the speakerboost algorithm
 *  this implies a full system startup when the system was not already started
 *
 */
enum Tfa98xx_Error tfaRunSpeakerBoost_v6(struct tfa_device *tfa, int force, int profile)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	int value;
	#ifdef OPLUS_ARCH_EXTENDS
	int calibrate_done = 0;
	#endif /* OPLUS_ARCH_EXTENDS */

	if (force) {
		err= tfaRunColdStartup_v6(tfa, profile);
		if ( err ) return err;
	}

	/* Returns 1 when device is "cold" and 0 when device is warm */
	value = tfa_is_cold(tfa);

	pr_debug("Startup of device [%s] is a %sstart\n", tfaContDevice_v6Name_v6(tfa->cnt, tfa->dev_idx), value ? "cold" : "warm");

	/* cold start and not tap profile */
	if (value) {
		/* Run startup and write all files */
		err = tfaRunSpeakerStartup_v6(tfa, force, profile);
		if ( err ) return err;

		/* Save the current profile and set the vstep to 0 */
		/* This needs to be overwriten even in CF bypass */
		tfa_dev_set_swprof(tfa, (unsigned short)profile);
		tfa_dev_set_swvstep(tfa, 0);

		/* Synchonize I/V delay on 96/97 at cold start */
		if ((tfa->tfa_family == 1) && (tfa->daimap == Tfa98xx_DAI_TDM))
			tfa->sync_iv_delay = 1;

		#ifdef OPLUS_ARCH_EXTENDS
		tfa_dev_set_state(tfa, TFA_STATE_OPERATING);
		if (!tfa->is_probus_device) {
			tfaRunSpeakerCalibration_result_v6(tfa, &calibrate_done);
		}
		#endif /* OPLUS_ARCH_EXTENDS */
	}

	return err;
}

enum Tfa98xx_Error tfaRunSpeakerStartup_v6(struct tfa_device *tfa, int force, int profile)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;

	if ( !force ) { // in case of force CF already runnning
		err = tfaRunStartup_v6(tfa, profile);
		PRINT_ASSERT(err);
		if ( err )
			return err;

		/* Startup with CF in bypass then return here */
		if (tfa_cf_enabled(tfa) == 0)
			return err;

		/* respond to external DSP: -1:none, 0:no_dsp, 1:cold, 2:warm */
		if(tfa->ext_dsp == -1) {
			err = tfaRunStartDSP_v6(tfa);
			if ( err )
				return err;
		}
	}

	/* Set auto_copy_mtp_to_iic (bit 5 of A3) to 1 */
	tfa98xx_auto_copy_mtp_to_iic(tfa);

	#ifndef OPLUS_ARCH_EXTENDS
	err = tfaGetFwApiVersion(tfa, (unsigned char *)&tfa->fw_itf_ver[0]);
	if (err) {
		pr_err("[%s] cannot get FWAPI error = %d \n", __FUNCTION__, err);
	}
	#endif /* OPLUS_ARCH_EXTENDS */
	/* write all the files from the device list */
	err = tfaContWriteFile_v6s(tfa);
	if (err) {
		pr_debug("[%s] tfaContWriteFile_v6s error = %d \n", __FUNCTION__, err);
		return err;
	}

	/* write all the files from the profile list (use volumstep 0) */
	err = tfaContWriteFile_v6sProf_v6(tfa, profile, 0);
	if (err) {
		pr_debug("[%s] tfaContWriteFile_v6sProf_v6 error = %d \n", __FUNCTION__, err);
		return err;
	}

	return err;
}

/*
 * Run calibration
 */
enum Tfa98xx_Error tfaRunSpeakerCalibration_v6(struct tfa_device *tfa)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	int calibrateDone;

	/* return if there is no audio running */
	if ((tfa->tfa_family == 2) && TFA_GET_BF(tfa, NOCLK))
		return Tfa98xx_Error_NoClock;

	/* When MTPOTC is set (cal=once) unlock key2 */
	if (TFA_GET_BF(tfa, MTPOTC) == 1) {
		tfa98xx_key2_v6(tfa, 0);
	}

	/* await calibration, this should return ok */
	err = tfaRunWaitCalibration_v6(tfa, &calibrateDone);
	if (err == Tfa98xx_Error_Ok) {
		err = tfa_dsp_get_calibration_impedance_v6(tfa);
		PRINT_ASSERT(err);
	}

	/* When MTPOTC is set (cal=once) re-lock key2 */
	if (TFA_GET_BF(tfa, MTPOTC) == 1) {
		tfa98xx_key2_v6(tfa, 1);
	}

	return err;
}

#ifdef OPLUS_ARCH_EXTENDS
enum Tfa98xx_Error tfaRunSpeakerCalibration_result_v6(struct tfa_device *tfa, int *result)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	int calibrateDone;
	int spkr_count = 0;

	/* return if there is no audio running */
	if ((tfa->tfa_family == 2) && TFA_GET_BF(tfa, NOCLK))
		return Tfa98xx_Error_NoClock;

	/* When MTPOTC is set (cal=once) unlock key2 */
	if (TFA_GET_BF(tfa, MTPOTC) == 1) {
		tfa98xx_key2_v6(tfa, 0);
	}

	/* await calibration, this should return ok */
	err = tfaRunWaitCalibration_v6(tfa, &calibrateDone);
	if (err == Tfa98xx_Error_Ok) {
		err = tfa_dsp_get_calibration_impedance_v6(tfa);
		PRINT_ASSERT(err);
	}

	if(err == Tfa98xx_Error_Ok) {
		err = tfa_supported_speakers(tfa, &spkr_count);

		if (spkr_count == 1) {
			pr_info("%s:%d mOhms\n", __func__, tfa->mohm[0]);
		} else {
			pr_info("%s:Prim:%d mOhms, Sec:%d mOhms\n", __func__, tfa->mohm[0], tfa->mohm[1]);
		}

		pr_info("%s: valid mohms[%d, %d]\n", __func__, tfa->min_mohms, tfa->max_mohms);
		if ((tfa->mohm[0] < tfa->min_mohms) || (tfa->mohm[0] > tfa->max_mohms)) {
			pr_info("speaker_resistance_fail\n");
			if (ftm_mode == BOOT_MODE_FACTORY) {
				strcpy(ftm_spk_resistance, "speaker_resistance_fail");
			}
			g_speaker_resistance_fail = true;

			/* When MTPOTC is set (cal=once) re-lock key2 */
			if (TFA_GET_BF(tfa, MTPOTC) == 1) {
				tfa98xx_key2_v6(tfa, 1);
			}

			*result = calibrateDone;
			err = Tfa98xx_Error_Fail;
			return err;
		}
		g_speaker_resistance_fail = false;
	}

	/* When MTPOTC is set (cal=once) re-lock key2 */
	if (TFA_GET_BF(tfa, MTPOTC) == 1) {
		tfa98xx_key2_v6(tfa, 1);
	}

	*result = calibrateDone;

	return err;
}
#endif /* OPLUS_ARCH_EXTENDS */

enum Tfa98xx_Error tfaRunColdboot_v6(struct tfa_device *tfa, int state)
{
#define CF_CONTROL 0x8100
	enum Tfa98xx_Error err=Tfa98xx_Error_Ok;
	int tries = 10;

	/* repeat set ACS bit until set as requested */
	while ( state != TFA_GET_BF(tfa, ACS)) {
		/* set colstarted in CF_CONTROL to force ACS */
                err = mem_write(tfa, CF_CONTROL, state, Tfa98xx_DMEM_IOMEM);
		PRINT_ASSERT(err);

		if (tries-- == 0) {
			pr_debug("coldboot (ACS) did not %s\n", state ? "set":"clear");
			return Tfa98xx_Error_Other;
		}
	}

	return err;
}



/*
 * load the patch if any
 *   else tell no loaded
 */
static enum Tfa98xx_Error tfa_run_load_patch(struct tfa_device *tfa)
{
	return tfaContWritePatch_v6(tfa);
}

/*
 *  this will load the patch witch will implicitly start the DSP
 *   if no patch is available the DPS is started immediately
 */
enum Tfa98xx_Error tfaRunStartDSP_v6(struct tfa_device *tfa)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;

	err = tfa_run_load_patch(tfa);
	if (err) { /* patch load is fatal so return immediately*/
		return err;
	}

	/* Clear count_boot, should be reset to 0 before the DSP reset is released */
	err = mem_write(tfa, 512, 0, Tfa98xx_DMEM_XMEM);
	PRINT_ASSERT(err);

	/* Reset DSP once for sure after initializing */
	if ( err == Tfa98xx_Error_Ok) {
		err = tfa98xx_dsp_reset_v6(tfa, 0);
		PRINT_ASSERT(err);
	}

	/* Sample rate is needed to set the correct tables */
	err = tfa98xx_dsp_write_tables_v6(tfa, TFA_GET_BF(tfa, AUDFS));
	PRINT_ASSERT(err);

	return err;
}

/*
 * start the clocks and wait until the AMP is switching
 *  on return the DSP sub system will be ready for loading
 */
enum Tfa98xx_Error tfaRunStartup_v6(struct tfa_device *tfa, int profile)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	nxpTfaDeviceList_t *dev = tfaContDevice_v6(tfa->cnt, tfa->dev_idx);
	int i, noinit=0, audfs=0, fractdel=0;

	if(dev == NULL)
		return Tfa98xx_Error_Fail;

	if(dev->bus) /* no i2c device, do nothing */
		return Tfa98xx_Error_Ok;

	/* process the device list to see if the user implemented the noinit */
	for(i=0;i<dev->length;i++) {
		if (dev->list[i].type == dscNoInit) {
			noinit=1;
			break;
		}
	}

	if(!noinit) {
		/* Read AUDFS & FRACTDEL prior to (re)init. */
		audfs = TFA_GET_BF(tfa, AUDFS);
		fractdel = TFA_GET_BF(tfa, FRACTDEL);
		/* load the optimal TFA98XX in HW settings */
		err = tfa98xx_init_v6(tfa);
		PRINT_ASSERT(err);

		/* Restore audfs & fractdel after coldboot, so we can calibrate with correct fs setting.
		 * in case something else was given in cnt file, profile below will apply this. */
		TFA_SET_BF(tfa, AUDFS, audfs);
		TFA_SET_BF(tfa, FRACTDEL, fractdel);
	} else {
		pr_debug("\nWarning: No init keyword found in the cnt file. Init is skipped! \n");
	}

	/* I2S settings to define the audio input properties
	 *  these must be set before the subsys is up */
	// this will run the list until a non-register item is encountered
	err = tfaContWriteRegsDev_v6_v6(tfa); // write device register settings
	PRINT_ASSERT(err);
	// also write register the settings from the default profile
	// NOTE we may still have ACS=1 so we can switch sample rate here
	err = tfaContWriteRegsProf_v6(tfa, profile);
	PRINT_ASSERT(err);

	/* Factory trimming for the Boost converter */
	tfa98xx_factory_trimmer(tfa);

	/* Go to the initCF state */
	tfa_dev_set_state(tfa, TFA_STATE_INIT_CF);

	err = show_current_state_v6(tfa);

	return err;
}

/*
 * run the startup/init sequence and set ACS bit
 */
enum Tfa98xx_Error tfaRunColdStartup_v6(struct tfa_device *tfa, int profile)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;

	err = tfaRunStartup_v6(tfa, profile);
	PRINT_ASSERT(err);
	if (err)
		return err;

	if(!tfa->is_probus_device) {
		/* force cold boot */
		err = tfaRunColdboot_v6(tfa, 1); // set ACS
		PRINT_ASSERT(err);
		if (err)
			return err;
	}

	/* start */
	err = tfaRunStartDSP_v6(tfa);
	PRINT_ASSERT(err);

	return err;
}

/*
 *
 */
enum Tfa98xx_Error tfaRunMute_v6(struct tfa_device *tfa)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	int status;
	int tries = 0;

	/* signal the TFA98XX to mute  */
	if (tfa->tfa_family == 1) {
		err = tfa98xx_set_mute_v6(tfa, Tfa98xx_Mute_Amplifier);

		if(err == Tfa98xx_Error_Ok) {
			/* now wait for the amplifier to turn off */
			do {
				status = TFA_GET_BF(tfa, SWS);
				if (status != 0)
					msleep_interruptible(10); /* wait 10ms to avoid busload */
				else
					break;
				tries++;
			}  while (tries < AMPOFFWAIT_TRIES);


			if (tfa->verbose)
				pr_debug("-------------------- muted --------------------\n");

			/*The amplifier is always switching*/
			if (tries == AMPOFFWAIT_TRIES)
				return Tfa98xx_Error_Other;
		}
	}

	return err;
}
/*
 *
 */
enum Tfa98xx_Error tfaRunUnmute_v6(struct tfa_device *tfa)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;

	/* signal the TFA98XX to mute  */
	err = tfa98xx_set_mute_v6(tfa, Tfa98xx_Mute_Off);

	if (tfa->verbose)
	    pr_debug("-------------------unmuted ------------------\n");

	return err;
}

static void individual_calibration_results(struct tfa_device *tfa)
{
	int value_P, value_S;

	/* Read the calibration result in xmem (529=primary channel) (530=secondary channel) */
	mem_read(tfa, 529, 1, &value_P);
	mem_read(tfa, 530, 1, &value_S);

	if(value_P != 1 && value_S != 1)
		pr_debug("Calibration failed on both channels! \n");
	else if(value_P != 1) {
		pr_debug("Calibration failed on Primary (Left) channel! \n");
		TFA_SET_BF_VOLATILE(tfa, SSLEFTE, 0); /* Disable the sound for the left speaker */
	}
	else if(value_S != 1) {
		pr_debug("Calibration failed on Secondary (Right) channel! \n");
		TFA_SET_BF_VOLATILE(tfa, SSRIGHTE, 0); /* Disable the sound for the right speaker */
	}

	TFA_SET_BF_VOLATILE(tfa, AMPINSEL, 0); /* Set amplifier input to TDM */
	TFA_SET_BF_VOLATILE(tfa, SBSL, 1);
}

/*
 * wait for calibrateDone
 */
enum Tfa98xx_Error tfaRunWaitCalibration_v6(struct tfa_device *tfa, int *calibrateDone)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	int tries = 0, mtp_busy = 1, tries_mtp_busy = 0;

	*calibrateDone = 0;

	/* in case of calibrate once wait for MTPEX */
	if (TFA_GET_BF(tfa, MTPOTC)) {
		// Check if MTP_busy is clear!
		while (tries_mtp_busy < MTPBWAIT_TRIES)
		{
			mtp_busy = tfa_dev_get_mtpb(tfa);
			if (mtp_busy == 1)
				msleep_interruptible(10); /* wait 10ms to avoid busload */
			else
				break;
			tries_mtp_busy++;
		}

		if (tries_mtp_busy < MTPBWAIT_TRIES) {
			/* Because of the msleep TFA98XX_API_WAITRESULT_NTRIES is way to long!
				* Setting this to 25 will take it atleast 25*50ms = 1.25 sec
				*/
			while ((*calibrateDone == 0) && (tries < MTPEX_WAIT_NTRIES)) {
				*calibrateDone = TFA_GET_BF(tfa, MTPEX);
				if (*calibrateDone == 1)
					break;
				msleep_interruptible(50); /* wait 50ms to avoid busload */
				tries++;
			}

			if (tries >= MTPEX_WAIT_NTRIES) {
				tries = TFA98XX_API_WAITRESULT_NTRIES;
			}
		} else {
			pr_err("MTP bussy after %d tries\n", MTPBWAIT_TRIES);
		}
	}

	/* poll xmem for calibrate always
		* calibrateDone = 0 means "calibrating",
		* calibrateDone = -1 (or 0xFFFFFF) means "fails"
		* calibrateDone = 1 means calibration done
		*/
	while ((*calibrateDone != 1) && (tries<TFA98XX_API_WAITRESULT_NTRIES)) {
		err = mem_read(tfa, TFA_FW_XMEM_CALIBRATION_DONE, 1, calibrateDone);
		if (*calibrateDone == -1)
			break;
		tries++;
	}

	if (*calibrateDone != 1) {
		#ifdef OPLUS_ARCH_EXTENDS
		if (ftm_mode == BOOT_MODE_FACTORY) {
			strcpy(ftm_SpeakerCalibration, "calibration_fail");
		}
		#endif /* OPLUS_ARCH_EXTENDS */
		pr_err("Calibration failed! \n");
		err = Tfa98xx_Error_Bad_Parameter;
	} else if (tries==TFA98XX_API_WAITRESULT_NTRIES) {
		pr_debug("Calibration has timedout! \n");
		err = Tfa98xx_Error_StateTimedOut;
	} else if(tries_mtp_busy == 1000) {
		pr_err("Calibrate Failed: MTP_busy stays high! \n");
		err = Tfa98xx_Error_StateTimedOut;
	}

	/* Give reason why calibration failed! */
	if (err != Tfa98xx_Error_Ok) {
		if ((tfa->tfa_family == 2) && (TFA_GET_BF(tfa, REFCKSEL) == 1)) {
			pr_err("Unable to calibrate the device with the internal clock! \n");
		}
	}

	/* Check which speaker calibration failed. Only for 88C */
	if ((err != Tfa98xx_Error_Ok) && ((tfa->rev & 0x0FFF) == 0xc88)) {
		individual_calibration_results(tfa);
	}

	return err;
}

/*
 * tfa_dev_start will only do the basics: Going from powerdown to operating or a profile switch.
 * for calibrating or akoustic shock handling use the tfa98xxCalibration function.
 */
enum tfa_error tfa_dev_start(struct tfa_device *tfa, int next_profile, int vstep)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	int active_profile = -1;

	pr_info("tfa_dev_start enter\n");

	/* Get currentprofile */
	active_profile = tfa_dev_get_swprof(tfa);
	if (active_profile == 0xff)
		active_profile = -1;

	/* TfaRun_SpeakerBoost implies un-mute */
	pr_debug("Active_profile:%s, next_profile:%s\n",
			tfaContProfileName_v6(tfa->cnt, tfa->dev_idx, active_profile),
			tfaContProfileName_v6(tfa->cnt, tfa->dev_idx, next_profile));

	err = show_current_state_v6(tfa);

	if ( tfa->tfa_family == 1 ) { /* TODO move this to ini file */
		/* Enable I2S output on TFA1 devices without TDM */
		err = tfa98xx_aec_output(tfa, 1);
		if ( err != Tfa98xx_Error_Ok)
			goto error_exit;
	}

	if ( tfa->bus != 0 )  { /* non i2c  */
#ifndef __KERNEL__
		tfadsp_fw_start(tfa, next_profile, vstep);
#endif /* __KERNEL__ */
	} else {
		/* Check if we need coldstart or ACS is set */
		err = tfaRunSpeakerBoost_v6(tfa, 0, next_profile);
		if ( err != Tfa98xx_Error_Ok)
			goto error_exit;

		/* Make sure internal oscillator is running for DSP devices (non-dsp and max1 this is no-op) */
		tfa98xx_set_osc_powerdown(tfa, 0);

		/* Go to the Operating state */
		#ifndef OPLUS_ARCH_EXTENDS
		tfa_dev_set_state(tfa, TFA_STATE_OPERATING | TFA_STATE_MUTE);
		#else /* OPLUS_ARCH_EXTENDS */
		tfa_dev_set_state(tfa, TFA_STATE_OPERATING);
		#endif /*OPLUS_ARCH_EXTENDS*/
		}
		active_profile = tfa_dev_get_swprof(tfa);

		/* Profile switching */
		if ((next_profile != active_profile && active_profile >= 0)) {
			err = tfaContWriteProfile_v6(tfa, next_profile, vstep);
			if (err!=Tfa98xx_Error_Ok)
				goto error_exit;
		}

		/* If the profile contains the .standby suffix go to powerdown
		 * else we should be in operating state
		 */
		if(strstr(tfaContProfileName_v6(tfa->cnt, tfa->dev_idx, next_profile), ".standby") != NULL) {
			tfa_dev_set_swprof(tfa, (unsigned short)next_profile);
			tfa_dev_set_swvstep(tfa, (unsigned short)tfa->vstep);
			goto error_exit;
		}

		err = show_current_state_v6(tfa);

	    tfa->vstep = tfa_dev_get_swvstep(tfa);
		if ((TFA_GET_BF(tfa, CFE) != 0) && (vstep != tfa->vstep) && (vstep != -1)) {
			err = tfaContWriteFilesVstep_v6(tfa, next_profile, vstep);
			if ( err != Tfa98xx_Error_Ok)
				goto error_exit;
		}

		/* Always search and apply filters after a startup */
		err = tfa_set_filters_v6(tfa, next_profile);
		if (err!=Tfa98xx_Error_Ok)
			goto error_exit;

		tfa_dev_set_swprof(tfa, (unsigned short)next_profile);
		tfa_dev_set_swvstep(tfa, (unsigned short)tfa->vstep);

		/* PLMA5539: Gives information about current setting of powerswitch */
		if (tfa->verbose) {
			if (!tfa98xx_powerswitch_is_enabled(tfa))
				pr_info("Device start without powerswitch enabled!\n");
		}

error_exit:
	show_current_state_v6(tfa);

	return (enum tfa_error)err;
}

enum tfa_error tfa_dev_stop(struct tfa_device *tfa)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	#ifdef OPLUS_ARCH_EXTENDS
	int total_wait_count = 20;
	int times = 0, ready = 0;
	#endif /*OPLUS_ARCH_EXTENDS*/

	/* mute */
	tfaRunMute_v6(tfa);

	/* Make sure internal oscillator is not running for DSP devices (non-dsp and max1 this is no-op) */
	tfa98xx_set_osc_powerdown(tfa, 1);

	/* powerdown CF */
	err = tfa98xx_powerdown_v6(tfa, 1 );
	if ( err != Tfa98xx_Error_Ok)
		return (enum tfa_error)err;

	/* disable I2S output on TFA1 devices without TDM */
	err = tfa98xx_aec_output(tfa, 0);

	#ifdef OPLUS_ARCH_EXTENDS
	/* we should ensure state machine in powedown here. */
	while ((TFA_GET_BF(tfa, MANSTATE) != 0) && (times++ < total_wait_count)) {
		pr_info("tfa stop wait state machine goto powerdown mode.\n");
		err = tfa98xx_dsp_system_stable_v6(tfa, &ready);
		if ((err != Tfa98xx_Error_Ok) || !ready) {
			pr_warn("tfa stop: No I2S CLK\n");
			break;
		}
		msleep_interruptible(5);
	}

	if (times < total_wait_count) {
		pr_info("tfa stop: already in PowerDown\n");
	} else {
		pr_info("tfa stop: Not in PowerDown\n");
	}
	#endif /*OPLUS_ARCH_EXTENDS*/

	return (enum tfa_error)err;
}

/*
 *  int registers and coldboot dsp
 */
int tfa_reset_v6(struct tfa_device *tfa)
{
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	int state = -1;
	int retry_cnt = 0;

	/* Check device state. Print warning if reset is done from other state than powerdown (when verbose) */
	state = tfa_dev_get_state(tfa);
	if (tfa->verbose) {
		if (((tfa->tfa_family == 1) && state != TFA_STATE_RESET) ||
			((tfa->tfa_family == 2) && state != TFA_STATE_POWERDOWN)) {
				pr_info("WARNING: Device reset should be performed in POWERDOWN state\n");
		}
	}

	/* Split TFA1 behavior from TFA2*/
	if (tfa->tfa_family == 1) {
		err = TFA_SET_BF(tfa, I2CR, 1);
		if(err)
			return err;
		err = tfa98xx_powerdown_v6(tfa, 0);
		if (err)
			return err;
		err = tfa_cf_powerup_v6(tfa);
		if (err)
			return err;
		err = tfaRunColdboot_v6(tfa, 1);
		if (err)
			return err;
		err = TFA_SET_BF(tfa, I2CR, 1);
	} else {
		/* Probus devices needs extra protection to ensure proper reset
		   behavior, this step is valid only in state other than powerdown */
		if (tfa->is_probus_device && state != TFA_STATE_POWERDOWN) {
			err = TFA_SET_BF_VOLATILE(tfa, AMPE, 0);
			if (err)
				return err;
			err = tfa98xx_powerdown_v6(tfa, 1);
			if (err)
				return err;
		}

		err = TFA_SET_BF_VOLATILE(tfa, I2CR, 1);
		if (err)
			return err;

		/* Restore MANSCONF to POR state */
		err = TFA_SET_BF_VOLATILE(tfa, MANSCONF, 0);
		if (err)
			return err;

		/* Probus devices HW are already reseted here,
		   Last step is to send init message to softDSP */
		if (tfa->is_probus_device) {
			if (tfa->ext_dsp > 0) {
				err = tfa98xx_init_dsp(tfa);
				/* ext_dsp status from warm to cold after reset */
				if(tfa->ext_dsp==2) {
					tfa->ext_dsp=1;
				}
			}
		} else {
			/* Restore MANCOLD to POR state */
			TFA_SET_BF_VOLATILE(tfa, MANCOLD, 1);

			/* Coolflux has to be powered on to ensure proper ACS
			   bit state */

			/* Powerup CF to access CF io */
			err = tfa98xx_powerdown_v6(tfa, 0);
			if (err)
				return err;

			/* For clock */
			err = tfa_cf_powerup_v6(tfa);
			if (err)
				return err;

			/* Force cold boot */
			err = tfaRunColdboot_v6(tfa, 1); /* Set ACS */
			if (err)
				return err;

			/* Set PWDN = 1, this will transfer device into powerdown state */
			err = TFA_SET_BF_VOLATILE(tfa, PWDN, 1);
			if (err)
				return err;

			/* 88 needs SBSL on top of PWDN bit to start transition,
			   for 92 and 94 this doesn't matter */
			err = TFA_SET_BF_VOLATILE(tfa, SBSL, 1);
			if (err)
				return err;

			/* Powerdown state should be reached within 1ms */
			for (retry_cnt = 0; retry_cnt < TFA98XX_WAITRESULT_NTRIES; retry_cnt++) {
				if (is_94_N2_device(tfa))
					state = tfa_get_bf_v6(tfa, TFA9894N2_BF_MANSTATE);
				else
					state = TFA_GET_BF(tfa, MANSTATE);
				if (state < 0) {
					return err;
				}

				/* Check for MANSTATE=Powerdown (0) */
				if (state == 0)
					break;
				msleep_interruptible(2);
			}

			/* Reset all I2C registers to default values,
			   now device state is consistent, same as after powerup */
			err = TFA_SET_BF(tfa, I2CR, 1);
		}
	}

	return err;
}

/*
 * Write all the bytes specified by num_bytes and data
 */
enum Tfa98xx_Error
tfa98xx_write_data_v6(struct tfa_device *tfa,
		  unsigned char subaddress, int num_bytes,
		  const unsigned char data[])
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	/* subaddress followed by data */
	const int bytes2write = num_bytes + 1;
	unsigned char *write_data;

	if (num_bytes > TFA2_MAX_PARAM_SIZE)
		return Tfa98xx_Error_Bad_Parameter;

	write_data = (unsigned char *)kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
	if (write_data == NULL)
		return Tfa98xx_Error_Fail;

	write_data[0] = subaddress;
	memcpy(&write_data[1], data, num_bytes);

	error = tfa98xx_write_raw_v6(tfa, bytes2write, write_data);

	kmem_cache_free(tfa->cachep, write_data);
	return error;
}

/*
 * fill the calibration value as milli ohms in the struct
 *
 *  assume that the device has been calibrated
 */
enum Tfa98xx_Error tfa_dsp_get_calibration_impedance_v6(struct tfa_device *tfa)
{
	enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
	unsigned char bytes[3*2] = {0};
	int nr_bytes, i, data[2], calibrateDone, spkr_count=0, cal_idx=0;
	unsigned int scaled_data;
	int tries=0;

	error = tfa_supported_speakers(tfa, &spkr_count);

	#ifndef OPLUS_ARCH_EXTENDS
	if (tfa_dev_mtp_get(tfa, TFA_MTP_OTC)) {
	#else /* OPLUS_ARCH_EXTENDS */
	if (tfa_dev_mtp_get(tfa, TFA_MTP_OTC) && !aging_flag) {
	#endif /* OPLUS_ARCH_EXTENDS */
		pr_debug("Getting calibration values from MTP\n");

		if((tfa->rev & 0xFF) == 0x88) {
			for(i=0; i<spkr_count; i++) {
				if(i==0)
					tfa->mohm[i] = tfa_dev_mtp_get(tfa, TFA_MTP_RE25_PRIM);
				else
					tfa->mohm[i] = tfa_dev_mtp_get(tfa, TFA_MTP_RE25_SEC);
			}
		} else {
			tfa->mohm[0] = tfa_dev_mtp_get(tfa, TFA_MTP_RE25);
		}
	} else {
		pr_debug("Getting calibration values from Speakerboost\n");

		/* Make sure the calibrateDone bit is set before getting the values from speakerboost!
		 * This does not work for 72 (because the dsp cannot set this bit)
		 */
		if (!tfa->is_probus_device) {
			/* poll xmem for calibrate always
				* calibrateDone = 0 means "calibrating",
				* calibrateDone = -1 (or 0xFFFFFF) means "fails"
				* calibrateDone = 1 means calibration done
				*/
			calibrateDone = 0;
			while ((calibrateDone != 1) && (tries < TFA98XX_API_WAITRESULT_NTRIES)) {
				error = mem_read(tfa, TFA_FW_XMEM_CALIBRATION_DONE, 1, &calibrateDone);
				if (calibrateDone == 1)
					break;
				tries++;
			}

			if (calibrateDone != 1) {
				pr_err("Calibration failed! \n");
				error = Tfa98xx_Error_Bad_Parameter;
			}
			else if (tries == TFA98XX_API_WAITRESULT_NTRIES) {
				pr_debug("Calibration has timedout! \n");
				error = Tfa98xx_Error_StateTimedOut;
			}
		}
		/* SoftDSP interface differs from hw-dsp interfaces */
		if(tfa->is_probus_device && tfa->cnt->ndev > 1) {
			spkr_count = tfa->cnt->ndev;
			#ifdef OPLUS_ARCH_EXTENDS
			/*Add for 4 PA solution */
			if (spkr_count == 4 )
				spkr_count = 2;
			#endif /*OPLUS_ARCH_EXTENDS*/
		}

		nr_bytes = spkr_count * 3;
		error = tfa_dsp_cmd_id_write_read_v6(tfa, MODULE_SPEAKERBOOST, SB_PARAM_GET_RE25C, nr_bytes, bytes);
		if (error == Tfa98xx_Error_Ok) {
			tfa98xx_convert_bytes2data_v6(nr_bytes, bytes, data);

			for(i=0; i<spkr_count; i++) {

				/* for probus devices, calibration values coming from soft-dsp speakerboost,
				   are ordered in a different way. Re-align to standard representation. */
				cal_idx=i;
				#ifdef OPLUS_ARCH_EXTENDS
				/*Add for 4 PA solution*/
				// cnt-id 0,2 is left, cnt-id 1,3 is right
				if((tfa->is_probus_device && (tfa->dev_idx % 2) == 1) ) {
				#else
				if((tfa->is_probus_device && tfa->dev_idx >= 1) ) {
				#endif/*OPLUS_ARCH_EXTENDS*/
					cal_idx=0;
				}

				/* signed data has a limit of 30 Ohm */
				scaled_data = data[i];

				if(tfa->tfa_family == 2)
					tfa->mohm[cal_idx] = (scaled_data*1000)/TFA2_FW_ReZ_SCALE;
				else
					tfa->mohm[cal_idx] = (scaled_data*1000)/TFA1_FW_ReZ_SCALE;
			}
		}
	}

	return error;
}

/* start count from 1, 0 is invalid */
int tfa_dev_get_swprof(struct tfa_device *tfa)
{
	return (tfa->dev_ops.get_swprof)(tfa);
}

int tfa_dev_set_swprof(struct tfa_device *tfa, unsigned short new_value)
{
	return (tfa->dev_ops.set_swprof)(tfa, new_value+1);
}

/*   same value for all channels
 * start count from 1, 0 is invalid */
int tfa_dev_get_swvstep(struct tfa_device *tfa)
{
	return (tfa->dev_ops.get_swvstep)(tfa);
}

int tfa_dev_set_swvstep(struct tfa_device *tfa, unsigned short new_value)
{
	return (tfa->dev_ops.set_swvstep)(tfa, new_value + 1);
}

/*
	function overload for MTPB
 */
int tfa_dev_get_mtpb(struct tfa_device *tfa)
{
	return (tfa->dev_ops.get_mtpb)(tfa);
}

int tfa_is_cold(struct tfa_device *tfa)
{
	int value;

	/*
	 * check for cold boot status
	 */
	if (tfa->is_probus_device) {
		if (tfa->ext_dsp > 0) {
			if (tfa->ext_dsp == 2)
				value = 0; // warm
			else /* no dsp or cold */
				value = 1; // cold
		} else {
			value = (TFA_GET_BF(tfa, MANSCONF) == 0);
		}
	} else {
		value = TFA_GET_BF(tfa, ACS);
	}

	return value;
}

int tfa_needs_reset(struct tfa_device *tfa)
{
	int value;

	/* checks if the DSP commands SetAlgoParams and SetMBDrc
	 * need a DSP reset (now: at coldstart or during calibration)
	 */
	if (tfa_is_cold(tfa) == 1 || tfa->needs_reset == 1)
		value = 1;
	else
		value = 0;

	return value;
}

int tfa_cf_enabled(struct tfa_device *tfa)
{
	int value;

	/* For 72 there is no CF */
	if(tfa->is_probus_device) {
		value = (tfa->ext_dsp != 0);
	} else {
		value = TFA_GET_BF(tfa, CFE);
	}

	return value;
}

#define NR_COEFFS 6
#define NR_BIQUADS 28
#define BQ_SIZE (3 * NR_COEFFS)
#define DSP_MSG_OVERHEAD 27

#pragma pack (push, 1)
struct dsp_msg_all_coeff {
	uint8_t select_eq[3];
	uint8_t biquad[NR_BIQUADS][NR_COEFFS][3];
};
#pragma pack (pop)

/* number of biquads for each equalizer */
static const int eq_biquads[] = {
	10, 10, 2, 2, 2, 2
};

#define NR_EQ (int)(sizeof(eq_biquads) / sizeof(int))

enum Tfa98xx_Error dsp_partial_coefficients(struct tfa_device *tfa, uint8_t *prev, uint8_t *next)
{
	uint8_t bq, eq;
	int eq_offset;
	int new_cost, old_cost;
	uint32_t eq_biquad_mask[NR_EQ];
	enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
	struct dsp_msg_all_coeff *data1 = (struct dsp_msg_all_coeff *)prev;
	struct dsp_msg_all_coeff *data2 = (struct dsp_msg_all_coeff *)next;

	old_cost = DSP_MSG_OVERHEAD + 3 + sizeof(struct dsp_msg_all_coeff);
	new_cost = 0;

	eq_offset = 0;
	for (eq=0; eq<NR_EQ; eq++) {
		uint8_t *eq1 = &data1->biquad[eq_offset][0][0];
		uint8_t *eq2 = &data2->biquad[eq_offset][0][0];

		eq_biquad_mask[eq] = 0;

		if (memcmp(eq1, eq2, BQ_SIZE*eq_biquads[eq]) != 0) {
			int nr_bq = 0;
			int bq_sz, eq_sz;

			for (bq=0; bq < eq_biquads[eq]; bq++) {
				uint8_t *bq1 = &eq1[bq*BQ_SIZE];
				uint8_t *bq2 = &eq2[bq*BQ_SIZE];

				if (memcmp(bq1, bq2, BQ_SIZE) != 0) {
					eq_biquad_mask[eq] |= (1<<bq);
					nr_bq++;
				}
			}

			bq_sz = (2 * 3 + BQ_SIZE) * nr_bq;
			eq_sz = 2 * 3 + BQ_SIZE * eq_biquads[eq];

			/* dsp message i2c transaction overhead */
			bq_sz += DSP_MSG_OVERHEAD * nr_bq;
			eq_sz += DSP_MSG_OVERHEAD;

			if (bq_sz >= eq_sz) {
				eq_biquad_mask[eq] = 0xffffffff;

				new_cost += eq_sz;

			} else {
				new_cost += bq_sz;
			}
		}
		pr_debug("eq_biquad_mask[%d] = 0x%.8x\n", eq, eq_biquad_mask[eq]);

		eq_offset += eq_biquads[eq];
	}

	pr_debug("cost for writing all coefficients     = %d\n", old_cost);
	pr_debug("cost for writing changed coefficients = %d\n", new_cost);

	if (new_cost >= old_cost) {
		const int buffer_sz = 3 + sizeof(struct dsp_msg_all_coeff);
		uint8_t *buffer;

		buffer = kmalloc(buffer_sz, GFP_KERNEL);
		if (buffer == NULL)
			return Tfa98xx_Error_Fail;

		/* cmd id */
		buffer[0] = 0x00;
		buffer[1] = 0x82;
		buffer[2] = 0x00;

		/* parameters */
		memcpy(&buffer[3], data2, sizeof(struct dsp_msg_all_coeff));

		err = dsp_msg(tfa, buffer_sz, (const char *)buffer);

		kfree(buffer);
		if (err)
			return err;

	} else {
		eq_offset = 0;
		for (eq=0; eq<NR_EQ; eq++) {
			uint8_t *eq2 = &data2->biquad[eq_offset][0][0];

			if (eq_biquad_mask[eq] == 0xffffffff) {
				const int msg_sz = 6 + BQ_SIZE * eq_biquads[eq];
				uint8_t *msg;

				msg = kmalloc(msg_sz, GFP_KERNEL);
				if (msg == NULL)
					return Tfa98xx_Error_Fail;

				/* cmd id */
				msg[0] = 0x00;
				msg[1] = 0x82;
				msg[2] = 0x00;

				/* select eq and bq */
				msg[3] = 0x00;
				msg[4] = eq+1;
				msg[5] = 0x00; /* all biquads */

				/* biquad parameters */
				memcpy(&msg[6], eq2, BQ_SIZE * eq_biquads[eq]);

				err = dsp_msg(tfa, msg_sz, (const char *)msg);

				kfree(msg);
				if (err)
					return err;

			} else if (eq_biquad_mask[eq] != 0) {
				for(bq=0; bq < eq_biquads[eq]; bq++) {

					if (eq_biquad_mask[eq] & (1<<bq)) {
						uint8_t *bq2 = &eq2[bq*BQ_SIZE];
						const int msg_sz = 6 + BQ_SIZE;
						uint8_t *msg;

						msg = kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
						if (msg == NULL)
							return Tfa98xx_Error_Fail;

						/* cmd id */
						msg[0] = 0x00;
						msg[1] = 0x82;
						msg[2] = 0x00;

						/* select eq and bq*/
						msg[3] = 0x00;
						msg[4] = eq+1;
						msg[5] = bq+1;

						/* biquad parameters */
						memcpy(&msg[6], bq2, BQ_SIZE);

						err = dsp_msg(tfa, msg_sz, (const char *)msg);

						kmem_cache_free(tfa->cachep, msg);
						if (err)
							return err;
					}
				}
			}
			eq_offset += eq_biquads[eq];
		}
	}

	return err;
}

/* fill context info */
int tfa_dev_probe(int slave, struct tfa_device *tfa)
{
	uint16_t rev;

	tfa->slave_address = (unsigned char)slave;

	/* read revid via low level hal, register 3 */
	if (tfa98xx_read_register16_v6(tfa, 3, &rev) != Tfa98xx_Error_Ok) {
		pr_err("Error: Unable to read revid from slave:0x%02x\n", slave);
		return -1;
	}

	tfa->rev = rev;
	tfa->dev_idx = -1;
	tfa->state = TFA_STATE_UNKNOWN;
	tfa->p_regInfo = NULL;

	tfa_set_query_info(tfa);

	tfa->in_use = 1;
	pr_info("%s: tfa[0x%02x] done.\n", __func__, slave);

	return 0;
}

enum tfa_error tfa_dev_set_state(struct tfa_device *tfa, enum tfa_state state)
{
	enum tfa_error err = tfa_error_ok;
	int loop = 50, ready = 0;
	int count;

	/* Base states */
	/* Do not change the order of setting bits as this is important! */
	switch (state & 0x0f) {
	case TFA_STATE_POWERDOWN:    /* PLL in powerdown, Algo up */
		break;
	case TFA_STATE_INIT_HW:      /* load I2C/PLL hardware setting (~wait2srcsettings) */
		break;
	case TFA_STATE_INIT_CF:      /* coolflux HW access possible (~initcf) */
								 /* Start with SBSL=0 to stay in initCF state */
		if(!tfa->is_probus_device)
			TFA_SET_BF(tfa, SBSL, 0);

		/* We want to leave Wait4SrcSettings state for max2 */
		if (tfa->tfa_family == 2)
			TFA_SET_BF(tfa, MANSCONF, 1);

		/* And finally set PWDN to 0 to leave powerdown state */
		TFA_SET_BF(tfa, PWDN, 0);

		/* Make sure the DSP is running! */
		do {
			err = (enum tfa_error)tfa98xx_dsp_system_stable_v6(tfa, &ready);
			if (err != tfa_error_ok)
				return err;
			if (ready)
				break;
		} while (loop--);

		if (!tfa->is_probus_device)
		{
			#ifdef OPLUS_ARCH_EXTENDS
			err = (enum tfa_error)tfa98xx_set_mtp_v6(tfa, 1, TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK);
			if (err != tfa_error_ok) {
				return err;
			}
			#endif /* OPLUS_ARCH_EXTENDS */

			/* Enable FAIM when clock is stable, to avoid MTP corruption */
			err = (enum tfa_error)tfa98xx_faim_protect(tfa, 1);
			if (tfa->verbose) {
				pr_debug("FAIM enabled (err:%d).\n", err);
			}
		}
		break;
	case TFA_STATE_INIT_FW:      /* DSP framework active (~patch loaded) */
		break;
	case TFA_STATE_OPERATING:    /* Amp and Algo running */
								 /* Depending on our previous state we need to set 3 bits */
		TFA_SET_BF(tfa, PWDN, 0);	/* Coming from state 0 */
		TFA_SET_BF(tfa, MANSCONF, 1);	/* Coming from state 1 */
		if (!tfa->is_probus_device)
			TFA_SET_BF(tfa, SBSL, 1);	/* Coming from state 6 */
		else
			TFA_SET_BF(tfa, AMPE, 1);	/* No SBSL for probus device, we set AMPE to 1  */

									/*
									* Disable MTP clock to protect memory.
									* However in case of calibration wait for DSP! (This should be case only during calibration).
									*/
		#ifndef OPLUS_ARCH_EXTENDS
		if (TFA_GET_BF(tfa, MTPOTC) == 1 && tfa->tfa_family == 2) {
		#else /* OPLUS_ARCH_EXTENDS */
		if ((TFA_GET_BF(tfa, MTPOTC) == 1) && (tfa->tfa_family == 2) && (!tfa->is_probus_device)) {
		#endif /* OPLUS_ARCH_EXTENDS */
			count = MTPEX_WAIT_NTRIES * 4; /* Calibration takes a lot of time */
			while ((TFA_GET_BF(tfa, MTPEX) != 1) && count) {
				msleep_interruptible(10);
				count--;
			}
		}
		if (!tfa->is_probus_device)
		{
			err = (enum tfa_error)tfa98xx_faim_protect(tfa, 0);
			if (tfa->verbose) {
				pr_debug("FAIM disabled (err:%d).\n", err);
			}
		}
		/* Synchonize I/V delay on 96/97 at cold start */
		if (tfa->sync_iv_delay) {
			if (tfa->verbose)
				pr_debug("syncing I/V delay for %x\n",
				(tfa->rev & 0xff));

			/* wait for ACS to be cleared */
			count = 10;
			while ((TFA_GET_BF(tfa, ACS) == 1) &&
				(count-- > 0)) {
				msleep_interruptible(1);
			}

			tfa98xx_dsp_reset_v6(tfa, 1);
			tfa98xx_dsp_reset_v6(tfa, 0);
			tfa->sync_iv_delay = 0;
		}
		break;
	case TFA_STATE_FAULT:        /* An alarm or error occurred */
		break;
	case TFA_STATE_RESET:        /* I2C reset and ACS set */
		tfa98xx_init_v6(tfa);
		break;
	default:
		if (state & 0x0f)
			return tfa_error_bad_param;
	}

	/* state modifiers */

	if (state & TFA_STATE_MUTE)
		tfa98xx_set_mute_v6(tfa, Tfa98xx_Mute_Amplifier);

	if (state & TFA_STATE_UNMUTE)
		tfa98xx_set_mute_v6(tfa, Tfa98xx_Mute_Off);

	tfa->state = state;

	return tfa_error_ok;
}

enum tfa_state tfa_dev_get_state(struct tfa_device *tfa)
{
	int cold = 0;
	int manstate;

	/* different per family type */
	if ( tfa->tfa_family == 1 ) {
		cold = TFA_GET_BF(tfa, ACS);
		if (  cold && TFA_GET_BF(tfa, PWDN) )
			tfa->state = TFA_STATE_RESET;
		else if ( !cold && TFA_GET_BF(tfa, SWS))
			tfa->state = TFA_STATE_OPERATING;
	} else /* family 2 */ {
		if (is_94_N2_device(tfa))
			manstate = tfa_get_bf_v6(tfa, TFA9894N2_BF_MANSTATE);
		else
			manstate = TFA_GET_BF(tfa, MANSTATE);
		switch(manstate) {
			case 0:
				tfa->state = TFA_STATE_POWERDOWN;
				break;
			case 8: /* if dsp reset if off assume framework is running */
				tfa->state = TFA_GET_BF(tfa, RST) ? TFA_STATE_INIT_CF : TFA_STATE_INIT_FW;
				break;
			case 9:
				tfa->state = TFA_STATE_OPERATING;
				break;
			default:
				break;
		}
	}

	return tfa->state;
}

int tfa_dev_mtp_get(struct tfa_device *tfa, enum tfa_mtp item)
{
	int value = 0;

	#ifdef OPLUS_ARCH_EXTENDS
	if(tfa->in_use == 0) {
		pr_info("%s: tfa is not opened!", __func__);
		return value;
	}
	#endif /* OPLUS_ARCH_EXTENDS */

	switch (item) {
		case TFA_MTP_OTC:
			value = TFA_GET_BF(tfa, MTPOTC);
			break;
		case TFA_MTP_EX:
			value = TFA_GET_BF(tfa, MTPEX);
			break;
		case TFA_MTP_RE25:
		case TFA_MTP_RE25_PRIM:
			if(tfa->tfa_family == 2) {
				if((tfa->rev & 0xFF) == 0x88)
					value = TFA_GET_BF(tfa, R25CL);
				else if((tfa->rev & 0xFF) == 0x13)
					value = tfa_get_bf_v6(tfa, TFA9912_BF_R25C);
				else
					value = TFA_GET_BF(tfa, R25C);
			} else {
				tfa_reg_read(tfa, 0x83, (unsigned short*)&value);
			}
			break;
		case TFA_MTP_RE25_SEC:
			if((tfa->rev & 0xFF) == 0x88) {
				value = TFA_GET_BF(tfa, R25CR);
			} else {
				pr_debug("Error: Current device has no secondary Re25 channel \n");
			}
			break;
		case TFA_MTP_LOCK:
			break;
	}

	return value;
}

enum tfa_error tfa_dev_mtp_set(struct tfa_device *tfa, enum tfa_mtp item, int value)
{
	enum tfa_error err = tfa_error_ok;

	#ifdef OPLUS_ARCH_EXTENDS
	if(tfa->in_use == 0) {
		pr_info("%s: tfa is not opened!", __func__);
		return tfa_error_device;
	}
	#endif /* OPLUS_ARCH_EXTENDS */

	switch (item) {
		case TFA_MTP_OTC:
			err = (enum tfa_error)tfa98xx_set_mtp_v6(tfa, (uint16_t)value, TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK);
			break;
		case TFA_MTP_EX:
			err = (enum tfa_error)tfa98xx_set_mtp_v6(tfa, (uint16_t)value, TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK);
			break;
		case TFA_MTP_RE25:
		case TFA_MTP_RE25_PRIM:
			if(tfa->tfa_family == 2) {
				tfa98xx_key2_v6(tfa, 0); /* unlock */
				if((tfa->rev & 0xFF) == 0x88)
					TFA_SET_BF(tfa, R25CL, (uint16_t)value);
				else
				{
					if (tfa->is_probus_device == 1)
						tfa2_manual_mtp_cpy(tfa, 0xF4, value, 2);
					TFA_SET_BF(tfa, R25C, (uint16_t)value);
				}
				tfa98xx_key2_v6(tfa, 1); /* lock */
			}
			break;
		case TFA_MTP_RE25_SEC:
			if((tfa->rev & 0xFF) == 0x88) {
				TFA_SET_BF(tfa, R25CR, (uint16_t)value);
			} else {
				pr_debug("Error: Current device has no secondary Re25 channel \n");
				err = tfa_error_bad_param;
			}
			break;
		case TFA_MTP_LOCK:
			break;
	}

	return err;
}

int tfa_get_pga_gain(struct tfa_device *tfa)
{
	return TFA_GET_BF(tfa, SAAMGAIN);
}

int tfa_set_pga_gain(struct tfa_device *tfa, uint16_t value)
{

	return TFA_SET_BF(tfa, SAAMGAIN, value);
}

int tfa_get_noclk(struct tfa_device *tfa)
{
	return TFA_GET_BF(tfa, NOCLK);
}


enum Tfa98xx_Error tfa_status(struct tfa_device *tfa)
{
	int value;
	uint16_t val;

	/*
	 * check IC status bits: cold start
	 * and DSP watch dog bit to re init
	 */
	value = TFA_READ_REG(tfa, VDDS); /* STATUSREG */
	if (value < 0)
		return -value;
	val = (uint16_t)value;

	/* pr_debug("SYS_STATUS0: 0x%04x\n", val); */
	if (TFA_GET_BF_VALUE(tfa, ACS, val) ||
	    TFA_GET_BF_VALUE(tfa, WDS, val)) {

		if (TFA_GET_BF_VALUE(tfa, ACS, val))
			pr_err("ERROR: ACS\n");
		if (TFA_GET_BF_VALUE(tfa, WDS, val))
			pr_err("ERROR: WDS\n");

		return Tfa98xx_Error_DSP_not_running;
	}

	if (TFA_GET_BF_VALUE(tfa, SPKS, val))
		pr_err("ERROR: SPKS\n");
	if (!TFA_GET_BF_VALUE(tfa, SWS, val))
		pr_err("ERROR: SWS\n");

	/* Check secondary errors */
	if (!TFA_GET_BF_VALUE(tfa, CLKS, val) ||
	    !TFA_GET_BF_VALUE(tfa, UVDS, val) ||
	    !TFA_GET_BF_VALUE(tfa, OVDS, val) ||
	    !TFA_GET_BF_VALUE(tfa, OTDS, val) ||
	    !TFA_GET_BF_VALUE(tfa, PLLS, val) ||
		(!(tfa->daimap & Tfa98xx_DAI_TDM) &&
		 !TFA_GET_BF_VALUE(tfa, VDDS, val)) )
		pr_err("Misc errors detected: STATUS_FLAG0 = 0x%x\n", val);

	if ((tfa->daimap & Tfa98xx_DAI_TDM) && (tfa->tfa_family == 2)) {
		value = TFA_READ_REG(tfa, TDMERR); /* STATUS_FLAGS1 */
		if (value < 0)
			return -value;
		val = (uint16_t)value;
		if (TFA_GET_BF_VALUE(tfa, TDMERR, val) ||
		    TFA_GET_BF_VALUE(tfa, TDMLUTER, val))
			pr_err("TDM related errors: STATUS_FLAG1 = 0x%x\n", val);
	}

	return Tfa98xx_Error_Ok;
}

#define NR_OF_BATS 10
void tfa_adapt_noisemode(struct tfa_device *tfa)
{
	int i, avbatt;
	long total_bats = 0;
	if ((tfa_get_bf_v6(tfa, 0x5900) == 0) || (tfa_get_bf_v6(tfa, TFA9873_BF_LP1) == 1))
	{
		if (tfa->verbose)
			pr_debug("Adapting low noise mode is not needed, condition not fulfilled!\n");
		return;
	}
	if (tfa->verbose)
		pr_debug("Adapting low noise mode\n");

	for (i = 0; i < NR_OF_BATS; i++) {
		int bat = TFA_GET_BF(tfa, BATS);
		if (tfa->verbose)
			pr_debug("bats[%d]=%d\n", i, bat);
		total_bats += bat;
		msleep_interruptible(5);
	}

	avbatt = (int)(total_bats / NR_OF_BATS);

	if (avbatt <= MIN_BATT_LEVEL && !tfa_get_bf_v6(tfa, TFA9873_BF_LNMODE))//640 corresponds to 3.4 volt, MCH_TO_TEST
	{
		tfa_set_bf_v6(tfa, TFA9873_BF_LNMODE, 1);
		pr_debug("\navbatt= %d--Applying high noise gain\n", avbatt);
	}
	else if (avbatt > MAX_BATT_LEVEL && tfa_get_bf_v6(tfa, TFA9873_BF_LNMODE))
	{
		tfa_set_bf_v6(tfa, TFA9873_BF_LNMODE, 0);
		pr_debug("\navbatt= %d--Applying automatic noise gain\n", avbatt);
	}


}
int tfa_plop_noise_interrupt(struct tfa_device *tfa, int profile, int vstep)
{
	enum Tfa98xx_Error err;
	int no_clk=0;

	/* Remove sticky bit by reading it once */
	TFA_GET_BF(tfa, NOCLK);

	/* No clock detected */
	if (tfa_irq_get(tfa, tfa9912_irq_stnoclk)) {
		no_clk = TFA_GET_BF(tfa, NOCLK);

		/* Detect for clock is lost! (clock is not stable) */
		if (no_clk == 1) {
			/* Clock is lost. Set I2CR to remove POP noise */
			pr_info("No clock detected. Resetting the I2CR to avoid pop on 72! \n");
			err = (enum Tfa98xx_Error)tfa_dev_start(tfa, profile, vstep);
			if (err != Tfa98xx_Error_Ok) {
				pr_err("Error loading i2c registers (tfa_dev_start), err=%d\n", err);
			} else {
				pr_info("Setting i2c registers after I2CR succesfull\n");
				tfa_dev_set_state(tfa, TFA_STATE_UNMUTE);
			}

			/* Remove sticky bit by reading it once */
			tfa_get_noclk(tfa);

			/* This is only for SAAM on the 72.
			   Since the NOCLK interrupt is only enabled for 72 this is the place
			   However: Not tested yet! But also does not harm normal flow!
			*/
			if (strstr(tfaContProfileName_v6(tfa->cnt, tfa->dev_idx, profile), ".saam")) {
				pr_info("Powering down from a SAAM profile, workaround PLMA4766 used! \n");
				TFA_SET_BF(tfa, PWDN, 1);
				TFA_SET_BF(tfa, AMPE, 0);
				TFA_SET_BF(tfa, SAMMODE, 0);
			}
		}

		/* If clk is stable set polarity to check for LOW (no clock)*/
		tfa_irq_set_pol(tfa, tfa9912_irq_stnoclk, (no_clk == 0));

		/* clear interrupt */
		tfa_irq_clear(tfa, tfa9912_irq_stnoclk);
	}

	/* return no_clk to know we called tfa_dev_start */
	return no_clk;
}

void tfa_lp_mode_interrupt(struct tfa_device *tfa)
{
	const int irq_stclp0 = 36; /* FIXME: this 72 interrupt does not excist for 9912 */
	int lp0, lp1;

	if (tfa_irq_get(tfa, irq_stclp0)) {
		lp0 = TFA_GET_BF(tfa, LP0);
		if (lp0 > 0) {
			pr_info("lowpower mode 0 detected\n");
		} else {
			pr_info("lowpower mode 0 not detected\n");
		}

		tfa_irq_set_pol(tfa, irq_stclp0, (lp0 == 0));

		/* clear interrupt */
		tfa_irq_clear(tfa, irq_stclp0);
	}

	if (tfa_irq_get(tfa, tfa9912_irq_stclpr)) {
		lp1 = TFA_GET_BF(tfa, LP1);
		if (lp1 > 0) {
			pr_info("lowpower mode 1 detected\n");
		} else {
			pr_info("lowpower mode 1 not detected\n");
		}

		tfa_irq_set_pol(tfa, tfa9912_irq_stclpr, (lp1 == 0));

		/* clear interrupt */
		tfa_irq_clear(tfa, tfa9912_irq_stclpr);
	}
}
