1972 lines
55 KiB
C
Executable file
1972 lines
55 KiB
C
Executable file
/*
|
|
* Copyright (C) 2014-2020 NXP Semiconductors, All Rights Reserved.
|
|
* Copyright 2020 GOODIX, 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 "inc/dbgprint.h"
|
|
#include "inc/tfa_service.h"
|
|
#include "inc/tfa_internal.h"
|
|
#include "inc/tfa_container.h"
|
|
#include "inc/tfa98xx_tfafieldnames.h"
|
|
|
|
/* The CurrentSense4 registers are not in the datasheet */
|
|
#define TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF (1 << 2)
|
|
#define TFA98XX_CURRENTSENSE4 0x49
|
|
|
|
/*********************/
|
|
/* GLOBAL (Defaults) */
|
|
/*********************/
|
|
|
|
static bool ipc_loaded;
|
|
|
|
void tfa_set_ipc_loaded(int status)
|
|
{
|
|
pr_info("set ipc_loaded %d for tfadsp\n", status);
|
|
ipc_loaded = (status) ? true : false;
|
|
}
|
|
|
|
int tfa_get_ipc_loaded(void)
|
|
{
|
|
pr_info("get ipc_loaded %d for tfadsp\n", ipc_loaded);
|
|
return ipc_loaded ? 1 : 0;
|
|
}
|
|
|
|
static enum tfa98xx_error no_overload_function_available
|
|
(struct tfa_device *tfa, int not_used)
|
|
{
|
|
(void)tfa;
|
|
(void)not_used;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static enum tfa98xx_error no_overload_function_available2
|
|
(struct tfa_device *tfa)
|
|
{
|
|
(void)tfa;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
/* tfa98xx_dsp_system_stable
|
|
* return: *ready = 1 when clocks are stable to allow DSP subsystem access
|
|
*/
|
|
static enum tfa98xx_error tfa_dsp_system_stable
|
|
(struct tfa_device *tfa, int *ready)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short status;
|
|
int value;
|
|
|
|
/* check the contents of the STATUS register */
|
|
value = TFA_READ_REG(tfa, AREFS);
|
|
if (value < 0) {
|
|
error = -value;
|
|
*ready = 0;
|
|
_ASSERT(error); /* an error here can be fatal */
|
|
return error;
|
|
}
|
|
status = (unsigned short)value;
|
|
|
|
/* check AREFS and CLKS: not ready if either is clear */
|
|
*ready = !((TFA_GET_BF_VALUE(tfa, AREFS, status) == 0)
|
|
|| (TFA_GET_BF_VALUE(tfa, CLKS, status) == 0));
|
|
|
|
return error;
|
|
}
|
|
|
|
/* tfa98xx_toggle_mtp_clock
|
|
* Allows to stop clock for MTP/FAim needed for PLMA5505
|
|
*/
|
|
static enum tfa98xx_error tfa_faim_protect(struct tfa_device *tfa, int state)
|
|
{
|
|
(void)tfa;
|
|
(void)state;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
/** Set internal oscillator into power down mode.
|
|
*
|
|
* This function is a worker for tfa98xx_set_osc_powerdown().
|
|
*
|
|
* @param[in] tfa device description structure
|
|
* @param[in] state new state 0 - oscillator is on, 1 oscillator is off.
|
|
*
|
|
* @return TFA98XX_ERROR_OK when successful, error otherwise.
|
|
*/
|
|
static enum tfa98xx_error tfa_set_osc_powerdown
|
|
(struct tfa_device *tfa, int state)
|
|
{
|
|
/* This function has no effect in general case, only for tfa9912 */
|
|
(void)tfa;
|
|
(void)state;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa_update_lpm(struct tfa_device *tfa, int state)
|
|
{
|
|
/* This function has no effect in general case, only for tfa9912 */
|
|
(void)tfa;
|
|
(void)state;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa_dsp_reset(struct tfa_device *tfa, int state)
|
|
{
|
|
/* generic function */
|
|
TFA_SET_BF_VOLATILE(tfa, RST, (uint16_t)state);
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
int tfa_set_swprofile(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
int mtpk, active_value = tfa->profile;
|
|
|
|
/* Also set the new value in the struct */
|
|
tfa->profile = new_value - 1;
|
|
|
|
/* for TFA1 devices */
|
|
/* it's in MTP shadow, so unlock if not done already */
|
|
mtpk = TFA_GET_BF(tfa, MTPK); /* get current key */
|
|
TFA_SET_BF_VOLATILE(tfa, MTPK, 0x5a);
|
|
TFA_SET_BF_VOLATILE(tfa, SWPROFIL, new_value); /* set current profile */
|
|
TFA_SET_BF_VOLATILE(tfa, MTPK, (uint16_t)mtpk); /* restore key */
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfa_get_swprofile(struct tfa_device *tfa)
|
|
{
|
|
/* return TFA_GET_BF(tfa, SWPROFIL) - 1; */
|
|
return tfa->profile;
|
|
}
|
|
|
|
static int tfa_set_swvstep(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
int mtpk, active_value = tfa->vstep;
|
|
|
|
/* Also set the new value in the struct */
|
|
tfa->vstep = new_value - 1;
|
|
|
|
/* for TFA1 devices */
|
|
/* it's in MTP shadow, so unlock if not done already */
|
|
mtpk = TFA_GET_BF(tfa, MTPK); /* get current key */
|
|
TFA_SET_BF_VOLATILE(tfa, MTPK, 0x5a);
|
|
TFA_SET_BF_VOLATILE(tfa, SWVSTEP, new_value); /* set current vstep */
|
|
TFA_SET_BF_VOLATILE(tfa, MTPK, (uint16_t)mtpk); /* restore key */
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfa_get_swvstep(struct tfa_device *tfa)
|
|
{
|
|
int value = 0;
|
|
/* Set the new value in the hw register */
|
|
value = TFA_GET_BF(tfa, SWVSTEP);
|
|
|
|
/* Also set the new value in the struct */
|
|
tfa->vstep = value - 1;
|
|
|
|
return value - 1; /* invalid if 0 */
|
|
}
|
|
|
|
static int tfa_get_mtpb(struct tfa_device *tfa)
|
|
{
|
|
|
|
int value = 0;
|
|
|
|
/* Set the new value in the hw register */
|
|
value = TFA_GET_BF(tfa, MTPB);
|
|
|
|
return value;
|
|
}
|
|
|
|
static enum tfa98xx_error
|
|
tfa_set_mute_nodsp(struct tfa_device *tfa, int mute)
|
|
{
|
|
(void)tfa;
|
|
(void)mute;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
void set_ops_defaults(struct tfa_device_ops *ops)
|
|
{
|
|
/* defaults */
|
|
ops->reg_read = tfa98xx_read_register16;
|
|
ops->reg_write = tfa98xx_write_register16;
|
|
ops->mem_read = tfa98xx_dsp_read_mem;
|
|
ops->mem_write = tfa98xx_dsp_write_mem_word;
|
|
if (!ipc_loaded) {
|
|
#if defined(TFA_SET_EXT_INTERNALLY)
|
|
ops->dsp_msg = tfa_dsp_msg;
|
|
ops->dsp_msg_read = tfa_dsp_msg_read;
|
|
#else
|
|
ops->dsp_msg = NULL;
|
|
ops->dsp_msg_read = NULL;
|
|
#endif
|
|
}
|
|
ops->dsp_write_tables = no_overload_function_available;
|
|
ops->dsp_reset = tfa_dsp_reset;
|
|
ops->dsp_system_stable = tfa_dsp_system_stable;
|
|
ops->auto_copy_mtp_to_iic = no_overload_function_available2;
|
|
ops->factory_trimmer = no_overload_function_available2;
|
|
ops->set_swprof = tfa_set_swprofile;
|
|
ops->get_swprof = tfa_get_swprofile;
|
|
ops->set_swvstep = tfa_set_swvstep;
|
|
ops->get_swvstep = tfa_get_swvstep;
|
|
ops->get_mtpb = tfa_get_mtpb;
|
|
ops->set_mute = tfa_set_mute_nodsp;
|
|
ops->faim_protect = tfa_faim_protect;
|
|
ops->set_osc_powerdown = tfa_set_osc_powerdown;
|
|
ops->update_lpm = tfa_update_lpm;
|
|
}
|
|
|
|
/****************************/
|
|
/* no TFA
|
|
* external DSP SB instance
|
|
****************************/
|
|
static short tfanone_swvstep, swprof; /* TODO emulate in hal plugin */
|
|
static enum tfa98xx_error tfanone_dsp_system_stable
|
|
(struct tfa_device *tfa, int *ready)
|
|
{
|
|
(void)tfa; /* suppress warning */
|
|
*ready = 1; /* assume always ready */
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static int tfanone_set_swprofile
|
|
(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
int active_value = tfa_dev_get_swprof(tfa);
|
|
|
|
/* Set the new value in the struct */
|
|
tfa->profile = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
swprof = new_value;
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfanone_get_swprofile(struct tfa_device *tfa)
|
|
{
|
|
(void)tfa; /* suppress warning */
|
|
|
|
return swprof;
|
|
}
|
|
|
|
static int tfanone_set_swvstep
|
|
(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
/* Set the new value in the struct */
|
|
tfa->vstep = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
tfanone_swvstep = new_value;
|
|
|
|
return new_value;
|
|
}
|
|
|
|
static int tfanone_get_swvstep(struct tfa_device *tfa)
|
|
{
|
|
(void)tfa; /* suppress warning */
|
|
|
|
return tfanone_swvstep;
|
|
}
|
|
|
|
void tfanone_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->dsp_system_stable = tfanone_dsp_system_stable;
|
|
ops->set_swprof = tfanone_set_swprofile;
|
|
ops->get_swprof = tfanone_get_swprofile;
|
|
ops->set_swvstep = tfanone_set_swvstep;
|
|
ops->get_swvstep = tfanone_get_swvstep;
|
|
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9912 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9912_faim_protect(struct tfa_device *tfa,
|
|
int status)
|
|
{
|
|
enum tfa98xx_error ret = TFA98XX_ERROR_FAIL;
|
|
|
|
if (!tfa)
|
|
return TFA98XX_ERROR_FAIL;
|
|
|
|
if (status == 0 || status == 1)
|
|
ret = -(tfa_set_bf(tfa, TFA9912_BF_SSFAIME, (uint16_t)status));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9912_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short value, xor;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* Unlock keys to write settings */
|
|
error = reg_write(tfa, 0x0F, 0x5A6B);
|
|
error = reg_read(tfa, 0xFB, &value);
|
|
xor = value ^ 0x005A;
|
|
error = reg_write(tfa, 0xA0, xor);
|
|
|
|
/* The optimal settings */
|
|
if (tfa->rev == 0x1a13) {
|
|
/* ----- generated code start ----- */
|
|
/* ----- version 1.43 ----- */
|
|
reg_write(tfa, 0x00, 0x0255); /* POR=0x0245 */
|
|
reg_write(tfa, 0x01, 0x838a); /* POR=0x83ca */
|
|
reg_write(tfa, 0x02, 0x2dc8); /* POR=0x2828 */
|
|
reg_write(tfa, 0x05, 0x762a); /* POR=0x766a */
|
|
reg_write(tfa, 0x22, 0x543c); /* POR=0x545c */
|
|
reg_write(tfa, 0x26, 0x0100); /* POR=0x0010 */
|
|
reg_write(tfa, 0x51, 0x0000); /* POR=0x0080 */
|
|
reg_write(tfa, 0x52, 0x551c); /* POR=0x1afc */
|
|
reg_write(tfa, 0x53, 0x003e); /* POR=0x001e */
|
|
reg_write(tfa, 0x61, 0x000c); /* POR=0x0018 */
|
|
reg_write(tfa, 0x63, 0x0a96); /* POR=0x0a9a */
|
|
reg_write(tfa, 0x65, 0x0a82); /* POR=0x0a8b */
|
|
reg_write(tfa, 0x66, 0x0701); /* POR=0x0700 */
|
|
reg_write(tfa, 0x6c, 0x00d5); /* POR=0x02d5 */
|
|
reg_write(tfa, 0x70, 0x26f8); /* POR=0x06e0 */
|
|
reg_write(tfa, 0x71, 0x3074); /* POR=0x2074 */
|
|
reg_write(tfa, 0x75, 0x4484); /* POR=0x4585 */
|
|
reg_write(tfa, 0x76, 0x72ea); /* POR=0x54a2 */
|
|
reg_write(tfa, 0x83, 0x0716); /* POR=0x0617 */
|
|
reg_write(tfa, 0x89, 0x0013); /* POR=0x0014 */
|
|
reg_write(tfa, 0xb0, 0x4c08); /* POR=0x4c00 */
|
|
reg_write(tfa, 0xc6, 0x004e); /* POR=0x000e */
|
|
/* PLMA5539: Please make sure bit 6 is always on! */
|
|
/* ----- generated code end ----- */
|
|
|
|
/* PLMA5505: MTP key open makes vulanable for MTP corruption */
|
|
tfa9912_faim_protect(tfa, 0);
|
|
} else {
|
|
pr_info("Warning: Optimal settings not found for device with revid = 0x%x\n",
|
|
tfa->rev);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9912_factory_trimmer(struct tfa_device *tfa)
|
|
{
|
|
unsigned short current_value, delta;
|
|
int result;
|
|
|
|
/* Factory trimming for the Boost converter */
|
|
/* check if there is a correction needed */
|
|
result = TFA_GET_BF(tfa, DCMCCAPI);
|
|
if (result) {
|
|
/* Get currentvalue of DCMCC and the Delta value */
|
|
current_value = (unsigned short)TFA_GET_BF(tfa, DCMCC);
|
|
delta = (unsigned short)TFA_GET_BF(tfa, USERDEF);
|
|
|
|
/* check the sign bit (+/-) */
|
|
result = TFA_GET_BF(tfa, DCMCCSB);
|
|
if (result == 0) {
|
|
/* Do not exceed the maximum value of 15 */
|
|
if (current_value + delta < 15) {
|
|
TFA_SET_BF_VOLATILE(tfa, DCMCC,
|
|
current_value + delta);
|
|
if (tfa->verbose)
|
|
pr_debug("Max coil current is set to: %d\n",
|
|
current_value + delta);
|
|
} else {
|
|
TFA_SET_BF_VOLATILE(tfa, DCMCC, 15);
|
|
if (tfa->verbose)
|
|
pr_debug("Max coil current is set to: 15\n");
|
|
}
|
|
} else if (result == 1) {
|
|
/* Do not exceed the minimum value of 0 */
|
|
if (current_value - delta > 0) {
|
|
TFA_SET_BF_VOLATILE(tfa, DCMCC,
|
|
current_value - delta);
|
|
if (tfa->verbose)
|
|
pr_debug("Max coil current is set to: %d\n",
|
|
current_value - delta);
|
|
} else {
|
|
TFA_SET_BF_VOLATILE(tfa, DCMCC, 0);
|
|
if (tfa->verbose)
|
|
pr_debug("Max coil current is set to: 0\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9912_auto_copy_mtp_to_iic(struct tfa_device *tfa)
|
|
{
|
|
/* Set auto_copy_mtp_to_iic (bit 5 of A3) */
|
|
/* to 1. Workaround for 72, 88 and 9912/9892(see PLMA5290) */
|
|
return reg_write(tfa, 0xA3, 0x20);
|
|
}
|
|
|
|
static int tfa9912_set_swprofile(struct tfa_device *tfa,
|
|
unsigned short new_value)
|
|
{
|
|
int active_value = tfa_dev_get_swprof(tfa);
|
|
|
|
/* Set the new value in the struct */
|
|
tfa->profile = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
tfa_set_bf_volatile(tfa, TFA9912_BF_SWPROFIL, new_value);
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfa9912_get_swprofile(struct tfa_device *tfa)
|
|
{
|
|
return tfa_get_bf(tfa, TFA9912_BF_SWPROFIL) - 1;
|
|
}
|
|
|
|
static int tfa9912_set_swvstep(struct tfa_device *tfa,
|
|
unsigned short new_value)
|
|
{
|
|
/* Set the new value in the struct */
|
|
tfa->vstep = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
tfa_set_bf_volatile(tfa, TFA9912_BF_SWVSTEP, new_value);
|
|
|
|
return new_value;
|
|
}
|
|
|
|
static int tfa9912_get_swvstep(struct tfa_device *tfa)
|
|
{
|
|
return tfa_get_bf(tfa, TFA9912_BF_SWVSTEP) - 1;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9912_set_mute(struct tfa_device *tfa, int mute)
|
|
{
|
|
tfa_set_bf(tfa, TFA9912_BF_CFSM, (const uint16_t)mute);
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
/* Maximum value for combination of boost_voltage */
|
|
/* and vout calibration offset (see PLMA5322, PLMA5528). */
|
|
#define TFA9912_VBOOST_MAX 57
|
|
#define TFA9912_CALIBR_BOOST_MAX 63
|
|
#define TFA9912_DCDCCNT6_REG (TFA9912_BF_DCVOF >> 8)
|
|
#define TFA9912_CALIBR_REG 0xf1
|
|
|
|
static uint16_t tfa9912_vboost_fixup(struct tfa_device *tfa,
|
|
uint16_t dcdc_cnt6)
|
|
{
|
|
unsigned short cal_offset;
|
|
unsigned short boost_v_1st, boost_v_2nd;
|
|
uint16_t new_dcdc_cnt6;
|
|
|
|
/* Get current calibr_vout_offset, */
|
|
/* this register is not supported by bitfields */
|
|
reg_read(tfa, TFA9912_CALIBR_REG, &cal_offset);
|
|
cal_offset = (cal_offset & 0x001f);
|
|
new_dcdc_cnt6 = dcdc_cnt6;
|
|
|
|
/* Get current boost_volatage values */
|
|
boost_v_1st = tfa_get_bf_value(TFA9912_BF_DCVOF, new_dcdc_cnt6);
|
|
boost_v_2nd = tfa_get_bf_value(TFA9912_BF_DCVOS, new_dcdc_cnt6);
|
|
|
|
/* Check boost voltages */
|
|
if (boost_v_1st > TFA9912_VBOOST_MAX)
|
|
boost_v_1st = TFA9912_VBOOST_MAX;
|
|
|
|
if (boost_v_2nd > TFA9912_VBOOST_MAX)
|
|
boost_v_2nd = TFA9912_VBOOST_MAX;
|
|
|
|
/* Recalculate values, max for the sum is TFA9912_CALIBR_BOOST_MAX */
|
|
if (boost_v_1st + cal_offset > TFA9912_CALIBR_BOOST_MAX)
|
|
boost_v_1st = TFA9912_CALIBR_BOOST_MAX - cal_offset;
|
|
|
|
if (boost_v_2nd + cal_offset > TFA9912_CALIBR_BOOST_MAX)
|
|
boost_v_2nd = TFA9912_CALIBR_BOOST_MAX - cal_offset;
|
|
|
|
tfa_set_bf_value(TFA9912_BF_DCVOF, boost_v_1st, &new_dcdc_cnt6);
|
|
tfa_set_bf_value(TFA9912_BF_DCVOS, boost_v_2nd, &new_dcdc_cnt6);
|
|
|
|
/* Change register value only when it's necessary */
|
|
if (new_dcdc_cnt6 != dcdc_cnt6) {
|
|
if (tfa->verbose)
|
|
pr_debug("tfa9912: V boost fixup applied. Old 0x%04x, new 0x%04x\n",
|
|
dcdc_cnt6, new_dcdc_cnt6);
|
|
dcdc_cnt6 = new_dcdc_cnt6;
|
|
}
|
|
|
|
return dcdc_cnt6;
|
|
}
|
|
|
|
/* PLMA5322, PLMA5528 */
|
|
/* - Limit values of DCVOS and DCVOF to range specified in datasheet. */
|
|
enum tfa98xx_error tfa9912_reg_write(struct tfa_device *tfa,
|
|
unsigned char subaddress, unsigned short value)
|
|
{
|
|
if (subaddress == TFA9912_DCDCCNT6_REG) {
|
|
/* Correct V boost (first and secondary) */
|
|
/* to ensure 12V is not exceeded. */
|
|
value = tfa9912_vboost_fixup(tfa, value);
|
|
}
|
|
|
|
return tfa98xx_write_register16(tfa, subaddress, value);
|
|
}
|
|
|
|
/* Set internal oscillator into power down mode for TFA9912.
|
|
*
|
|
* This function is a worker for tfa98xx_set_osc_powerdown().
|
|
*
|
|
* @param[in] tfa device description structure
|
|
* @param[in] state new state 0 - oscillator is on, 1 oscillator is off.
|
|
*
|
|
* @return TFA98XX_ERROR_OK when successful, error otherwise.
|
|
*/
|
|
static enum tfa98xx_error tfa9912_set_osc_powerdown(struct tfa_device *tfa,
|
|
int state)
|
|
{
|
|
if (state == 1 || state == 0)
|
|
return -tfa_set_bf(tfa, TFA9912_BF_MANAOOSC, (uint16_t)state);
|
|
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
/* update low power mode of the device.
|
|
*
|
|
* @param[in] tfa device description structure
|
|
* @param[in] state State of the low power mode1 detector control
|
|
* 0 - low power mode1 detector control enabled,
|
|
* 1 - low power mode1 detector control disabled (low power mode disabled).
|
|
*
|
|
* @return TFA98XX_ERROR_OK when successful, error otherwise.
|
|
*/
|
|
static enum tfa98xx_error tfa9912_update_lpm(struct tfa_device *tfa,
|
|
int state)
|
|
{
|
|
if (state == 1 || state == 0)
|
|
return -tfa_set_bf(tfa, TFA9912_BF_LPM1DIS, (uint16_t)state);
|
|
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
void tfa9912_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9912_specific;
|
|
/* PLMA5322, PLMA5528 - Limits values of DCVOS and DCVOF. */
|
|
ops->reg_write = tfa9912_reg_write;
|
|
ops->factory_trimmer = tfa9912_factory_trimmer;
|
|
ops->auto_copy_mtp_to_iic = tfa9912_auto_copy_mtp_to_iic;
|
|
ops->set_swprof = tfa9912_set_swprofile;
|
|
ops->get_swprof = tfa9912_get_swprofile;
|
|
ops->set_swvstep = tfa9912_set_swvstep;
|
|
ops->get_swvstep = tfa9912_get_swvstep;
|
|
ops->set_mute = tfa9912_set_mute;
|
|
ops->faim_protect = tfa9912_faim_protect;
|
|
ops->set_osc_powerdown = tfa9912_set_osc_powerdown;
|
|
ops->update_lpm = tfa9912_update_lpm;
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9872 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9872_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
uint16_t MANAOOSC = 0x0140; /* version 17 */
|
|
unsigned short value, xor;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* Unlock key 1 and 2 */
|
|
error = reg_write(tfa, 0x0F, 0x5A6B);
|
|
error = reg_read(tfa, 0xFB, &value);
|
|
xor = value ^ 0x005A;
|
|
error = reg_write(tfa, 0xA0, xor);
|
|
tfa98xx_key2(tfa, 0);
|
|
|
|
switch (tfa->rev) {
|
|
case 0x1a72:
|
|
case 0x2a72:
|
|
/* ----- generated code start ----- */
|
|
/* ----- version 26 ----- */
|
|
reg_write(tfa, 0x00, 0x1801); /* POR=0x0001 */
|
|
reg_write(tfa, 0x02, 0x2dc8); /* POR=0x2028 */
|
|
reg_write(tfa, 0x20, 0x0890); /* POR=0x2890 */
|
|
reg_write(tfa, 0x22, 0x043c); /* POR=0x045c */
|
|
reg_write(tfa, 0x51, 0x0000); /* POR=0x0080 */
|
|
reg_write(tfa, 0x52, 0x1a1c); /* POR=0x7ae8 */
|
|
reg_write(tfa, 0x58, 0x161c); /* POR=0x101c */
|
|
reg_write(tfa, 0x61, 0x0198); /* POR=0x0000 */
|
|
reg_write(tfa, 0x65, 0x0a8b); /* POR=0x0a9a */
|
|
reg_write(tfa, 0x70, 0x07f5); /* POR=0x06e6 */
|
|
reg_write(tfa, 0x74, 0xcc84); /* POR=0xd823 */
|
|
reg_write(tfa, 0x82, 0x01ed); /* POR=0x000d */
|
|
reg_write(tfa, 0x83, 0x0014); /* POR=0x0013 */
|
|
reg_write(tfa, 0x84, 0x0021); /* POR=0x0020 */
|
|
reg_write(tfa, 0x85, 0x0001); /* POR=0x0003 */
|
|
/* ----- generated code end ----- */
|
|
break;
|
|
case 0x1b72:
|
|
case 0x2b72:
|
|
case 0x3b72:
|
|
/* ----- generated code start ----- */
|
|
/* ----- version 25.00 ----- */
|
|
reg_write(tfa, 0x02, 0x2dc8); /* POR=0x2828 */
|
|
reg_write(tfa, 0x20, 0x0890); /* POR=0x2890 */
|
|
reg_write(tfa, 0x22, 0x043c); /* POR=0x045c */
|
|
reg_write(tfa, 0x23, 0x0001); /* POR=0x0003 */
|
|
reg_write(tfa, 0x51, 0x0000); /* POR=0x0080 */
|
|
reg_write(tfa, 0x52, 0x5a1c); /* POR=0x7a08 */
|
|
reg_write(tfa, 0x61, 0x0198); /* POR=0x0000 */
|
|
reg_write(tfa, 0x63, 0x0a9a); /* POR=0x0a93 */
|
|
reg_write(tfa, 0x65, 0x0a82); /* POR=0x0a8d */
|
|
reg_write(tfa, 0x6f, 0x01e3); /* POR=0x02e4 */
|
|
reg_write(tfa, 0x70, 0x06fd); /* POR=0x06e6 */
|
|
reg_write(tfa, 0x71, 0x307e); /* POR=0x207e */
|
|
reg_write(tfa, 0x74, 0xcc84); /* POR=0xd913 */
|
|
reg_write(tfa, 0x75, 0x1132); /* POR=0x118a */
|
|
reg_write(tfa, 0x82, 0x01ed); /* POR=0x000d */
|
|
reg_write(tfa, 0x83, 0x001a); /* POR=0x0013 */
|
|
/* ----- generated code end ----- */
|
|
break;
|
|
default:
|
|
pr_info("\nWarning: Optimal settings not found for device with revid = 0x%x\n",
|
|
tfa->rev);
|
|
break;
|
|
}
|
|
|
|
/* Turn off the osc1m to save power: PLMA4928 */
|
|
error = tfa_set_bf(tfa, MANAOOSC, 1);
|
|
|
|
/* PLMA5258 */
|
|
/* Bypass OVP by setting bit 3 from register 0xB0 (bypass_ovp=1) */
|
|
error = reg_read(tfa, 0xB0, &value);
|
|
value |= 1 << 3;
|
|
error = reg_write(tfa, 0xB0, value);
|
|
|
|
return error;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9872_auto_copy_mtp_to_iic(struct tfa_device *tfa)
|
|
{
|
|
/* Set auto_copy_mtp_to_iic (bit 5 of A3) */
|
|
/* to 1. Workaround for 72 and 88 (see PLMA5290) */
|
|
return reg_write(tfa, 0xA3, 0x20);
|
|
}
|
|
|
|
static int tfa9872_set_swprofile(struct tfa_device *tfa,
|
|
unsigned short new_value)
|
|
{
|
|
int active_value = tfa_dev_get_swprof(tfa);
|
|
|
|
/* Set the new value in the struct */
|
|
tfa->profile = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
tfa_set_bf_volatile(tfa, TFA9872_BF_SWPROFIL, new_value);
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfa9872_get_swprofile(struct tfa_device *tfa)
|
|
{
|
|
return tfa_get_bf(tfa, TFA9872_BF_SWPROFIL) - 1;
|
|
}
|
|
|
|
static int tfa9872_set_swvstep(struct tfa_device *tfa,
|
|
unsigned short new_value)
|
|
{
|
|
|
|
/* Set the new value in the struct */
|
|
tfa->vstep = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
tfa_set_bf_volatile(tfa, TFA9872_BF_SWVSTEP, new_value);
|
|
|
|
return new_value;
|
|
}
|
|
|
|
static int tfa9872_get_swvstep(struct tfa_device *tfa)
|
|
{
|
|
return tfa_get_bf(tfa, TFA9872_BF_SWVSTEP) - 1;
|
|
}
|
|
|
|
void tfa9872_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9872_specific;
|
|
ops->auto_copy_mtp_to_iic = tfa9872_auto_copy_mtp_to_iic;
|
|
ops->set_swprof = tfa9872_set_swprofile;
|
|
ops->get_swprof = tfa9872_get_swprofile;
|
|
ops->set_swvstep = tfa9872_set_swvstep;
|
|
ops->get_swvstep = tfa9872_get_swvstep;
|
|
ops->set_mute = tfa_set_mute_nodsp;
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9874 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9874_faim_protect(struct tfa_device *tfa,
|
|
int status)
|
|
{
|
|
enum tfa98xx_error ret = TFA98XX_ERROR_OK;
|
|
|
|
/* 0b = FAIM protection enabled 1b = FAIM protection disabled*/
|
|
ret = tfa_set_bf_volatile(tfa, TFA9874_BF_OPENMTP, (uint16_t)(status));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9874_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short value, xor;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* Unlock key 1 and 2 */
|
|
error = reg_write(tfa, 0x0F, 0x5A6B);
|
|
error = reg_read(tfa, 0xFB, &value);
|
|
xor = value ^ 0x005A;
|
|
error = reg_write(tfa, 0xA0, xor);
|
|
tfa98xx_key2(tfa, 0);
|
|
|
|
switch (tfa->rev) {
|
|
case 0x0a74: /* Initial revision ID */
|
|
/* ----- generated code start ----- */
|
|
/* V25 */
|
|
reg_write(tfa, 0x02, 0x22a8); /* POR=0x25c8 */
|
|
reg_write(tfa, 0x51, 0x0020); /* POR=0x0000 */
|
|
reg_write(tfa, 0x52, 0x57dc); /* POR=0x56dc */
|
|
reg_write(tfa, 0x58, 0x16a4); /* POR=0x1614 */
|
|
reg_write(tfa, 0x61, 0x0110); /* POR=0x0198 */
|
|
reg_write(tfa, 0x66, 0x0701); /* POR=0x0700 */
|
|
reg_write(tfa, 0x6f, 0x00a3); /* POR=0x01a3 */
|
|
reg_write(tfa, 0x70, 0x07f8); /* POR=0x06f8 */
|
|
reg_write(tfa, 0x73, 0x0007); /* POR=0x0005 */
|
|
reg_write(tfa, 0x74, 0x5068); /* POR=0xcc80 */
|
|
reg_write(tfa, 0x75, 0x0d28); /* POR=0x1138 */
|
|
reg_write(tfa, 0x83, 0x0594); /* POR=0x061a */
|
|
reg_write(tfa, 0x84, 0x0001); /* POR=0x0021 */
|
|
reg_write(tfa, 0x85, 0x0001); /* POR=0x0003 */
|
|
reg_write(tfa, 0x88, 0x0000); /* POR=0x0002 */
|
|
reg_write(tfa, 0xc4, 0x2001); /* POR=0x0001 */
|
|
/* ----- generated code end ----- */
|
|
break;
|
|
case 0x0b74:
|
|
/* ----- generated code start ----- */
|
|
/* V1.6 */
|
|
reg_write(tfa, 0x02, 0x22a8); /* POR=0x25c8 */
|
|
reg_write(tfa, 0x51, 0x0020); /* POR=0x0000 */
|
|
reg_write(tfa, 0x52, 0x57dc); /* POR=0x56dc */
|
|
reg_write(tfa, 0x58, 0x16a4); /* POR=0x1614 */
|
|
reg_write(tfa, 0x61, 0x0110); /* POR=0x0198 */
|
|
reg_write(tfa, 0x66, 0x0701); /* POR=0x0700 */
|
|
reg_write(tfa, 0x6f, 0x00a3); /* POR=0x01a3 */
|
|
reg_write(tfa, 0x70, 0x07f8); /* POR=0x06f8 */
|
|
reg_write(tfa, 0x73, 0x0047); /* POR=0x0045 */
|
|
reg_write(tfa, 0x74, 0x5068); /* POR=0xcc80 */
|
|
reg_write(tfa, 0x75, 0x0d28); /* POR=0x1138 */
|
|
reg_write(tfa, 0x83, 0x0595); /* POR=0x061a */
|
|
reg_write(tfa, 0x84, 0x0001); /* POR=0x0021 */
|
|
reg_write(tfa, 0x85, 0x0001); /* POR=0x0003 */
|
|
reg_write(tfa, 0x88, 0x0000); /* POR=0x0002 */
|
|
reg_write(tfa, 0xc4, 0x2001); /* POR=0x0001 */
|
|
/* ----- generated code end ----- */
|
|
break;
|
|
case 0x0c74:
|
|
/* ----- generated code start ----- */
|
|
/* V1.16 */
|
|
reg_write(tfa, 0x02, 0x22c8); /* POR=0x25c8 */
|
|
reg_write(tfa, 0x52, 0x57dc); /* POR=0x56dc */
|
|
reg_write(tfa, 0x53, 0x003e); /* POR=0x001e */
|
|
reg_write(tfa, 0x56, 0x0400); /* POR=0x0600 */
|
|
reg_write(tfa, 0x61, 0x0110); /* POR=0x0198 */
|
|
reg_write(tfa, 0x6f, 0x00a5); /* POR=0x01a3 */
|
|
reg_write(tfa, 0x70, 0x07f8); /* POR=0x06f8 */
|
|
reg_write(tfa, 0x73, 0x0047); /* POR=0x0045 */
|
|
reg_write(tfa, 0x74, 0x5098); /* POR=0xcc80 */
|
|
reg_write(tfa, 0x75, 0x8d28); /* POR=0x1138 */
|
|
reg_write(tfa, 0x80, 0x0000); /* POR=0x0003 */
|
|
reg_write(tfa, 0x83, 0x0799); /* POR=0x061a */
|
|
reg_write(tfa, 0x84, 0x0081); /* POR=0x0021 */
|
|
/* ----- generated code end ----- */
|
|
break;
|
|
default:
|
|
pr_info("\nWarning: Optimal settings not found for device with revid = 0x%x\n",
|
|
tfa->rev);
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static int tfa9874_set_swprofile(struct tfa_device *tfa,
|
|
unsigned short new_value)
|
|
{
|
|
int active_value = tfa_dev_get_swprof(tfa);
|
|
|
|
/* Set the new value in the struct */
|
|
tfa->profile = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
tfa_set_bf_volatile(tfa, TFA9874_BF_SWPROFIL, new_value);
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfa9874_get_swprofile(struct tfa_device *tfa)
|
|
{
|
|
return tfa_get_bf(tfa, TFA9874_BF_SWPROFIL) - 1;
|
|
}
|
|
|
|
static int tfa9874_set_swvstep(struct tfa_device *tfa,
|
|
unsigned short new_value)
|
|
{
|
|
/* Set the new value in the struct */
|
|
tfa->vstep = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
tfa_set_bf_volatile(tfa, TFA9874_BF_SWVSTEP, new_value);
|
|
|
|
return new_value;
|
|
}
|
|
|
|
static int tfa9874_get_swvstep(struct tfa_device *tfa)
|
|
{
|
|
return tfa_get_bf(tfa, TFA9874_BF_SWVSTEP) - 1;
|
|
}
|
|
|
|
/* tfa98xx_dsp_system_stable
|
|
* return: *ready = 1 when clocks are stable to allow DSP subsystem access
|
|
*/
|
|
static enum tfa98xx_error tfa9874_dsp_system_stable(struct tfa_device *tfa,
|
|
int *ready)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
|
|
/* check CLKS: ready if set */
|
|
*ready = tfa_get_bf(tfa, TFA9874_BF_CLKS) == 1;
|
|
|
|
return error;
|
|
}
|
|
|
|
static int tfa9874_get_mtpb(struct tfa_device *tfa)
|
|
{
|
|
int value;
|
|
|
|
value = tfa_get_bf(tfa, TFA9874_BF_MTPB);
|
|
|
|
return value;
|
|
}
|
|
|
|
void tfa9874_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9874_specific;
|
|
ops->set_swprof = tfa9874_set_swprofile;
|
|
ops->get_swprof = tfa9874_get_swprofile;
|
|
ops->set_swvstep = tfa9874_set_swvstep;
|
|
ops->get_swvstep = tfa9874_get_swvstep;
|
|
ops->dsp_system_stable = tfa9874_dsp_system_stable;
|
|
ops->faim_protect = tfa9874_faim_protect;
|
|
ops->get_mtpb = tfa9874_get_mtpb;
|
|
ops->set_mute = tfa_set_mute_nodsp;
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9878 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9878_faim_protect(struct tfa_device *tfa,
|
|
int status)
|
|
{
|
|
enum tfa98xx_error ret = TFA98XX_ERROR_OK;
|
|
|
|
/* 0b = FAIM protection enabled 1b = FAIM protection disabled*/
|
|
ret = tfa_set_bf_volatile(tfa, TFA9878_BF_OPENMTP, (uint16_t)(status));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9878_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short value, xor;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* Unlock key 1 and 2 */
|
|
error = reg_write(tfa, 0x0F, 0x5A6B);
|
|
error = reg_read(tfa, 0xFB, &value);
|
|
xor = value ^ 0x005A;
|
|
error = reg_write(tfa, 0xA0, xor);
|
|
tfa98xx_key2(tfa, 0);
|
|
|
|
switch (tfa->rev) {
|
|
case 0x0a78: /* Initial revision ID */
|
|
/* ----- generated code start ----- */
|
|
/* ----- version 28 ----- */
|
|
reg_write(tfa, 0x01, 0x2e18); /* POR=0x2e88 */
|
|
reg_write(tfa, 0x02, 0x0628); /* POR=0x0008 */
|
|
reg_write(tfa, 0x04, 0x0240); /* POR=0x0340 */
|
|
reg_write(tfa, 0x52, 0x587c); /* POR=0x57dc */
|
|
reg_write(tfa, 0x61, 0x0183); /* POR=0x0a82 */
|
|
reg_write(tfa, 0x63, 0x055a); /* POR=0x0a9a */
|
|
reg_write(tfa, 0x65, 0x0542); /* POR=0x0a82 */
|
|
reg_write(tfa, 0x71, 0x303e); /* POR=0x307e */
|
|
reg_write(tfa, 0x83, 0x009a); /* POR=0x0799 */
|
|
/* ----- generated code end ----- */
|
|
break;
|
|
|
|
case 0x1a78: /* Initial revision ID */
|
|
/* ----- generated code start ----- */
|
|
/* ----- version 12 ----- */
|
|
reg_write(tfa, 0x01, 0x2e18); /* POR=0x2e88 */
|
|
reg_write(tfa, 0x02, 0x0628); /* POR=0x0008 */
|
|
reg_write(tfa, 0x04, 0x0241); /* POR=0x0340 */
|
|
reg_write(tfa, 0x52, 0x587c); /* POR=0x57dc */
|
|
reg_write(tfa, 0x61, 0x0183); /* POR=0x0a82 */
|
|
reg_write(tfa, 0x63, 0x055a); /* POR=0x0a9a */
|
|
reg_write(tfa, 0x65, 0x0542); /* POR=0x0a82 */
|
|
reg_write(tfa, 0x70, 0xb7ff); /* POR=0x37ff */
|
|
reg_write(tfa, 0x71, 0x303e); /* POR=0x307e */
|
|
reg_write(tfa, 0x83, 0x009a); /* POR=0x0799 */
|
|
reg_write(tfa, 0x84, 0x0211); /* POR=0x0011 */
|
|
reg_write(tfa, 0x8c, 0x0210); /* POR=0x0010 */
|
|
reg_write(tfa, 0xce, 0x2202); /* POR=0xa202 */
|
|
reg_write(tfa, 0xd5, 0x0000); /* POR=0x0100 */
|
|
/* ----- generated code end ----- */
|
|
break;
|
|
|
|
default:
|
|
pr_info("\nWarning: Optimal settings not found for device with revid = 0x%x\n",
|
|
tfa->rev);
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static int tfa9878_set_swprofile(struct tfa_device *tfa,
|
|
unsigned short new_value)
|
|
{
|
|
int active_value = tfa_dev_get_swprof(tfa);
|
|
|
|
/* Set the new value in the struct */
|
|
tfa->profile = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
tfa_set_bf_volatile(tfa, TFA9878_BF_SWPROFIL, new_value);
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfa9878_get_swprofile(struct tfa_device *tfa)
|
|
{
|
|
return tfa_get_bf(tfa, TFA9878_BF_SWPROFIL) - 1;
|
|
}
|
|
|
|
static int tfa9878_set_swvstep(struct tfa_device *tfa,
|
|
unsigned short new_value)
|
|
{
|
|
/* Set the new value in the struct */
|
|
tfa->vstep = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
tfa_set_bf_volatile(tfa, TFA9878_BF_SWVSTEP, new_value);
|
|
|
|
return new_value;
|
|
}
|
|
|
|
static int tfa9878_get_swvstep(struct tfa_device *tfa)
|
|
{
|
|
return tfa_get_bf(tfa, TFA9878_BF_SWVSTEP) - 1;
|
|
}
|
|
|
|
/* tfa98xx_dsp_system_stable
|
|
* return: *ready = 1 when clocks are stable to allow DSP subsystem access
|
|
*/
|
|
static enum tfa98xx_error tfa9878_dsp_system_stable(struct tfa_device *tfa,
|
|
int *ready)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
|
|
/* check CLKS: ready if set */
|
|
*ready = tfa_get_bf(tfa, TFA9878_BF_CLKS) == 1;
|
|
|
|
return error;
|
|
}
|
|
|
|
static int tfa9878_get_mtpb(struct tfa_device *tfa)
|
|
{
|
|
int value;
|
|
|
|
value = tfa_get_bf(tfa, TFA9878_BF_MTPB);
|
|
|
|
return value;
|
|
}
|
|
|
|
void tfa9878_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9878_specific;
|
|
ops->set_swprof = tfa9878_set_swprofile;
|
|
ops->get_swprof = tfa9878_get_swprofile;
|
|
ops->set_swvstep = tfa9878_set_swvstep;
|
|
ops->get_swvstep = tfa9878_get_swvstep;
|
|
ops->dsp_system_stable = tfa9878_dsp_system_stable;
|
|
ops->faim_protect = tfa9878_faim_protect;
|
|
ops->get_mtpb = tfa9878_get_mtpb;
|
|
ops->set_mute = tfa_set_mute_nodsp;
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9888 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9888_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short value, xor;
|
|
int patch_version;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* Unlock keys to write settings */
|
|
error = reg_write(tfa, 0x0F, 0x5A6B);
|
|
error = reg_read(tfa, 0xFB, &value);
|
|
xor = value ^ 0x005A;
|
|
error = reg_write(tfa, 0xA0, xor);
|
|
|
|
/* Only N1C2 is supported */
|
|
/* ----- generated code start ----- */
|
|
/* --------- Version v1 ---------- */
|
|
if (tfa->rev == 0x2c88) {
|
|
reg_write(tfa, 0x00, 0x164d); /* POR=0x064d */
|
|
reg_write(tfa, 0x01, 0x828b); /* POR=0x92cb */
|
|
reg_write(tfa, 0x02, 0x1dc8); /* POR=0x1828 */
|
|
reg_write(tfa, 0x0e, 0x0080); /* POR=0x0000 */
|
|
reg_write(tfa, 0x20, 0x089e); /* POR=0x0890 */
|
|
reg_write(tfa, 0x22, 0x543c); /* POR=0x545c */
|
|
reg_write(tfa, 0x23, 0x0006); /* POR=0x0000 */
|
|
reg_write(tfa, 0x24, 0x0014); /* POR=0x0000 */
|
|
reg_write(tfa, 0x25, 0x000a); /* POR=0x0000 */
|
|
reg_write(tfa, 0x26, 0x0100); /* POR=0x0000 */
|
|
reg_write(tfa, 0x28, 0x1000); /* POR=0x0000 */
|
|
reg_write(tfa, 0x51, 0x0000); /* POR=0x00c0 */
|
|
reg_write(tfa, 0x52, 0xfafe); /* POR=0xbaf6 */
|
|
reg_write(tfa, 0x70, 0x3ee4); /* POR=0x3ee6 */
|
|
reg_write(tfa, 0x71, 0x1074); /* POR=0x3074 */
|
|
reg_write(tfa, 0x83, 0x0014); /* POR=0x0013 */
|
|
/* ----- generated code end ----- */
|
|
} else {
|
|
pr_info("Warning: Optimal settings not found for device with revid = 0x%x\n",
|
|
tfa->rev);
|
|
}
|
|
|
|
patch_version = tfa_cnt_get_patch_version(tfa);
|
|
if (patch_version >= 0x060401)
|
|
tfa->partial_enable = 1;
|
|
|
|
return error;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9888_tfa_dsp_write_tables(struct tfa_device *tfa,
|
|
int sample_rate)
|
|
{
|
|
unsigned char buffer[15] = {0};
|
|
int size = 15 * sizeof(char);
|
|
|
|
/* Write fractional delay to register 'cs_frac_delay' */
|
|
switch (sample_rate) {
|
|
case 0: /* 8kHz */
|
|
TFA_SET_BF(tfa, FRACTDEL, 40);
|
|
break;
|
|
case 1: /* 11.025KHz */
|
|
TFA_SET_BF(tfa, FRACTDEL, 38);
|
|
break;
|
|
case 2: /* 12kHz */
|
|
TFA_SET_BF(tfa, FRACTDEL, 37);
|
|
break;
|
|
case 3: /* 16kHz */
|
|
TFA_SET_BF(tfa, FRACTDEL, 59);
|
|
break;
|
|
case 4: /* 22.05KHz */
|
|
TFA_SET_BF(tfa, FRACTDEL, 56);
|
|
break;
|
|
case 5: /* 24kHz */
|
|
TFA_SET_BF(tfa, FRACTDEL, 56);
|
|
break;
|
|
case 6: /* 32kHz */
|
|
TFA_SET_BF(tfa, FRACTDEL, 52);
|
|
break;
|
|
case 7: /* 44.1kHz */
|
|
TFA_SET_BF(tfa, FRACTDEL, 48);
|
|
break;
|
|
case 8:
|
|
default:/* 48kHz */
|
|
TFA_SET_BF(tfa, FRACTDEL, 46);
|
|
break;
|
|
}
|
|
|
|
/* First copy the msg_id to the buffer */
|
|
buffer[0] = (uint8_t)0;
|
|
buffer[1] = (uint8_t)MODULE_FRAMEWORK + 128;
|
|
buffer[2] = (uint8_t)FW_PAR_ID_SET_SENSES_DELAY;
|
|
|
|
/* Required for all FS exept 8kHz (8kHz is all zero) */
|
|
if (sample_rate != 0) {
|
|
buffer[5] = 1; /* Vdelay_P */
|
|
buffer[8] = 0; /* Idelay_P */
|
|
buffer[11] = 1; /* Vdelay_S */
|
|
buffer[14] = 0; /* Idelay_S */
|
|
}
|
|
|
|
/* send SetSensesDelay msg */
|
|
return dsp_msg(tfa, size, (char *)buffer);
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9888_auto_copy_mtp_to_iic(struct tfa_device *tfa)
|
|
{
|
|
/* Set auto_copy_mtp_to_iic (bit 5 of A3) */
|
|
/* to 1. Workaround for 72 and 88 (see PLMA5290) */
|
|
return reg_write(tfa, 0xA3, 0x20);
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9888_factory_trimmer(struct tfa_device *tfa)
|
|
{
|
|
unsigned short current_value, delta;
|
|
int result;
|
|
|
|
/* Factory trimming for the Boost converter */
|
|
/* check if there is a correction needed */
|
|
result = TFA_GET_BF(tfa, DCMCCAPI);
|
|
if (result) {
|
|
/* Get currentvalue of DCMCC and the Delta value */
|
|
current_value = (unsigned short)TFA_GET_BF(tfa, DCMCC);
|
|
delta = (unsigned short)TFA_GET_BF(tfa, USERDEF);
|
|
|
|
/* check the sign bit (+/-) */
|
|
result = TFA_GET_BF(tfa, DCMCCSB);
|
|
if (result == 0) {
|
|
/* Do not exceed the maximum value of 15 */
|
|
if (current_value + delta < 15) {
|
|
TFA_SET_BF_VOLATILE(tfa, DCMCC,
|
|
current_value + delta);
|
|
if (tfa->verbose)
|
|
pr_debug("Max coil current is set to: %d\n",
|
|
current_value + delta);
|
|
} else {
|
|
TFA_SET_BF_VOLATILE(tfa, DCMCC, 15);
|
|
if (tfa->verbose)
|
|
pr_debug("Max coil current is set to: 15\n");
|
|
}
|
|
} else if (result == 1) {
|
|
/* Do not exceed the minimum value of 0 */
|
|
if (current_value - delta > 0) {
|
|
TFA_SET_BF_VOLATILE(tfa,
|
|
DCMCC, current_value - delta);
|
|
if (tfa->verbose)
|
|
pr_debug("Max coil current is set to: %d\n",
|
|
current_value - delta);
|
|
} else {
|
|
TFA_SET_BF_VOLATILE(tfa, DCMCC, 0);
|
|
if (tfa->verbose)
|
|
pr_debug("Max coil current is set to: 0\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static enum tfa98xx_error
|
|
tfa9888_set_mute(struct tfa_device *tfa, int mute)
|
|
{
|
|
TFA_SET_BF(tfa, CFSMR, (const uint16_t)mute);
|
|
TFA_SET_BF(tfa, CFSML, (const uint16_t)mute);
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
void tfa9888_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9888_specific;
|
|
ops->dsp_write_tables = tfa9888_tfa_dsp_write_tables;
|
|
ops->auto_copy_mtp_to_iic = tfa9888_auto_copy_mtp_to_iic;
|
|
ops->factory_trimmer = tfa9888_factory_trimmer;
|
|
ops->set_mute = tfa9888_set_mute;
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9896 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9896_faim_protect
|
|
(struct tfa_device *tfa, int status)
|
|
{
|
|
enum tfa98xx_error ret = TFA98XX_ERROR_OK;
|
|
|
|
if ((tfa->rev == 0x2b96) || (tfa->rev == 0x3b96))
|
|
ret = tfa_set_bf_volatile(tfa,
|
|
TFA9896_BF_OPENMTP, (uint16_t)status);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9896_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short check_value;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* all i2C registers must already set to default POR value */
|
|
|
|
/* $48:[3] - 1 ==> 0; iddqtestbst - default value changed.
|
|
* When Iddqtestbst is set to "0", the slewrate is reduced.
|
|
* This will lower overshoot on IN-B to avoid NMOS damage of booster.
|
|
*/
|
|
if (tfa->rev == 0x1b96) {
|
|
/* ----- generated code start v17 ----- */
|
|
reg_write(tfa, 0x06, 0x000b); /* POR=0x0001 */
|
|
reg_write(tfa, 0x07, 0x3e7f); /* POR=0x1e7f */
|
|
reg_write(tfa, 0x0a, 0x0d8a); /* POR=0x0592 */
|
|
reg_write(tfa, 0x48, 0x0300); /* POR=0x0308 */
|
|
reg_write(tfa, 0x88, 0x0100); /* POR=0x0000 */
|
|
/* ----- generated code end ----- */
|
|
} else if (tfa->rev == 0x2b96) {
|
|
/* ----- generated code start ----- v1*/
|
|
reg_write(tfa, 0x06, 0x000b); /* POR=0x0001 */
|
|
reg_write(tfa, 0x07, 0x3e7f); /* POR=0x1e7f */
|
|
reg_write(tfa, 0x0a, 0x0d8a); /* POR=0x0592 */
|
|
reg_write(tfa, 0x48, 0x0300); /* POR=0x0308 */
|
|
reg_write(tfa, 0x88, 0x0100); /* POR=0x0000 */
|
|
/* ----- generated code end ----- */
|
|
} else if (tfa->rev == 0x3b96) {
|
|
/* ----- generated code start ----- v1*/
|
|
reg_write(tfa, 0x06, 0x000b); /* POR=0x0001 */
|
|
reg_write(tfa, 0x07, 0x3e7f); /* POR=0x1e7f */
|
|
reg_write(tfa, 0x0a, 0x0d8a); /* POR=0x0592 */
|
|
reg_write(tfa, 0x48, 0x0300); /* POR=0x0308 */
|
|
reg_write(tfa, 0x88, 0x0100); /* POR=0x0000 */
|
|
/* ----- generated code end ----- */
|
|
}
|
|
/*
|
|
* $49:[0] - 1 ==> 0;
|
|
* CLIP - default value changed. 0 means CLIPPER on
|
|
*/
|
|
error = reg_read(tfa, 0x49, &check_value);
|
|
check_value &= ~0x1;
|
|
error = reg_write(tfa, 0x49, check_value);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* int24 values for the vsfw delay table
|
|
*/
|
|
static unsigned char tfa9896_vsfwdelay_table[] = {
|
|
0, 0, 2, /* Index 0 - Current/Volt Fractional Delay for 8KHz */
|
|
0, 0, 0, /* Index 1 - Current/Volt Fractional Delay for 11KHz */
|
|
0, 0, 0, /* Index 2 - Current/Volt Fractional Delay for 12KHz */
|
|
0, 0, 2, /* Index 3 - Current/Volt Fractional Delay for 16KHz */
|
|
0, 0, 2, /* Index 4 - Current/Volt Fractional Delay for 22KHz */
|
|
0, 0, 2, /* Index 5 - Current/Volt Fractional Delay for 24KHz */
|
|
0, 0, 2, /* Index 6 - Current/Volt Fractional Delay for 32KHz */
|
|
0, 0, 2, /* Index 7 - Current/Volt Fractional Delay for 44KHz */
|
|
0, 0, 3 /* Index 8 - Current/Volt Fractional Delay for 48KHz */
|
|
};
|
|
|
|
/*
|
|
* TODO make this tfa98xx
|
|
* Note that the former products write this table via the patch
|
|
* so moving this to the tfa98xx API requires also updating all patches
|
|
*/
|
|
static enum tfa98xx_error tfa9896_dsp_write_vsfwdelay_table
|
|
(struct tfa_device *tfa)
|
|
{
|
|
return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK,
|
|
TFA1_FW_PAR_ID_SET_CURRENT_DELAY,
|
|
sizeof(tfa9896_vsfwdelay_table), tfa9896_vsfwdelay_table);
|
|
}
|
|
|
|
/*
|
|
* The int24 values for the fracdelay table
|
|
* For now applicable only for 8 and 48 kHz
|
|
*/
|
|
static unsigned char tfa9896_cvfracdelay_table[] = {
|
|
0, 0, 51, /* Index 0 - Current/Volt Fractional Delay for 8KHz */
|
|
0, 0, 0, /* Index 1 - Current/Volt Fractional Delay for 11KHz */
|
|
0, 0, 0, /* Index 2 - Current/Volt Fractional Delay for 12KHz */
|
|
0, 0, 38, /* Index 3 - Current/Volt Fractional Delay for 16KHz */
|
|
0, 0, 34, /* Index 4 - Current/Volt Fractional Delay for 22KHz */
|
|
0, 0, 33, /* Index 5 - Current/Volt Fractional Delay for 24KHz */
|
|
0, 0, 11, /* Index 6 - Current/Volt Fractional Delay for 32KHz */
|
|
0, 0, 2, /* Index 7 - Current/Volt Fractional Delay for 44KHz */
|
|
0, 0, 62 /* Index 8 - Current/Volt Fractional Delay for 48KHz */
|
|
};
|
|
|
|
static enum tfa98xx_error tfa9896_dsp_write_cvfracdelay_table
|
|
(struct tfa_device *tfa)
|
|
{
|
|
return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK,
|
|
TFA1_FW_PAR_ID_SET_CURFRAC_DELAY,
|
|
sizeof(tfa9896_cvfracdelay_table), tfa9896_cvfracdelay_table);
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9896_tfa_dsp_write_tables
|
|
(struct tfa_device *tfa, int sample_rate)
|
|
{
|
|
enum tfa98xx_error error;
|
|
|
|
/* Not used for max1! */
|
|
(void)sample_rate;
|
|
|
|
error = tfa9896_dsp_write_vsfwdelay_table(tfa);
|
|
if (error == TFA98XX_ERROR_OK)
|
|
error = tfa9896_dsp_write_cvfracdelay_table(tfa);
|
|
|
|
return error;
|
|
}
|
|
|
|
void tfa9896_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9896_specific;
|
|
ops->dsp_write_tables = tfa9896_tfa_dsp_write_tables;
|
|
ops->faim_protect = tfa9896_faim_protect;
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9897 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9897_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short check_value;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* all i2C registers must already set to default POR value */
|
|
|
|
/*
|
|
* $48:[3] - 1 ==> 0; iddqtestbst - default value changed.
|
|
* When Iddqtestbst is set to "0", the slewrate is reduced.
|
|
* This will lower overshoot on IN-B to avoid NMOS damage of booster
|
|
*/
|
|
error = reg_write(tfa, 0x48, 0x0300); /* POR value = 0x308 */
|
|
|
|
/*
|
|
* $49:[0] - 1 ==> 0;
|
|
* CLIP - default value changed. 0 means CLIPPER on
|
|
*/
|
|
error = reg_read(tfa, 0x49, &check_value);
|
|
check_value &= ~0x1;
|
|
error = reg_write(tfa, 0x49, check_value);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* int24 values for the vsfw delay table
|
|
*/
|
|
static unsigned char tfa9897_vsfwdelay_table[] = {
|
|
0, 0, 2, /* Index 0 - Current/Volt Fractional Delay for 8KHz */
|
|
0, 0, 0, /* Index 1 - Current/Volt Fractional Delay for 11KHz */
|
|
0, 0, 0, /* Index 2 - Current/Volt Fractional Delay for 12KHz */
|
|
0, 0, 2, /* Index 3 - Current/Volt Fractional Delay for 16KHz */
|
|
0, 0, 2, /* Index 4 - Current/Volt Fractional Delay for 22KHz */
|
|
0, 0, 2, /* Index 5 - Current/Volt Fractional Delay for 24KHz */
|
|
0, 0, 2, /* Index 6 - Current/Volt Fractional Delay for 32KHz */
|
|
0, 0, 2, /* Index 7 - Current/Volt Fractional Delay for 44KHz */
|
|
0, 0, 3 /* Index 8 - Current/Volt Fractional Delay for 48KHz */
|
|
};
|
|
|
|
/*
|
|
* TODO make this tfa98xx
|
|
* Note that the former products write this table via the patch
|
|
* so moving this to the tfa98xx API requires also updating all patches
|
|
*/
|
|
static enum tfa98xx_error tfa9897_dsp_write_vsfwdelay_table
|
|
(struct tfa_device *tfa)
|
|
{
|
|
return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK,
|
|
TFA1_FW_PAR_ID_SET_CURRENT_DELAY,
|
|
sizeof(tfa9897_vsfwdelay_table), tfa9897_vsfwdelay_table);
|
|
}
|
|
|
|
/*
|
|
* int24 values for the fracdelay table
|
|
* For now applicable only for 8 and 48 kHz
|
|
*/
|
|
static unsigned char tfa9897_cvfracdelay_table[] = {
|
|
0, 0, 51, /* Index 0 - Current/Volt Fractional Delay for 8KHz */
|
|
0, 0, 0, /* Index 1 - Current/Volt Fractional Delay for 11KHz */
|
|
0, 0, 0, /* Index 2 - Current/Volt Fractional Delay for 12KHz */
|
|
0, 0, 38, /* Index 3 - Current/Volt Fractional Delay for 16KHz */
|
|
0, 0, 34, /* Index 4 - Current/Volt Fractional Delay for 22KHz */
|
|
0, 0, 33, /* Index 5 - Current/Volt Fractional Delay for 24KHz */
|
|
0, 0, 11, /* Index 6 - Current/Volt Fractional Delay for 32KHz */
|
|
0, 0, 2, /* Index 7 - Current/Volt Fractional Delay for 44KHz */
|
|
0, 0, 62 /* Index 8 - Current/Volt Fractional Delay for 48KHz */
|
|
};
|
|
|
|
static enum tfa98xx_error tfa9897_dsp_write_cvfracdelay_table
|
|
(struct tfa_device *tfa)
|
|
{
|
|
return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK,
|
|
TFA1_FW_PAR_ID_SET_CURFRAC_DELAY,
|
|
sizeof(tfa9897_cvfracdelay_table), tfa9897_cvfracdelay_table);
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9897_tfa_dsp_write_tables
|
|
(struct tfa_device *tfa, int sample_rate)
|
|
{
|
|
enum tfa98xx_error error;
|
|
|
|
/* Not used for max1! */
|
|
(void)sample_rate;
|
|
|
|
error = tfa9897_dsp_write_vsfwdelay_table(tfa);
|
|
if (error == TFA98XX_ERROR_OK)
|
|
error = tfa9897_dsp_write_cvfracdelay_table(tfa);
|
|
|
|
return error;
|
|
}
|
|
|
|
void tfa9897_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9897_specific;
|
|
ops->dsp_write_tables = tfa9897_tfa_dsp_write_tables;
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9895 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9895_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
int result;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* all i2C registers are already set to default */
|
|
|
|
result = TFA_SET_BF(tfa, AMPE, 1);
|
|
if (result < 0)
|
|
return -result;
|
|
|
|
/* some other registers must be set for optimal amplifier behaviour */
|
|
reg_write(tfa, 0x05, 0x13AB);
|
|
reg_write(tfa, 0x06, 0x001F);
|
|
/* peak voltage protection is always on, but may be written */
|
|
reg_write(tfa, 0x08, 0x3C4E);
|
|
/*TFA98XX_SYSCTRL_DCA=0*/
|
|
reg_write(tfa, 0x09, 0x024D);
|
|
reg_write(tfa, 0x41, 0x0308);
|
|
error = reg_write(tfa, 0x49, 0x0E82);
|
|
|
|
return error;
|
|
}
|
|
|
|
void tfa9895_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9895_specific;
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9891 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9891_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* ----- generated code start ----- */
|
|
/* ----- version 18.0 ----- */
|
|
reg_write(tfa, 0x09, 0x025d); /* POR=0x024d */
|
|
reg_write(tfa, 0x10, 0x0018); /* POR=0x0024 */
|
|
reg_write(tfa, 0x22, 0x0003); /* POR=0x0023 */
|
|
reg_write(tfa, 0x25, 0x0001); /* POR=0x0000 */
|
|
reg_write(tfa, 0x46, 0x0000); /* POR=0x4000 */
|
|
reg_write(tfa, 0x55, 0x3ffb); /* POR=0x7fff */
|
|
/* ----- generated code end ----- */
|
|
|
|
return error;
|
|
}
|
|
|
|
void tfa9891_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9891_specific;
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9890 */
|
|
/***********/
|
|
static enum tfa98xx_error tfa9890_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short regRead = 0;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
/* all i2C registers are already set to default for N1C2 */
|
|
|
|
/* some PLL registers must be set optimal for amplifier behaviour */
|
|
error = reg_write(tfa, 0x40, 0x5a6b);
|
|
if (error)
|
|
return error;
|
|
reg_read(tfa, 0x59, ®Read);
|
|
regRead |= 0x3;
|
|
reg_write(tfa, 0x59, regRead);
|
|
error = reg_write(tfa, 0x40, 0x0000);
|
|
error = reg_write(tfa, 0x47, 0x7BE1);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Disable clock gating
|
|
*/
|
|
static enum tfa98xx_error tfa9890_clockgating(struct tfa_device *tfa, int on)
|
|
{
|
|
enum tfa98xx_error error;
|
|
unsigned short value;
|
|
|
|
/*
|
|
* for TFA9890 temporarily disable clock gating
|
|
* when dsp reset is used
|
|
*/
|
|
error = reg_read(tfa, TFA98XX_CURRENTSENSE4, &value);
|
|
if (error)
|
|
return error;
|
|
|
|
if (error == TFA98XX_ERROR_OK) {
|
|
if (on) /* clock gating on - clear the bit */
|
|
value &= ~TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF;
|
|
else /* clock gating off - set the bit */
|
|
value |= TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF;
|
|
|
|
error = reg_write(tfa, TFA98XX_CURRENTSENSE4, value);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Tfa9890_DspReset will deal with clock gating control in order
|
|
* to reset the DSP for warm state restart
|
|
*/
|
|
static enum tfa98xx_error tfa9890_dsp_reset(struct tfa_device *tfa, int state)
|
|
{
|
|
enum tfa98xx_error error;
|
|
|
|
/*
|
|
* for TFA9890 temporarily disable clock gating
|
|
* when dsp reset is used
|
|
*/
|
|
tfa9890_clockgating(tfa, 0);
|
|
|
|
TFA_SET_BF(tfa, RST, (uint16_t)state);
|
|
|
|
/* clock gating restore */
|
|
error = tfa9890_clockgating(tfa, 1);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Tfa9890_DspSystemStable will compensate for the wrong behavior of CLKS
|
|
* to determine if the DSP subsystem is ready for patch and config loading.
|
|
*
|
|
* A MTP calibration register is checked for non-zero.
|
|
*
|
|
* Note: This only works after i2c reset as this will clear the MTP contents.
|
|
* When we are configured then the DSP communication will synchronize access.
|
|
*
|
|
*/
|
|
static enum tfa98xx_error tfa9890_dsp_system_stable
|
|
(struct tfa_device *tfa, int *ready)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short status, mtp0;
|
|
int result, tries;
|
|
|
|
/* check the contents of the STATUS register */
|
|
result = TFA_READ_REG(tfa, AREFS);
|
|
if (result < 0) {
|
|
error = -result;
|
|
goto errorExit;
|
|
}
|
|
status = (unsigned short)result;
|
|
|
|
/* if AMPS is set then we were already configured and running
|
|
* no need to check further
|
|
*/
|
|
*ready = (TFA_GET_BF_VALUE(tfa, AMPS, status) == 1);
|
|
if (*ready) /* if ready go back */
|
|
return error; /* will be TFA98XX_ERROR_OK */
|
|
|
|
/* check AREFS and CLKS: not ready if either is clear */
|
|
*ready = !((TFA_GET_BF_VALUE(tfa, AREFS, status) == 0)
|
|
|| (TFA_GET_BF_VALUE(tfa, CLKS, status) == 0));
|
|
if (!*ready) /* if not ready go back */
|
|
return error; /* will be TFA98XX_ERROR_OK */
|
|
|
|
/* check MTPB
|
|
* mtpbusy will be active when the subsys copies MTP to I2C
|
|
* 2 times retry avoids catching this short mtpbusy active period
|
|
*/
|
|
for (tries = 2; tries > 0; tries--) {
|
|
result = TFA_GET_BF(tfa, MTPB);/*TODO_MTPB*/
|
|
if (result < 0) {
|
|
error = -result;
|
|
goto errorExit;
|
|
}
|
|
status = (unsigned short)result;
|
|
|
|
/* check the contents of the STATUS register */
|
|
*ready = (result == 0);
|
|
if (*ready) /* if ready go on */
|
|
break;
|
|
}
|
|
if (tries == 0) /* ready will be 0 if retries exausted */
|
|
return TFA98XX_ERROR_OK;
|
|
|
|
/* check the contents of MTP register for non-zero,
|
|
* this indicates that the subsys is ready
|
|
*/
|
|
|
|
error = reg_read(tfa, 0x84, &mtp0); /* 0x84 needs double check */
|
|
if (error)
|
|
goto errorExit;
|
|
|
|
*ready = (mtp0 != 0); /* The MTP register written? */
|
|
|
|
return error;
|
|
|
|
errorExit:
|
|
*ready = 0;
|
|
return error;
|
|
}
|
|
|
|
void tfa9890_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9890_specific;
|
|
ops->dsp_reset = tfa9890_dsp_reset;
|
|
ops->dsp_system_stable = tfa9890_dsp_system_stable;
|
|
}
|
|
|
|
/***********/
|
|
/* TFA9894 */
|
|
/***********/
|
|
static int tfa9894_set_swprofile
|
|
(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
int active_value = tfa_dev_get_swprof(tfa);
|
|
|
|
/* Set the new value in the struct */
|
|
tfa->profile = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
#if defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
tfa_set_bf_volatile(tfa, TFA9894N2_BF_SWPROFIL, new_value);
|
|
else
|
|
tfa_set_bf_volatile(tfa, TFA9894_BF_SWPROFIL, new_value);
|
|
#else
|
|
tfa_set_bf_volatile(tfa, TFA9894_BF_SWPROFIL, new_value);
|
|
#endif
|
|
|
|
return active_value;
|
|
}
|
|
|
|
static int tfa9894_get_swprofile(struct tfa_device *tfa)
|
|
{
|
|
#if defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
return tfa_get_bf(tfa, TFA9894N2_BF_SWPROFIL) - 1;
|
|
else
|
|
return tfa_get_bf(tfa, TFA9894_BF_SWPROFIL) - 1;
|
|
#else
|
|
return tfa_get_bf(tfa, TFA9894_BF_SWPROFIL) - 1;
|
|
#endif
|
|
}
|
|
|
|
static int tfa9894_set_swvstep
|
|
(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
/* Set the new value in the struct */
|
|
tfa->vstep = new_value - 1;
|
|
|
|
/* Set the new value in the hw register */
|
|
#if defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
tfa_set_bf_volatile(tfa, TFA9894N2_BF_SWVSTEP, new_value);
|
|
else
|
|
tfa_set_bf_volatile(tfa, TFA9894_BF_SWVSTEP, new_value);
|
|
#else
|
|
tfa_set_bf_volatile(tfa, TFA9894_BF_SWVSTEP, new_value);
|
|
#endif
|
|
|
|
return new_value;
|
|
}
|
|
|
|
static int tfa9894_get_swvstep(struct tfa_device *tfa)
|
|
{
|
|
#if defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
return tfa_get_bf(tfa, TFA9894N2_BF_SWVSTEP) - 1;
|
|
else
|
|
return tfa_get_bf(tfa, TFA9894_BF_SWVSTEP) - 1;
|
|
#else
|
|
return tfa_get_bf(tfa, TFA9894_BF_SWVSTEP) - 1;
|
|
#endif
|
|
}
|
|
|
|
static int tfa9894_get_mtpb(struct tfa_device *tfa)
|
|
{
|
|
int value = 0;
|
|
|
|
/* Set the new value in the hw register */
|
|
#if defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
value = tfa_get_bf(tfa, TFA9894N2_BF_MTPB);
|
|
else
|
|
value = tfa_get_bf(tfa, TFA9894_BF_MTPB);
|
|
#else
|
|
value = tfa_get_bf(tfa, TFA9894_BF_MTPB);
|
|
#endif
|
|
|
|
return value;
|
|
}
|
|
|
|
/* Set internal oscillator into power down mode for TFA9894.
|
|
*
|
|
* This function is a worker for tfa98xx_set_osc_powerdown().
|
|
*
|
|
* @param[in] tfa device description structure
|
|
* @param[in] state new state 0 - oscillator is on, 1 oscillator is off.
|
|
*
|
|
* @return TFA98XX_ERROR_OK when successful, error otherwise.
|
|
*/
|
|
static enum tfa98xx_error tfa9894_set_osc_powerdown
|
|
(struct tfa_device *tfa, int state)
|
|
{
|
|
if (!(state == 1 || state == 0))
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
|
|
#if defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
return -tfa_set_bf(tfa,
|
|
TFA9894N2_BF_MANAOOSC, (uint16_t)state);
|
|
else
|
|
return -tfa_set_bf(tfa,
|
|
TFA9894_BF_MANAOOSC, (uint16_t)state);
|
|
#else
|
|
return -tfa_set_bf(tfa,
|
|
TFA9894_BF_MANAOOSC, (uint16_t)state);
|
|
#endif
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9894_faim_protect
|
|
(struct tfa_device *tfa, int status)
|
|
{
|
|
enum tfa98xx_error ret = TFA98XX_ERROR_OK;
|
|
|
|
/* 0b = FAIM protection enabled 1b = FAIM protection disabled*/
|
|
#if defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
ret = tfa_set_bf_volatile(tfa,
|
|
TFA9894N2_BF_OPENMTP, (uint16_t)(status));
|
|
else
|
|
ret = tfa_set_bf_volatile(tfa,
|
|
TFA9894_BF_OPENMTP, (uint16_t)(status));
|
|
#else
|
|
ret = tfa_set_bf_volatile(tfa,
|
|
TFA9894_BF_OPENMTP, (uint16_t)(status));
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9894_specific(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned short value, xor;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
#if defined(USE_TFA9894N2)
|
|
if (tfa->verbose)
|
|
if (is_94_N2_device(tfa))
|
|
pr_debug("check_correct\n");
|
|
#endif
|
|
/* Unlock keys to write settings */
|
|
error = reg_write(tfa, 0x0F, 0x5A6B);
|
|
error = reg_read(tfa, 0xFB, &value);
|
|
xor = value ^ 0x005A;
|
|
error = reg_write(tfa, 0xA0, xor);
|
|
pr_debug("Device REFID:%x\n", tfa->rev);
|
|
/* The optimal settings */
|
|
if (tfa->rev == 0x0a94) {
|
|
/* V36 */
|
|
/* ----- generated code start ----- */
|
|
reg_write(tfa, 0x00, 0xa245); /* POR=0x8245 */
|
|
reg_write(tfa, 0x02, 0x51e8); /* POR=0x55c8 */
|
|
reg_write(tfa, 0x52, 0xbe17); /* POR=0xb617 */
|
|
reg_write(tfa, 0x57, 0x0344); /* POR=0x0366 */
|
|
reg_write(tfa, 0x61, 0x0033); /* POR=0x0073 */
|
|
reg_write(tfa, 0x71, 0x00cf); /* POR=0x018d */
|
|
reg_write(tfa, 0x72, 0x34a9); /* POR=0x44e8 */
|
|
reg_write(tfa, 0x73, 0x3808); /* POR=0x3806 */
|
|
reg_write(tfa, 0x76, 0x0067); /* POR=0x0065 */
|
|
reg_write(tfa, 0x80, 0x0000); /* POR=0x0003 */
|
|
reg_write(tfa, 0x81, 0x5715); /* POR=0x561a */
|
|
reg_write(tfa, 0x82, 0x0104); /* POR=0x0044 */
|
|
/* ----- generated code end ----- */
|
|
} else if (tfa->rev == 0x1a94) {
|
|
/* V17 */
|
|
/* ----- generated code start ----- */
|
|
reg_write(tfa, 0x00, 0xa245); /* POR=0x8245 */
|
|
reg_write(tfa, 0x01, 0x15da); /* POR=0x11ca */
|
|
reg_write(tfa, 0x02, 0x5288); /* POR=0x55c8 */
|
|
reg_write(tfa, 0x52, 0xbe17); /* POR=0xb617 */
|
|
reg_write(tfa, 0x53, 0x0dbe); /* POR=0x0d9e */
|
|
reg_write(tfa, 0x56, 0x05c3); /* POR=0x07c3 */
|
|
reg_write(tfa, 0x57, 0x0344); /* POR=0x0366 */
|
|
reg_write(tfa, 0x61, 0x0032); /* POR=0x0073 */
|
|
reg_write(tfa, 0x71, 0x00cf); /* POR=0x018d */
|
|
reg_write(tfa, 0x72, 0x34a9); /* POR=0x44e8 */
|
|
reg_write(tfa, 0x73, 0x38c8); /* POR=0x3806 */
|
|
reg_write(tfa, 0x76, 0x0067); /* POR=0x0065 */
|
|
reg_write(tfa, 0x80, 0x0000); /* POR=0x0003 */
|
|
reg_write(tfa, 0x81, 0x5799); /* POR=0x561a */
|
|
reg_write(tfa, 0x82, 0x0104); /* POR=0x0044 */
|
|
/* ----- generated code end ----- */
|
|
} else if (tfa->rev == 0x2a94 || tfa->rev == 0x3a94) {
|
|
/* ----- generated code start ----- */
|
|
/* ----- version 25.00 ----- */
|
|
reg_write(tfa, 0x01, 0x15da); /* POR=0x11ca */
|
|
reg_write(tfa, 0x02, 0x51e8); /* POR=0x55c8 */
|
|
reg_write(tfa, 0x04, 0x0200); /* POR=0x0000 */
|
|
reg_write(tfa, 0x52, 0xbe17); /* POR=0xb617 */
|
|
reg_write(tfa, 0x53, 0x0dbe); /* POR=0x0d9e */
|
|
reg_write(tfa, 0x57, 0x0344); /* POR=0x0366 */
|
|
reg_write(tfa, 0x61, 0x0032); /* POR=0x0073 */
|
|
reg_write(tfa, 0x71, 0x6ecf); /* POR=0x6f8d */
|
|
reg_write(tfa, 0x72, 0xb4a9); /* POR=0x44e8 */
|
|
reg_write(tfa, 0x73, 0x38c8); /* POR=0x3806 */
|
|
reg_write(tfa, 0x76, 0x0067); /* POR=0x0065 */
|
|
reg_write(tfa, 0x80, 0x0000); /* POR=0x0003 */
|
|
reg_write(tfa, 0x81, 0x5799); /* POR=0x561a */
|
|
reg_write(tfa, 0x82, 0x0104); /* POR=0x0044 */
|
|
/* ----- generated code end ----- */
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static enum tfa98xx_error
|
|
tfa9894_set_mute(struct tfa_device *tfa, int mute)
|
|
{
|
|
#if defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
tfa_set_bf(tfa, TFA9894N2_BF_CFSM, (const uint16_t)mute);
|
|
else
|
|
tfa_set_bf(tfa, TFA9894_BF_CFSM, (const uint16_t)mute);
|
|
#else
|
|
tfa_set_bf(tfa, TFA9894_BF_CFSM, (const uint16_t)mute);
|
|
#endif
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa9894_dsp_system_stable
|
|
(struct tfa_device *tfa, int *ready)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
|
|
/* check CLKS: ready if set */
|
|
#if defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
*ready = tfa_get_bf(tfa, TFA9894N2_BF_CLKS) == 1;
|
|
else
|
|
*ready = tfa_get_bf(tfa, TFA9894_BF_CLKS) == 1;
|
|
#else
|
|
*ready = tfa_get_bf(tfa, TFA9894_BF_CLKS) == 1;
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
|
|
void tfa9894_ops(struct tfa_device_ops *ops)
|
|
{
|
|
/* Set defaults for ops */
|
|
set_ops_defaults(ops);
|
|
|
|
ops->tfa_init = tfa9894_specific;
|
|
ops->dsp_system_stable = tfa9894_dsp_system_stable;
|
|
ops->set_mute = tfa9894_set_mute;
|
|
ops->faim_protect = tfa9894_faim_protect;
|
|
ops->get_mtpb = tfa9894_get_mtpb;
|
|
ops->set_swprof = tfa9894_set_swprofile;
|
|
ops->get_swprof = tfa9894_get_swprofile;
|
|
ops->set_swvstep = tfa9894_set_swvstep;
|
|
ops->get_swvstep = tfa9894_get_swvstep;
|
|
/* ops->auto_copy_mtp_to_iic = tfa9894_auto_copy_mtp_to_iic; */
|
|
ops->set_osc_powerdown = tfa9894_set_osc_powerdown;
|
|
}
|