8244 lines
205 KiB
C
Executable file
8244 lines
205 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_device.h"
|
|
#include "inc/tfa_container.h"
|
|
#include "inc/tfa.h"
|
|
#include "inc/tfa98xx_tfafieldnames.h"
|
|
#include "inc/tfa_internal.h"
|
|
|
|
/* 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
|
|
|
|
#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
|
|
#define ACS_RESET_WAIT_NTRIES 10
|
|
|
|
/* set intervals */
|
|
#define BUSLOAD_INTERVAL 10
|
|
#define RAMPING_INTERVAL 1
|
|
|
|
#define REDUCED_REGISTER_SETTING
|
|
#define WRITE_CALIBRATION_DATA_TO_MTP
|
|
#define CHECK_CALIBRATION_DATA_RANGE
|
|
#define SET_CALIBRATION_AT_ALL_DEVICE_READY
|
|
#define WRITE_CALIBRATION_DATA_PARTLY
|
|
#define TRACE_STATUS_AT_CALIBRATION
|
|
#define DETECT_VVAL_WITH_EVENT
|
|
#undef DETECT_DAMAGE_WITH_EVENT
|
|
#undef CHECK_CALIBRATION_DONE_MANUALLY
|
|
#define RAMPING_WITH_USLEEP
|
|
|
|
/* calibration done executed */
|
|
#define TFA_MTPEX_POS TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS /**/
|
|
|
|
/*
|
|
* static variables
|
|
*/
|
|
static DEFINE_MUTEX(dev_lock);
|
|
static DEFINE_MUTEX(dsp_msg_lock);
|
|
static int dsp_cal_value[MAX_CHANNELS] = {-1, -1};
|
|
|
|
#if defined(TFA_WAIT_CAL_IN_WORKQUEUE)
|
|
static void tfa_wait_cal_work(struct work_struct *work);
|
|
#endif
|
|
#if defined(CHECK_CALIBRATION_DATA_RANGE)
|
|
static enum tfa98xx_error tfa_calibration_range_check(struct tfa_device *tfa,
|
|
unsigned int channel, int mohm);
|
|
#endif
|
|
static enum tfa98xx_error tfa_process_re25(struct tfa_device *tfa);
|
|
static enum tfa98xx_error _dsp_msg(struct tfa_device *tfa, int lastmessage);
|
|
|
|
#if defined(TFA_WAIT_CAL_IN_WORKQUEUE)
|
|
/* enqueue work to separate thread after calibration */
|
|
static void tfa_wait_cal_work(struct work_struct *work)
|
|
{
|
|
enum tfa98xx_error cal_err = TFA98XX_ERROR_OK;
|
|
struct tfa_device *tfa
|
|
= container_of(work, struct tfa_device, wait_cal_work.work);
|
|
struct tfa_device *ntfa;
|
|
int i;
|
|
|
|
pr_info("%s: enter with dev_idx %d\n", __func__, tfa->dev_idx);
|
|
|
|
cal_err = tfa_wait_cal(tfa);
|
|
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
if ((ntfa->active_handle != -1)
|
|
&& (ntfa->active_handle != i))
|
|
continue;
|
|
|
|
if (cal_err != TFA98XX_ERROR_OK
|
|
|| ntfa->spkr_damaged) {
|
|
tfa_handle_damaged_speakers(ntfa);
|
|
continue;
|
|
}
|
|
|
|
#if defined(TFA_TDMSPKG_CONTROL)
|
|
if (ntfa->spkgain != -1) {
|
|
pr_info("%s: set speaker gain 0x%x\n",
|
|
__func__, ntfa->spkgain);
|
|
TFA7x_SET_BF(ntfa, TDMSPKG,
|
|
ntfa->spkgain);
|
|
}
|
|
#endif
|
|
/* force UNMUTE after processing calibration */
|
|
pr_debug("%s: [%d] force UNMUTE after processing calibration\n",
|
|
__func__, ntfa->dev_idx);
|
|
tfa_dev_set_state(ntfa, TFA_STATE_UNMUTE, 1);
|
|
}
|
|
}
|
|
#endif /* TFA_WAIT_CAL_IN_WORKQUEUE */
|
|
|
|
void tfa_handle_damaged_speakers(struct tfa_device *tfa)
|
|
{
|
|
#if defined(TFA_REDUCE_BYPASS_GAIN)
|
|
int cur_spkg = 0;
|
|
#endif
|
|
|
|
tfa->is_configured = -1;
|
|
|
|
if ((tfa->active_handle != -1)
|
|
&& (tfa->active_handle != tfa->dev_idx))
|
|
return;
|
|
|
|
#if defined(TFA_BYPASS_AT_START_FAILURE)
|
|
if (tfa->spkr_damaged) {
|
|
pr_info("[%d] stop damaged device!\n", tfa->dev_idx);
|
|
tfa_dev_stop(tfa);
|
|
return;
|
|
}
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
if (tfa->vval_result == VVAL_FAIL) {
|
|
pr_info("[%d] stop erroneous device from V validation!\n",
|
|
tfa->dev_idx);
|
|
tfa_dev_stop(tfa);
|
|
return;
|
|
}
|
|
#endif
|
|
#if defined(TFA_REDUCE_BYPASS_GAIN)
|
|
cur_spkg = TFA7x_GET_BF(tfa, TDMSPKG);
|
|
|
|
if (cur_spkg < TFA_TDMSPKG_IN_BYPASS) {
|
|
pr_info("[%d] reduce TDMSPKG (%d to %d) in bypass!\n",
|
|
tfa->dev_idx, cur_spkg, TFA_TDMSPKG_IN_BYPASS);
|
|
|
|
/* force TDMSPKG as 7 dB */
|
|
TFA7x_SET_BF(tfa, TDMSPKG, TFA_TDMSPKG_IN_BYPASS);
|
|
|
|
/* reload for next time */
|
|
tfa->first_after_boot = 1;
|
|
}
|
|
#endif /* TFA_REDUCE_BYPASS_GAIN */
|
|
pr_info("%s: UNMUTE dev %d\n", __func__, tfa->dev_idx);
|
|
tfa_dev_set_state(tfa, TFA_STATE_UNMUTE, 1);
|
|
#endif /* TFA_BYPASS_AT_START_FAILURE */
|
|
#if defined(TFA_STOP_AT_START_FAILURE)
|
|
pr_info("[%d] stop damaged device!\n", tfa->dev_idx);
|
|
tfa_dev_stop(tfa);
|
|
#endif /* TFA_STOP_AT_START_FAILURE */
|
|
}
|
|
|
|
#if (defined(USE_TFA9891) || defined(USE_TFA9912))
|
|
/* return sign extended tap pattern */
|
|
int tfa_get_tap_pattern(struct tfa_device *tfa)
|
|
{
|
|
int value = tfa_get_bf(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;
|
|
}
|
|
#endif /* (USE_TFA9891) || (USE_TFA9912) */
|
|
|
|
/*
|
|
* interrupt bit function to clear
|
|
*/
|
|
int tfa_irq_clear(struct tfa_device *tfa, int bit)
|
|
{
|
|
unsigned char reg;
|
|
|
|
/* make bitfield enum */
|
|
if (bit == tfa->irq_all) {
|
|
/* operate on all bits */
|
|
for (reg = TFA98XX_INTERRUPT_IN_REG1;
|
|
reg < TFA98XX_INTERRUPT_IN_REG1 + 3; reg++)
|
|
reg_write(tfa, reg, 0xffff); /* all bits */
|
|
} else if (bit < tfa->irq_max) {
|
|
reg = (unsigned char)
|
|
(TFA98XX_INTERRUPT_IN_REG1 + (bit >> 4));
|
|
reg_write(tfa, reg, 1 << (bit & 0x0f));
|
|
/* only this bit */
|
|
} else {
|
|
return TFA_ERROR;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* return state of irq or -1 if illegal bit
|
|
*/
|
|
int tfa_irq_get(struct tfa_device *tfa, int bit)
|
|
{
|
|
uint16_t value;
|
|
int reg, mask;
|
|
|
|
if (bit < tfa->irq_max) {
|
|
/* only this bit */
|
|
reg = TFA98XX_INTERRUPT_OUT_REG1 + (bit >> 4);
|
|
mask = 1 << (bit & 0x0f);
|
|
reg_read(tfa, (unsigned char)reg, &value);
|
|
} else {
|
|
return TFA_ERROR;
|
|
}
|
|
|
|
return (value & mask) != 0;
|
|
}
|
|
|
|
/*
|
|
* interrupt bit function that operates on the shadow regs in the handle
|
|
*/
|
|
int tfa_irq_ena(struct tfa_device *tfa, int bit, int state)
|
|
{
|
|
uint16_t value, new_value;
|
|
int reg = 0, mask;
|
|
|
|
/* */
|
|
if (bit == tfa->irq_all) {
|
|
/* operate on all bits */
|
|
for (reg = TFA98XX_INTERRUPT_ENABLE_REG1;
|
|
reg <= TFA98XX_INTERRUPT_ENABLE_REG1
|
|
+ tfa->irq_max / 16; reg++) {
|
|
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 < tfa->irq_max) {
|
|
/* only this bit */
|
|
reg = TFA98XX_INTERRUPT_ENABLE_REG1 + (bit >> 4);
|
|
mask = 1 << (bit & 0x0f);
|
|
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) {
|
|
reg_write(tfa,
|
|
(unsigned char)reg,
|
|
new_value); /* only this bit */
|
|
tfa->interrupt_enable
|
|
[reg - TFA98XX_INTERRUPT_ENABLE_REG1]
|
|
= new_value;
|
|
}
|
|
} else {
|
|
return TFA_ERROR;
|
|
}
|
|
|
|
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
|
|
+ tfa->irq_max / 16; reg++)
|
|
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
|
|
+ tfa->irq_max / 16; reg++)
|
|
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, int bit, int state)
|
|
{
|
|
uint16_t value, new_value;
|
|
int reg = 0, mask;
|
|
|
|
if (bit == tfa->irq_all) {
|
|
/* operate on all bits */
|
|
for (reg = TFA98XX_STATUS_POLARITY_REG1;
|
|
reg <= TFA98XX_STATUS_POLARITY_REG1
|
|
+ tfa->irq_max / 16; reg++) {
|
|
reg_write(tfa,
|
|
(unsigned char)reg,
|
|
state ? 0xffff : 0); /* all bits */
|
|
}
|
|
} else if (bit < tfa->irq_max) {
|
|
/* only this bit */
|
|
reg = TFA98XX_STATUS_POLARITY_REG1 + (bit >> 4);
|
|
mask = 1 << (bit & 0x0f);
|
|
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)
|
|
reg_write(tfa, (unsigned char)reg, new_value);
|
|
/* only this bit */
|
|
} else {
|
|
return TFA_ERROR;
|
|
}
|
|
|
|
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->next_profile = -1;
|
|
tfa->vstep = -1;
|
|
/* defaults */
|
|
tfa->is_probus_device = 0;
|
|
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->spkr_damaged = 0;
|
|
tfa->support_tcoef = SUPPORT_YES;
|
|
tfa->support_drc = SUPPORT_NOT_SET;
|
|
tfa->support_saam = SUPPORT_NOT_SET;
|
|
/* respond to external DSP: -1:none, 0:no_dsp, 1:cold, 2:warm */
|
|
tfa->ext_dsp = -1;
|
|
tfa->bus = 0;
|
|
tfa->partial_enable = 0;
|
|
#if defined(TFADSP_32BITS)
|
|
tfa->convert_dsp32 = 1; /* conversion in kernel */
|
|
#else
|
|
tfa->convert_dsp32 = 0;
|
|
#endif
|
|
tfa->sync_iv_delay = 0;
|
|
|
|
tfa->temp = 0xffff;
|
|
tfa->is_cold = 1;
|
|
tfa->is_bypass = 0;
|
|
tfa->is_configured = 0;
|
|
tfa->reset_mtpex = 0;
|
|
tfa->stream_state = 0;
|
|
tfa->prev_samstream = -1; /* not in use */
|
|
tfa->first_after_boot = 1;
|
|
tfa->active_handle = -1;
|
|
tfa->active_count = -1;
|
|
tfa->ampgain = -1;
|
|
tfa->individual_msg = 0;
|
|
tfa->fw_itf_ver[0] = 0xff;
|
|
tfa->fw_lib_ver[0] = 0xff;
|
|
#if defined(TFA_BLACKBOX_LOGGING)
|
|
tfa->blackbox_enable = 1;
|
|
tfa->unset_log = 0;
|
|
memset(tfa->log_data, 0, LOG_BUFFER_SIZE * sizeof(int));
|
|
#endif
|
|
tfa->irq_all = 0;
|
|
tfa->irq_max = 0;
|
|
|
|
#if defined(TFA_WAIT_CAL_IN_WORKQUEUE)
|
|
INIT_DELAYED_WORK(&tfa->wait_cal_work, tfa_wait_cal_work);
|
|
#endif
|
|
|
|
/* TODO use the getfeatures() for retrieving the features [artf103523]
|
|
* tfa->support_drc = SUPPORT_NOT_SET;
|
|
*/
|
|
|
|
pr_info("%s: device type (0x%04x)\n", __func__, tfa->rev);
|
|
switch (tfa->rev & 0xff) {
|
|
case 0: /* tfanone : non-i2c external DSP device */
|
|
/* e.g. qc adsp */
|
|
tfa->support_drc = SUPPORT_YES;
|
|
tfa->tfa_family = 0;
|
|
tfa->spkr_count = 0;
|
|
tfa->daimap = 0;
|
|
/* register device operations via tfa hal */
|
|
tfanone_ops(&tfa->dev_ops);
|
|
tfa->bus = 1;
|
|
break;
|
|
case 0x72:
|
|
/* tfa9872 */
|
|
tfa->support_drc = SUPPORT_YES;
|
|
tfa->tfa_family = 2;
|
|
tfa->spkr_count = 1;
|
|
tfa->is_probus_device = 1;
|
|
tfa->ext_dsp = 1; /* set DSP-free by force */
|
|
tfa->daimap = TFA98XX_DAI_TDM;
|
|
#if (defined(USE_TFA9872) || defined(TFA98XX_FULL))
|
|
tfa9872_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
tfa->irq_all = tfa9872_irq_all;
|
|
tfa->irq_max = tfa9872_irq_max;
|
|
break;
|
|
case 0x74:
|
|
/* tfa9874 */
|
|
tfa->support_drc = SUPPORT_YES;
|
|
tfa->tfa_family = 2;
|
|
tfa->spkr_count = 1;
|
|
tfa->is_probus_device = 1;
|
|
tfa->ext_dsp = 1; /* set DSP-free by force */
|
|
tfa->daimap = TFA98XX_DAI_TDM;
|
|
#if (defined(USE_TFA9874) || defined(TFA98XX_FULL))
|
|
tfa9874_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
tfa->irq_all = tfa9874_irq_all;
|
|
tfa->irq_max = tfa9874_irq_max;
|
|
break;
|
|
case 0x78:
|
|
/* tfa9878 */
|
|
tfa->support_drc = SUPPORT_YES;
|
|
tfa->tfa_family = 2;
|
|
tfa->spkr_count = 1;
|
|
tfa->is_probus_device = 1;
|
|
tfa->ext_dsp = 1; /* set DSP-free by force */
|
|
tfa->advance_keys_handling = 1; /*artf65038*/
|
|
tfa->daimap = TFA98XX_DAI_TDM;
|
|
#if (defined(USE_TFA9878) || defined(TFA98XX_FULL))
|
|
tfa9878_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
tfa->irq_all = tfa9878_irq_all;
|
|
tfa->irq_max = tfa9878_irq_max;
|
|
break;
|
|
case 0x88:
|
|
/* tfa9888 */
|
|
tfa->tfa_family = 2;
|
|
tfa->spkr_count = 2;
|
|
tfa->daimap = TFA98XX_DAI_TDM;
|
|
#if (defined(USE_TFA9888) || defined(TFA98XX_FULL))
|
|
tfa9888_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
break;
|
|
case 0x97:
|
|
/* tfa9897 */
|
|
tfa->support_drc = SUPPORT_NO;
|
|
tfa->spkr_count = 1;
|
|
tfa->daimap = TFA98XX_DAI_TDM;
|
|
#if (defined(USE_TFA9897) || defined(TFA98XX_FULL))
|
|
tfa9897_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
break;
|
|
case 0x96:
|
|
/* tfa9896 */
|
|
tfa->support_drc = SUPPORT_NO;
|
|
tfa->spkr_count = 1;
|
|
tfa->daimap = TFA98XX_DAI_TDM;
|
|
#if (defined(USE_TFA9896) || defined(TFA98XX_FULL))
|
|
tfa9896_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
tfa->irq_all = tfa9896_irq_all;
|
|
tfa->irq_max = tfa9896_irq_max;
|
|
break;
|
|
case 0x92:
|
|
/* tfa9891 */
|
|
tfa->spkr_count = 1;
|
|
tfa->daimap = (TFA98XX_DAI_PDM | TFA98XX_DAI_I2S);
|
|
#if (defined(USE_TFA9891) || defined(TFA98XX_FULL))
|
|
tfa9891_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
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->support_drc = SUPPORT_NO;
|
|
tfa->support_framework = SUPPORT_NO;
|
|
#if (defined(USE_TFA9890) || defined(TFA98XX_FULL))
|
|
tfa9890_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
break;
|
|
case 0x12:
|
|
/* tfa9895 */
|
|
tfa->spkr_count = 1;
|
|
tfa->daimap = TFA98XX_DAI_I2S;
|
|
#if (defined(USE_TFA9895) || defined(TFA98XX_FULL))
|
|
tfa9895_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
break;
|
|
case 0x13:
|
|
/* tfa9912 */
|
|
tfa->tfa_family = 2;
|
|
tfa->spkr_count = 1;
|
|
tfa->daimap = TFA98XX_DAI_TDM;
|
|
#if (defined(USE_TFA9912) || defined(TFA98XX_FULL))
|
|
tfa9912_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
tfa->irq_all = tfa9912_irq_all;
|
|
tfa->irq_max = tfa9912_irq_max;
|
|
break;
|
|
case 0x94:
|
|
/* tfa9894 */
|
|
tfa->tfa_family = 2;
|
|
tfa->spkr_count = 1;
|
|
tfa->daimap = TFA98XX_DAI_TDM;
|
|
#if (defined(USE_TFA9894) || defined(TFA98XX_FULL))
|
|
tfa9894_ops(&tfa->dev_ops); /* register device operations */
|
|
#endif
|
|
tfa->irq_all = tfa9894_irq_all;
|
|
tfa->irq_max = tfa9894_irq_max;
|
|
break;
|
|
default:
|
|
pr_err("unknown device type : 0x%02x\n", tfa->rev & 0xff);
|
|
_ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if defined(TFADSP_DSP_BUFFER_POOL)
|
|
enum tfa98xx_error tfa_buffer_pool(struct tfa_device *tfa,
|
|
int index, int size, int control)
|
|
{
|
|
if (tfa->dev_idx != 0) { /* use main device only */
|
|
pr_err("%s: failed to set main device (%d)\n",
|
|
__func__, tfa->dev_idx);
|
|
return TFA_ERROR;
|
|
}
|
|
|
|
switch (control) {
|
|
case POOL_ALLOC: /* allocate */
|
|
tfa->buf_pool[index].pool =
|
|
kmalloc(size, GFP_KERNEL);
|
|
if (tfa->buf_pool[index].pool == NULL) {
|
|
tfa->buf_pool[index].size = 0;
|
|
tfa->buf_pool[index].in_use = 1;
|
|
pr_err("%s: buffer_pool[%d] - kmalloc error %d bytes\n",
|
|
__func__, index, size);
|
|
return TFA98XX_ERROR_FAIL;
|
|
}
|
|
pr_debug("%s: buffer_pool[%d] - kmalloc allocated %d bytes\n",
|
|
__func__, index, size);
|
|
tfa->buf_pool[index].size = size;
|
|
tfa->buf_pool[index].in_use = 0;
|
|
break;
|
|
|
|
case POOL_FREE: /* deallocate */
|
|
if (tfa->buf_pool[index].pool != NULL)
|
|
kfree(tfa->buf_pool[index].pool);
|
|
pr_debug("%s: buffer_pool[%d] - kfree\n",
|
|
__func__, index);
|
|
tfa->buf_pool[index].pool = NULL;
|
|
tfa->buf_pool[index].size = 0;
|
|
tfa->buf_pool[index].in_use = 0;
|
|
break;
|
|
|
|
default:
|
|
pr_err("%s: wrong control\n", __func__);
|
|
break;
|
|
}
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
int tfa98xx_buffer_pool_access(int r_index,
|
|
size_t g_size, uint8_t **buf, int control)
|
|
{
|
|
int index;
|
|
struct tfa_device *tfa
|
|
= tfa98xx_get_tfa_device_from_index(0);
|
|
|
|
if (tfa == NULL) {
|
|
pr_err("%s: failed to get main device\n",
|
|
__func__);
|
|
return TFA_ERROR;
|
|
}
|
|
if (tfa->dev_idx != 0) {
|
|
pr_err("%s: failed to get main device (%d)\n",
|
|
__func__, tfa->dev_idx);
|
|
return TFA_ERROR;
|
|
}
|
|
|
|
switch (control) {
|
|
case POOL_GET: /* get */
|
|
if (tfa->verbose)
|
|
pr_debug("%s: dev %d, request buffer_pool, size=%d\n",
|
|
__func__, tfa->dev_idx, (int)g_size);
|
|
*buf = NULL;
|
|
for (index = 0; index < POOL_MAX_INDEX; index++) {
|
|
if (tfa->buf_pool[index].in_use) {
|
|
if (tfa->verbose)
|
|
pr_debug("%s: buffer_pool[%d] is in use\n",
|
|
__func__, index);
|
|
continue;
|
|
}
|
|
if (tfa->buf_pool[index].size < (int)g_size) {
|
|
if (tfa->verbose)
|
|
pr_debug("%s: buffer_pool[%d] size %d < %d\n",
|
|
__func__, index,
|
|
tfa->buf_pool[index].size,
|
|
(int)g_size);
|
|
continue;
|
|
}
|
|
*buf = (uint8_t *)(tfa->buf_pool[index].pool);
|
|
if (*buf == NULL) {
|
|
pr_err("%s: found NULL in buffer_pool[%d]\n",
|
|
__func__, index);
|
|
continue;
|
|
}
|
|
tfa->buf_pool[index].in_use = 1;
|
|
if (tfa->verbose)
|
|
pr_debug("%s: get buffer_pool[%d]\n",
|
|
__func__, index);
|
|
return index;
|
|
}
|
|
|
|
pr_err("%s: failed to get buffer_pool\n",
|
|
__func__);
|
|
break;
|
|
|
|
case POOL_RETURN: /* return */
|
|
if (tfa->verbose)
|
|
pr_debug("%s: dev %d, release buffer_pool[%d]\n",
|
|
__func__, tfa->dev_idx, r_index);
|
|
if (r_index < 0 || r_index >= POOL_MAX_INDEX) {
|
|
pr_err("%s: out of range [%d]\n", __func__, r_index);
|
|
return TFA_ERROR;
|
|
}
|
|
if (tfa->buf_pool[r_index].in_use == 0
|
|
|| tfa->buf_pool[r_index].size == 0) {
|
|
if (tfa->verbose)
|
|
pr_debug("%s: buffer_pool[%d] is not in use\n",
|
|
__func__, r_index);
|
|
return TFA_ERROR; /* reset by force */
|
|
}
|
|
|
|
if (tfa->verbose)
|
|
pr_debug("%s: return buffer_pool[%d]\n",
|
|
__func__, r_index);
|
|
memset(tfa->buf_pool[r_index].pool,
|
|
0, tfa->buf_pool[r_index].size);
|
|
tfa->buf_pool[r_index].in_use = 0;
|
|
r_index = -1;
|
|
|
|
return r_index;
|
|
|
|
default:
|
|
pr_err("%s: wrong control\n", __func__);
|
|
break;
|
|
}
|
|
|
|
return TFA_ERROR;
|
|
}
|
|
#endif /* TFADSP_DSP_BUFFER_POOL */
|
|
|
|
/*
|
|
* lookup the device type and return the family type
|
|
*/
|
|
int tfa98xx_dev2family(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 0x13:
|
|
case 0x74:
|
|
case 0x94:
|
|
case 0x78:
|
|
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(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 ((filter_index >= 10) && (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 0x74:
|
|
case 0x13:
|
|
case 0x78:
|
|
default:
|
|
/* unsupported case, possibly intermediate version */
|
|
/* _ASSERT(0); */
|
|
return TFA98XX_DMEM_ERR;
|
|
}
|
|
}
|
|
return dmem;
|
|
}
|
|
|
|
/************************ query functions *******************************/
|
|
/* no device involved
|
|
* return revision
|
|
* Used by the LTT
|
|
*/
|
|
void tfa98xx_rev(int *major, int *minor, int *revision)
|
|
{
|
|
char version_str[] = TFA98XX_API_REV_STR;
|
|
char residual[20] = {'\0'};
|
|
int ret;
|
|
|
|
ret = sscanf(version_str, "v%d.%d.%d-%s",
|
|
major, minor, revision, residual);
|
|
if (ret != 4)
|
|
pr_err("%s: failure reading API revision", __func__);
|
|
}
|
|
|
|
/*
|
|
* 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_NOT_OPEN;
|
|
|
|
if (spkr_count)
|
|
*spkr_count = tfa->spkr_count;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
/*
|
|
* tfa98xx_supported_saam
|
|
* returns the supportedspeaker as microphone feature
|
|
*/
|
|
enum tfa98xx_error tfa98xx_supported_saam(struct tfa_device *tfa,
|
|
enum tfa98xx_saam *saam)
|
|
{
|
|
int features;
|
|
enum tfa98xx_error error;
|
|
|
|
if (tfa->support_saam == SUPPORT_NOT_SET) {
|
|
error = tfa98xx_dsp_get_hw_feature_bits(tfa, &features);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
return error;
|
|
tfa->support_saam
|
|
= (features & 0x8000) ? SUPPORT_YES : SUPPORT_NO;
|
|
/* SAAM is bit15 */
|
|
}
|
|
*saam = tfa->support_saam == SUPPORT_YES
|
|
? TFA98XX_SAAM : TFA98XX_SAAM_NONE;
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
/*
|
|
* tfa98xx_set_stream_state
|
|
* sets the stream: b0: pstream (Rx), b1: cstream (Tx), b2: samstream (SaaM)
|
|
*/
|
|
enum tfa98xx_error tfa98xx_set_stream_state(struct tfa_device *tfa,
|
|
int stream_state)
|
|
{
|
|
#if defined(REDUCED_REGISTER_SETTING)
|
|
int cur_samstream = (stream_state & BIT_SAMSTREAM) ? 1 : 0;
|
|
#endif
|
|
|
|
pr_debug("%s: set stream_state=0x%04x\n", __func__, stream_state);
|
|
|
|
tfa->stream_state = stream_state;
|
|
#if defined(REDUCED_REGISTER_SETTING)
|
|
if (tfa->prev_samstream != cur_samstream) {
|
|
pr_debug("%s: samstream toggled: %d -> %d\n",
|
|
__func__, tfa->prev_samstream, cur_samstream);
|
|
|
|
/* reload reg at toggling SAMSTREAM */
|
|
#if defined(TFA_PRELOAD_SETTING_AT_PROBING)
|
|
if ((tfa->first_after_boot != 2)
|
|
|| (tfa->prev_samstream != -1))
|
|
tfa->first_after_boot = 1;
|
|
#else
|
|
tfa->first_after_boot = 1;
|
|
#endif
|
|
|
|
tfa->prev_samstream = cur_samstream;
|
|
}
|
|
#endif /* REDUCED_REGISTER_SETTING */
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
/*
|
|
* tfa98xx_compare_features
|
|
* Obtains features_from_MTP and features_from_cnt
|
|
*/
|
|
enum tfa98xx_error tfa98xx_compare_features(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[2 * 3];
|
|
int status;
|
|
|
|
tfa98xx_dsp_system_stable(tfa, &status);
|
|
if (!status)
|
|
/* Only test when we have a clock. */
|
|
return TFA98XX_ERROR_NO_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(tfa, mtpbf) & 0xffff;
|
|
features_from_MTP[0] = tfa->hw_feature_bits = value;
|
|
|
|
/* Read SW features: */
|
|
error = tfa_dsp_cmd_id_write_read(tfa, MODULE_FRAMEWORK,
|
|
FW_PAR_ID_GET_FEATURE_INFO, sizeof(bytes), bytes);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
/* old ROM code may respond with TFA98XX_ERROR_RPC_PARAM_ID */
|
|
return error;
|
|
|
|
tfa98xx_convert_bytes2data(sizeof(bytes), bytes, &features_from_MTP[1]);
|
|
|
|
/* check if feature bits from MTP match feature bits from cnt file: */
|
|
get_hw_features_from_cnt(tfa, &features_from_cnt[0]);
|
|
get_sw_features_from_cnt(tfa, &features_from_cnt[1]);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*************************** device specific ops ***************************/
|
|
/* the wrapper for DspReset, in case of full */
|
|
enum tfa98xx_error tfa98xx_dsp_reset(struct tfa_device *tfa, int state)
|
|
{
|
|
if (!tfa->dev_ops.dsp_reset)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
return (tfa->dev_ops.dsp_reset)(tfa, state);
|
|
}
|
|
|
|
/* the ops wrapper for tfa98xx_dsp_SystemStable */
|
|
enum tfa98xx_error tfa98xx_dsp_system_stable(struct tfa_device *tfa,
|
|
int *ready)
|
|
{
|
|
if (!tfa->dev_ops.dsp_system_stable)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
return (tfa->dev_ops.dsp_system_stable)(tfa, ready);
|
|
}
|
|
|
|
/* the ops wrapper for tfa98xx_dsp_system_stable */
|
|
enum tfa98xx_error tfa98xx_auto_copy_mtp_to_iic(struct tfa_device *tfa)
|
|
{
|
|
if (!tfa->dev_ops.auto_copy_mtp_to_iic)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
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)
|
|
{
|
|
if (!tfa->dev_ops.faim_protect)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
return (tfa->dev_ops.faim_protect)(tfa, state);
|
|
}
|
|
|
|
enum tfa98xx_error tfa98xx_dsp_write_tables(struct tfa_device *tfa,
|
|
int sample_rate)
|
|
{
|
|
if (!tfa->dev_ops.dsp_write_tables)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
return (tfa->dev_ops.dsp_write_tables)(tfa, sample_rate);
|
|
}
|
|
|
|
/* 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 successful, error otherwise.
|
|
*/
|
|
enum tfa98xx_error tfa98xx_set_osc_powerdown(struct tfa_device *tfa, int state)
|
|
{
|
|
if (!tfa->dev_ops.set_osc_powerdown)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
return (tfa->dev_ops.set_osc_powerdown)(tfa, state);
|
|
}
|
|
|
|
/* 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 successful, error otherwise.
|
|
*/
|
|
enum tfa98xx_error tfa98xx_update_lpm(struct tfa_device *tfa, int state)
|
|
{
|
|
if (!tfa->dev_ops.update_lpm)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
return (tfa->dev_ops.update_lpm)(tfa, state);
|
|
}
|
|
|
|
/*
|
|
* bring the device into a state similar to reset
|
|
*/
|
|
enum tfa98xx_error tfa98xx_init(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 read in the bitfield function.
|
|
* The I2CR bit may overwrite 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);
|
|
|
|
#if defined(USE_POR_ALWAYS)
|
|
if (tfa->tfa_family == 2) {
|
|
/* restore MANSCONF and MANCOLD to POR state */
|
|
TFA_SET_BF_VOLATILE(tfa, MANSCONF, 0);
|
|
TFA_SET_BF_VOLATILE(tfa, MANCOLD, 1);
|
|
}
|
|
#endif
|
|
|
|
if (tfa->ext_dsp == -1)
|
|
/* Put DSP in reset, in pair of tfa_run_start_dsp() */
|
|
tfa98xx_dsp_reset(tfa, 1);
|
|
|
|
/* 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) {
|
|
pr_debug("%s: device specific (0x%04x)\n",
|
|
__func__, tfa->rev);
|
|
error = (tfa->dev_ops.tfa_init)(tfa);
|
|
} else {
|
|
pr_debug("%s: no init code\n", __func__);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
#if (defined(USE_TFA9892) || defined(USE_TFA9888))
|
|
/* 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) /* tfa9912 */
|
|
|| ((tfa->rev & 0xff) == 0x88)) /* tfa9888 */ {
|
|
ret = reg_read(tfa, 0xc6, &value);
|
|
if (ret != TFA98XX_ERROR_OK)
|
|
return TFA_ERROR;
|
|
/* PLMA5539: Check actual value of powerswitch. */
|
|
/* TODO: regmap v1.40 should make this bit public. */
|
|
|
|
return (int)(value & (1u << 6));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
#endif /* USE_TFA9892 || USE_TFA9888 */
|
|
|
|
/********************* new tfa2 ****************************/
|
|
/* newly added messaging for tfa2 tfa1? */
|
|
enum tfa98xx_error tfa98xx_dsp_get_memory(struct tfa_device *tfa,
|
|
int memory_type, int offset, int length, unsigned char bytes[])
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
char msg[4 * 3];
|
|
int nr = 0;
|
|
|
|
mutex_lock(&dsp_msg_lock);
|
|
|
|
msg[nr++] = 8;
|
|
msg[nr++] = MODULE_FRAMEWORK + 0x80;
|
|
msg[nr++] = FW_PAR_ID_GET_MEMORY;
|
|
|
|
msg[nr++] = 0;
|
|
msg[nr++] = 0;
|
|
msg[nr++] = (char)memory_type;
|
|
|
|
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 */
|
|
tfa->individual_msg = 1;
|
|
error = dsp_msg(tfa, nr, (char *)msg);
|
|
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
mutex_unlock(&dsp_msg_lock);
|
|
return error;
|
|
}
|
|
|
|
/* read the data from the device (length * 3 = words) */
|
|
error = dsp_msg_read(tfa, length * 3, bytes);
|
|
|
|
mutex_unlock(&dsp_msg_lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error tfa98xx_dsp_set_memory(struct tfa_device *tfa,
|
|
int memory_type, int offset, int length, int value)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
int nr = 0;
|
|
char msg[5 * 3];
|
|
|
|
mutex_lock(&dsp_msg_lock);
|
|
|
|
msg[nr++] = 8;
|
|
msg[nr++] = MODULE_FRAMEWORK + 0x80;
|
|
msg[nr++] = FW_PAR_ID_SET_MEMORY;
|
|
|
|
msg[nr++] = 0;
|
|
msg[nr++] = 0;
|
|
msg[nr++] = (char)memory_type;
|
|
|
|
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 */
|
|
tfa->individual_msg = 1;
|
|
error = dsp_msg(tfa, nr, (char *)msg);
|
|
|
|
mutex_unlock(&dsp_msg_lock);
|
|
|
|
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(struct tfa_device *tfa, uint16_t *value)
|
|
{
|
|
int status;
|
|
int result;
|
|
|
|
/* not possible if PLL in powerdown */
|
|
if (TFA_GET_BF(tfa, PWDN)) {
|
|
pr_debug("PLL in powerdown\n");
|
|
return TFA98XX_ERROR_NO_CLOCK;
|
|
}
|
|
|
|
tfa98xx_dsp_system_stable(tfa, &status);
|
|
if (status == 0) {
|
|
pr_debug("PLL not running\n");
|
|
return TFA98XX_ERROR_NO_CLOCK;
|
|
}
|
|
|
|
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(struct tfa_device *tfa, int lock)
|
|
{
|
|
/* unhide lock registers */
|
|
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 */
|
|
if (!tfa->advance_keys_handling) /*artf65038*/
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
reg_read(tfa, (unsigned char)reg_row_to_keep, &value);
|
|
if (!row) {
|
|
reg_write(tfa, 0xa7, value);
|
|
reg_write(tfa, 0xa8, reg_row_to_set);
|
|
} else {
|
|
reg_write(tfa, 0xa7, reg_row_to_set);
|
|
reg_write(tfa, 0xa8, value);
|
|
}
|
|
reg_write(tfa, 0xa3, 0x10 | row);
|
|
if (tfa->is_probus_device) {
|
|
/* Assure FAIM is enabled (enable it when neccesery) */
|
|
for (loop = 0; loop < 100 /* x 10ms */; loop++) {
|
|
/* wait 10ms to avoid busload */
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
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(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(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(tfa, &status);
|
|
if (error)
|
|
return error;
|
|
if (status == 0)
|
|
return TFA98XX_ERROR_NO_CLOCK;
|
|
|
|
tfa98xx_key2(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_STATE_TIMED_OUT;
|
|
for (loop = 0; loop < 100 /* x 10ms */; loop++) {
|
|
/* wait 10ms to avoid busload */
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
if (tfa_dev_get_mtpb(tfa) == 0) {
|
|
error = TFA98XX_ERROR_OK;
|
|
break;
|
|
}
|
|
}
|
|
tfa98xx_key2(tfa, 1); /* lock */
|
|
/* MTP setting failed due to timeout ?*/
|
|
if (error) {
|
|
tfa98xx_faim_protect(tfa, 0);
|
|
return error;
|
|
}
|
|
|
|
/* Disable the FAIM, if this is necessary */
|
|
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);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* clear mtpex
|
|
* set ACS
|
|
* start tfa
|
|
*/
|
|
int tfa_calibrate(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error;
|
|
|
|
tfa->is_cold = 1;
|
|
|
|
/* clear mtpex */
|
|
error = tfa98xx_set_mtp(tfa, 0,
|
|
TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK);
|
|
if (error) {
|
|
pr_info("resetting MTPEX failed (%d)\n", error);
|
|
tfa->reset_mtpex = 1; /* suspend until TFA98xx is active */
|
|
return error;
|
|
}
|
|
|
|
/* set ACS/coldboot state */
|
|
if (!tfa->is_probus_device) {
|
|
/* put DSP in reset state */
|
|
error = tfa98xx_dsp_reset(tfa, 1);
|
|
|
|
/* force cold boot */
|
|
error = tfa_run_coldboot(tfa, 1);
|
|
if (error)
|
|
pr_info("coldboot failed (%d)\n", error);
|
|
}
|
|
|
|
/* start tfa by playing */
|
|
return error;
|
|
}
|
|
|
|
static short twos(short x)
|
|
{
|
|
return (x < 0) ? x + 512 : x;
|
|
}
|
|
|
|
void tfa98xx_set_exttemp(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);
|
|
}
|
|
|
|
/* for FW_PAR_ID_SET_CHIP_TEMP_SELECTOR */
|
|
tfa->temp = ext_temp;
|
|
}
|
|
|
|
short tfa98xx_get_exttemp(struct tfa_device *tfa)
|
|
{
|
|
short ext_temp = (short)TFA_GET_BF(tfa, EXTTS);
|
|
|
|
pr_debug("Using ext temp %d degC (%d internal)\n",
|
|
ext_temp, tfa->temp);
|
|
|
|
return twos(ext_temp);
|
|
}
|
|
|
|
/************* tfa simple bitfield interfacing ********************/
|
|
/* convenience functions */
|
|
enum tfa98xx_error tfa98xx_set_volume_level(struct tfa_device *tfa,
|
|
unsigned short vol)
|
|
{
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
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);
|
|
}
|
|
|
|
static enum tfa98xx_error
|
|
tfa98xx_set_mute_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_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(struct tfa_device *tfa, enum tfa98xx_mute mute)
|
|
{
|
|
int cur_ampe;
|
|
|
|
if (tfa->in_use == 0) {
|
|
pr_err("device is not opened\n");
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
}
|
|
|
|
cur_ampe = TFA_GET_BF(tfa, AMPE);
|
|
if ((mute == TFA98XX_MUTE_OFF && cur_ampe == 1)
|
|
|| (mute == TFA98XX_MUTE_AMPLIFIER && cur_ampe == 0)) {
|
|
pr_info("%s: skip mute request (%d, AMPE %d)\n",
|
|
__func__, mute, cur_ampe);
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
if (tfa->tfa_family == 1)
|
|
return tfa98xx_set_mute_tfa1(tfa, mute);
|
|
else
|
|
return tfa98xx_set_mute_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(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(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(struct tfa_device *tfa, int patch_length,
|
|
const unsigned char *patch_bytes)
|
|
{
|
|
enum tfa98xx_error error;
|
|
int status;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
if (patch_length < PATCH_HEADER_LENGTH)
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
|
|
error = tfa98xx_check_ic_rom_version(tfa, patch_bytes);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
return error;
|
|
|
|
tfa98xx_dsp_system_stable(tfa, &status);
|
|
if (!status)
|
|
/* Only test when we have a clock. */
|
|
return TFA98XX_ERROR_NO_CLOCK;
|
|
/******* TO_TEST *******/
|
|
if (error == TFA98XX_ERROR_OK) {
|
|
error = tfa_run_coldboot(tfa, 1);
|
|
if (error)
|
|
return TFA98XX_ERROR_DSP_NOT_RUNNING;
|
|
}
|
|
/***********************/
|
|
error = tfa98xx_process_patch_file(tfa,
|
|
patch_length - PATCH_HEADER_LENGTH,
|
|
patch_bytes + PATCH_HEADER_LENGTH);
|
|
|
|
return error;
|
|
}
|
|
|
|
/****************** end patching ******************/
|
|
|
|
TFA_INTERNAL enum tfa98xx_error
|
|
tfa98xx_wait_result(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 {
|
|
/* i2c_cmd_ack */
|
|
cf_status = TFA_GET_BF(tfa, ACK);
|
|
if (cf_status < 0)
|
|
error = -cf_status;
|
|
tries++;
|
|
/* 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(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);
|
|
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
/* shift one data if reading contains status*/
|
|
i = 1;
|
|
k = 3;
|
|
#else
|
|
i = 0;
|
|
k = 0;
|
|
#endif /* TFA_CUSTOM_FORMAT_AT_RESPONSE */
|
|
|
|
for (; 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(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()
|
|
* 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_write(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(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_write_id(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(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(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() to retrieve command/msg status:
|
|
* return a <0 status of the DSP did not ACK.
|
|
*/
|
|
enum tfa98xx_error tfa_dsp_msg_status(struct tfa_device *tfa, int *p_rpc_status)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
|
|
error = tfa98xx_wait_result(tfa, 2); /* 2 is only one try */
|
|
if (error == TFA98XX_ERROR_DSP_NOT_RUNNING) {
|
|
*p_rpc_status = -1;
|
|
return TFA98XX_ERROR_OK;
|
|
} else if (error != TFA98XX_ERROR_OK) {
|
|
return error;
|
|
}
|
|
|
|
error = tfa98xx_check_rpc_status(tfa, p_rpc_status);
|
|
|
|
return error;
|
|
}
|
|
|
|
const char *tfa98xx_get_i2c_status_id_string(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 range [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)";
|
|
break;
|
|
case TFA98XX_I2C_REQ_INVALID_SEQ:
|
|
p_id_str =
|
|
"Invalid order of commands, contrary to expected one";
|
|
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 overflowed: host sent too many parameters";
|
|
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";
|
|
break;
|
|
}
|
|
|
|
return p_id_str;
|
|
}
|
|
|
|
static enum tfa98xx_error _dsp_msg(struct tfa_device *tfa, int lastmessage)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
uint8_t *blob = NULL;
|
|
int len;
|
|
#if defined(TFADSP_DSP_BUFFER_POOL)
|
|
int buf_p_index = -1;
|
|
#endif
|
|
|
|
#if defined(TFADSP_DSP_BUFFER_POOL)
|
|
buf_p_index = tfa98xx_buffer_pool_access
|
|
(-1, 64 * 1024, &blob, POOL_GET);
|
|
if (buf_p_index != -1) {
|
|
pr_debug("%s: allocated from buffer_pool[%d] for 64 KB\n",
|
|
__func__, buf_p_index);
|
|
} else {
|
|
/* max length is 64k */
|
|
blob = kmalloc(64 * 1024, GFP_KERNEL);
|
|
if (blob == NULL)
|
|
return TFA98XX_ERROR_FAIL;
|
|
}
|
|
#else
|
|
/* max length is 64k */
|
|
blob = kmalloc(64 * 1024, GFP_KERNEL);
|
|
#endif /* TFADSP_DSP_BUFFER_POOL */
|
|
|
|
len = tfa_tib_dsp_msgmulti(tfa, -1, (const char *)blob);
|
|
if (tfa->verbose)
|
|
pr_debug("%s: send multi-message, length=%d (update at %s)\n",
|
|
__func__, len,
|
|
lastmessage ? "the last message" : "buffer full");
|
|
|
|
/* send messages to the target selected */
|
|
if (tfa98xx_count_active_stream(BIT_PSTREAM) > 0) {
|
|
#if defined(TFADSP_DSP_MSG_PACKET_STRATEGY)
|
|
error = dsp_msg_packet(tfa, blob, len);
|
|
#else
|
|
if (tfa->has_msg == 0) { /* via i2c */
|
|
/* Send to the target selected */
|
|
if (tfa->dev_ops.dsp_msg)
|
|
error = (tfa->dev_ops.dsp_msg)
|
|
((void *)tfa, len, (const char *)blob);
|
|
} else { /* via msg hal */
|
|
error = tfa98xx_write_dsp
|
|
((void *)tfa, len, (const char *)blob);
|
|
}
|
|
#endif /* TFADSP_DSP_MSG_PACKET_STRATEGY */
|
|
} else {
|
|
pr_info("%s: skip if PSTREAM is lost\n",
|
|
__func__);
|
|
}
|
|
if (error != TFA98XX_ERROR_OK)
|
|
pr_err("%s: error in sending messages (%d)\n",
|
|
__func__, error);
|
|
|
|
#if defined(TFADSP_DSP_BUFFER_POOL)
|
|
if (buf_p_index != -1)
|
|
buf_p_index = tfa98xx_buffer_pool_access
|
|
(buf_p_index, 0, &blob, POOL_RETURN);
|
|
else
|
|
kfree(blob);
|
|
#else
|
|
kfree(blob);
|
|
#endif /* TFADSP_DSP_BUFFER_POOL */
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error dsp_msg(struct tfa_device *tfa,
|
|
int length24, const char *buf24)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
static int lastmessage;
|
|
int i;
|
|
int *intbuf = NULL;
|
|
char *buf = (char *)buf24;
|
|
int length = length24;
|
|
|
|
if (tfa98xx_count_active_stream(BIT_PSTREAM) == 0) {
|
|
pr_info("%s: skip if PSTREAM is lost\n", __func__);
|
|
tfa->individual_msg = 0;
|
|
return error;
|
|
}
|
|
|
|
if (tfa->convert_dsp32) {
|
|
int idx = 0;
|
|
|
|
length = 4 * length24 / 3;
|
|
intbuf = kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
|
|
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)
|
|
goto dsp_msg_error_exit;
|
|
|
|
/* if buffer is full we need to send the existing message
|
|
* and add the current message
|
|
*/
|
|
if (error == TFA98XX_ERROR_BUFFER_TOO_SMALL) {
|
|
/* (a) send the existing (full) message */
|
|
error = _dsp_msg(tfa, lastmessage);
|
|
|
|
/* (b) add to a new multi-message */
|
|
error = tfa_tib_dsp_msgmulti(tfa, length, buf);
|
|
if (error == TFA98XX_ERROR_FAIL)
|
|
goto dsp_msg_error_exit;
|
|
}
|
|
|
|
lastmessage = error;
|
|
|
|
/* At the last message, send the multi-msg to the target */
|
|
if (lastmessage == 1) {
|
|
/* Get the full multi-msg data */
|
|
error = _dsp_msg(tfa, lastmessage);
|
|
|
|
/* reset to re-start */
|
|
lastmessage = 0;
|
|
}
|
|
} else {
|
|
if (tfa98xx_count_active_stream(BIT_PSTREAM) > 0) {
|
|
if (tfa->has_msg == 0) { /* via i2c */
|
|
if (tfa->dev_ops.dsp_msg)
|
|
error = (tfa->dev_ops.dsp_msg)
|
|
((void *)tfa,
|
|
length,
|
|
(const char *)buf);
|
|
} else { /* via msg hal */
|
|
error = tfa98xx_write_dsp((void *)tfa,
|
|
length, (const char *)buf);
|
|
}
|
|
} else {
|
|
pr_info("%s: skip if PSTREAM is lost\n",
|
|
__func__);
|
|
}
|
|
}
|
|
|
|
if (error != TFA98XX_ERROR_OK)
|
|
/* Get actual error code from softDSP */
|
|
error = (enum tfa98xx_error)
|
|
(error + TFA98XX_ERROR_BUFFER_RPC_BASE);
|
|
|
|
/* DSP verbose has argument 0x04 */
|
|
if ((tfa->verbose & 0x04) != 0) {
|
|
pr_debug("DSP W [%d]: ", length);
|
|
for (i = 0; i < ((length > 3) ? 3 : length); i++)
|
|
pr_debug("0x%02x ", (uint8_t)buf[i]);
|
|
pr_debug("\n");
|
|
}
|
|
|
|
dsp_msg_error_exit:
|
|
tfa->individual_msg = 0;
|
|
|
|
if (tfa->convert_dsp32)
|
|
kmem_cache_free(tfa->cachep, intbuf);
|
|
|
|
return error;
|
|
}
|
|
|
|
#if defined(TFADSP_DSP_MSG_PACKET_STRATEGY)
|
|
enum tfa98xx_error dsp_msg_packet(struct tfa_device *tfa,
|
|
uint8_t *blob, int tfadsp_buf_size)
|
|
{
|
|
enum tfa98xx_error error = 0;
|
|
uint8_t *pkt_buff = NULL;
|
|
#if defined(TFADSP_DSP_BUFFER_POOL)
|
|
int pkt_buff_p_index = -1;
|
|
#endif
|
|
int loop = 0;
|
|
int remaining_blob_size, tfadsp_buf_offset;
|
|
int packet_id, packet_size;
|
|
|
|
tfadsp_buf_offset = 0;
|
|
remaining_blob_size = tfadsp_buf_size;
|
|
packet_size = MAX_PKT_MSG_SIZE - 4;
|
|
#if defined(TFADSP_DSP_BUFFER_POOL)
|
|
pkt_buff_p_index = tfa98xx_buffer_pool_access
|
|
(-1, MAX_PKT_MSG_SIZE, &pkt_buff, POOL_GET);
|
|
if (pkt_buff_p_index != -1) {
|
|
pr_debug("%s: allocated from buffer_pool[%d] - pkt_buff\n",
|
|
__func__, pkt_buff_p_index);
|
|
} else {
|
|
pkt_buff = kmalloc(MAX_PKT_MSG_SIZE, GFP_KERNEL);
|
|
if (pkt_buff == NULL) {
|
|
error = TFA98XX_ERROR_FAIL;
|
|
goto dsp_msg_packet_error_exit;
|
|
}
|
|
}
|
|
#else
|
|
pkt_buff = kmalloc(MAX_PKT_MSG_SIZE, GFP_KERNEL);
|
|
if (pkt_buff == NULL)
|
|
return TFA98XX_ERROR_FAIL;
|
|
#endif /* TFADSP_DSP_BUFFER_POOL */
|
|
|
|
loop = (tfadsp_buf_size / packet_size)
|
|
+ (tfadsp_buf_size % packet_size) ? 1 : 0;
|
|
|
|
for (packet_id = 0; packet_id < loop; packet_id++) {
|
|
if (packet_id < loop - 1) {
|
|
pr_debug("packet[%d]: size (%d)\n",
|
|
packet_id, packet_size);
|
|
|
|
pkt_buff[0] = (uint8_t)(((packet_id + 1) >> 8) & 0xff);
|
|
pkt_buff[1] = (uint8_t)((packet_id + 1) & 0xff);
|
|
} else {
|
|
packet_size = remaining_blob_size;
|
|
pr_debug("packet[%d]: size (%d) - last\n",
|
|
packet_id, packet_size);
|
|
|
|
pkt_buff[0] = 0xff;
|
|
pkt_buff[1] = 0xff;
|
|
}
|
|
|
|
pkt_buff[2] = (uint8_t)((packet_size >> 8) & 0xff);
|
|
pkt_buff[3] = (uint8_t) (packet_size & 0xff);
|
|
|
|
memcpy(pkt_buff + 4, blob + tfadsp_buf_offset, packet_size);
|
|
|
|
if (tfa->has_msg == 0) { /* via i2c */
|
|
/* Send to the target selected */
|
|
if (tfa->dev_ops.dsp_msg)
|
|
error = (tfa->dev_ops.dsp_msg)
|
|
((void *)tfa,
|
|
packet_size + 4,
|
|
(const char *)pkt_buff);
|
|
} else { /* via msg hal */
|
|
error = tfa98xx_write_dsp
|
|
((void *)tfa,
|
|
packet_size + 4,
|
|
(const char *)pkt_buff);
|
|
}
|
|
|
|
tfadsp_buf_offset += packet_size;
|
|
remaining_blob_size -= packet_size;
|
|
}
|
|
|
|
pr_info("%s: sent %d packets: size (%d:%d)\n",
|
|
__func__, packet_id,
|
|
(packet_id - 1) * (MAX_PKT_MSG_SIZE - 4) + packet_size,
|
|
tfadsp_buf_size);
|
|
|
|
dsp_msg_packet_error_exit:
|
|
#if defined(TFADSP_DSP_BUFFER_POOL)
|
|
if (pkt_buff_p_index != -1)
|
|
pkt_buff_p_index = tfa98xx_buffer_pool_access
|
|
(pkt_buff_p_index, 0, &pkt_buff, POOL_RETURN);
|
|
else
|
|
kfree(pkt_buff);
|
|
#else
|
|
kfree(pkt_buff);
|
|
#endif /* TFADSP_DSP_BUFFER_POOL */
|
|
pkt_buff = NULL;
|
|
|
|
return error;
|
|
}
|
|
#endif /* TFADSP_DSP_MSG_PACKET_STRATEGY */
|
|
|
|
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 (tfa98xx_count_active_stream(BIT_PSTREAM) == 0) {
|
|
pr_info("%s: skip if PSTREAM is lost\n", __func__);
|
|
tfa->individual_msg = 0;
|
|
return error;
|
|
}
|
|
|
|
if (tfa->convert_dsp32) {
|
|
length = 4 * length24 / 3;
|
|
bytes = kmem_cache_alloc(tfa->cachep, GFP_KERNEL);
|
|
}
|
|
|
|
if (tfa->has_msg == 0) { /* via i2c */
|
|
if (tfa->dev_ops.dsp_msg_read)
|
|
error = (tfa->dev_ops.dsp_msg_read)((void *)tfa,
|
|
length, bytes);
|
|
} else { /* via msg hal */
|
|
error = tfa98xx_read_dsp((void *)tfa, length, bytes);
|
|
}
|
|
if (error == TFA98XX_ERROR_OK)
|
|
pr_debug("%s: OK\n", __func__);
|
|
else
|
|
/* Get actual error code from softDSP */
|
|
error = (enum tfa98xx_error)
|
|
(error + TFA98XX_ERROR_BUFFER_RPC_BASE);
|
|
|
|
/* DSP verbose has argument 0x04 */
|
|
if ((tfa->verbose & 0x04) != 0) {
|
|
pr_debug("DSP R [%d]: ", length);
|
|
for (i = 0; i < ((length > 3) ? 3 : 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];
|
|
}
|
|
}
|
|
|
|
tfa->individual_msg = 0;
|
|
|
|
if (tfa->convert_dsp32)
|
|
kmem_cache_free(tfa->cachep, bytes);
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error reg_read(struct tfa_device *tfa,
|
|
unsigned char subaddress, unsigned short *value)
|
|
{
|
|
enum tfa98xx_error error;
|
|
|
|
error = (tfa->dev_ops.reg_read)(tfa, subaddress, value);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
/* Get actual error code from softDSP */
|
|
error = (enum tfa98xx_error)
|
|
(error + TFA98XX_ERROR_BUFFER_RPC_BASE);
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error reg_write(struct tfa_device *tfa,
|
|
unsigned char subaddress, unsigned short value)
|
|
{
|
|
enum tfa98xx_error error;
|
|
|
|
error = (tfa->dev_ops.reg_write)(tfa, subaddress, value);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
/* Get actual error code from softDSP */
|
|
error = (enum tfa98xx_error)
|
|
(error + TFA98XX_ERROR_BUFFER_RPC_BASE);
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error mem_read(struct tfa_device *tfa,
|
|
unsigned int start_offset, int num_words, int *p_values)
|
|
{
|
|
enum tfa98xx_error error;
|
|
|
|
error = (tfa->dev_ops.mem_read)(tfa, start_offset, num_words, p_values);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
/* Get actual error code from softDSP */
|
|
error = (enum tfa98xx_error)
|
|
(error + TFA98XX_ERROR_BUFFER_RPC_BASE);
|
|
|
|
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)
|
|
/* Get actual error code from softDSP */
|
|
error = (enum tfa98xx_error)
|
|
(error + TFA98XX_ERROR_BUFFER_RPC_BASE);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* write/read raw msg functions :
|
|
* the buffer is provided in little endian format, each word
|
|
* occupying 3 bytes, length is in bytes.
|
|
* functions will return immediately and do not not wait for DSP response.
|
|
*/
|
|
#define MAX_WORDS (300)
|
|
int tfa_dsp_msg(void *data, int length, const char *buf)
|
|
{
|
|
enum tfa98xx_error error;
|
|
struct tfa_device *tfa = (struct tfa_device *)data;
|
|
int tries, rpc_status = TFA98XX_I2C_REQ_DONE;
|
|
|
|
pr_debug("%s: (dev %d) length (%d), [0]=0x%x-[1]=0x%x-[2]=0x%x\n",
|
|
__func__, tfa->dev_idx,
|
|
length, buf[0], buf[1], buf[2]);
|
|
|
|
/* write the message and notify the DSP */
|
|
error = tfa_dsp_msg_write(tfa, length, buf);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
return (int)error;
|
|
|
|
/* get the result from the DSP (polling) */
|
|
for (tries = TFA98XX_WAITRESULT_NTRIES; tries > 0; tries--) {
|
|
error = tfa_dsp_msg_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_BUFFER_RPC_BASE);
|
|
pr_debug("DSP msg status: %d (%s)\n", rpc_status,
|
|
tfa98xx_get_i2c_status_id_string(rpc_status));
|
|
}
|
|
return (int)error;
|
|
}
|
|
|
|
/*
|
|
* Read a message from dsp
|
|
*/
|
|
int tfa_dsp_msg_read(void *data, int length, unsigned char *bytes)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
struct tfa_device *tfa = (struct tfa_device *)data;
|
|
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 (int)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(tfa, FAM_TFA98XX_CF_MEM,
|
|
burst_size, bytes + offset);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
return (int)error;
|
|
|
|
num_bytes -= burst_size;
|
|
offset += burst_size;
|
|
}
|
|
|
|
return (int)error;
|
|
}
|
|
|
|
/*
|
|
* write/read raw msg functions:
|
|
* the buffer is provided in little endian format, each word
|
|
* occupying 3 bytes, length is in bytes.
|
|
* functions will return immediately and do not not wait for DSP ressponse.
|
|
* An ID is added to modify the command-ID
|
|
*/
|
|
enum tfa98xx_error tfa_dsp_msg_id(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_write_id(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_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_BUFFER_RPC_BASE);
|
|
pr_debug("DSP msg status: %d (%s)\n", rpc_status,
|
|
tfa98xx_get_i2c_status_id_string(rpc_status));
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/* read the return code for the RPC call */
|
|
TFA_INTERNAL enum tfa98xx_error
|
|
tfa98xx_check_rpc_status(struct tfa_device *tfa, int *p_rpc_status)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
/*
|
|
* value to send to 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_NOT_OPEN;
|
|
if (p_rpc_status == 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(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(tfa, 0, 1, p_rpc_status);
|
|
|
|
return error;
|
|
}
|
|
|
|
/***************************** xmem only **********************************/
|
|
enum tfa98xx_error
|
|
tfa98xx_dsp_read_mem(struct tfa_device *tfa,
|
|
unsigned int start_offset, int num_words, int *p_values)
|
|
{
|
|
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 address */
|
|
start_offset = start_offset & 0xffff;
|
|
num_bytes = num_words * bytes_per_word;
|
|
p = p_values;
|
|
|
|
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_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(tfa, FAM_TFA98XX_CF_MEM,
|
|
burst_size, bytes);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
goto tfa98xx_dsp_read_mem_exit;
|
|
|
|
tfa98xx_convert_bytes2data(burst_size, bytes, p);
|
|
|
|
num_bytes -= burst_size;
|
|
p += burst_size / bytes_per_word;
|
|
}
|
|
|
|
tfa98xx_dsp_read_mem_exit:
|
|
kmem_cache_free(tfa->cachep, bytes);
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa98xx_dsp_write_mem_word(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(1, &value, bytes);
|
|
error = tfa98xx_write_data(tfa, FAM_TFA98XX_CF_MEM, 3, bytes);
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa_cont_write_filterbank(struct tfa_device *tfa, struct tfa_filter *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(tfa,
|
|
MODULE_BIQUADFILTERBANK,
|
|
biquad_index + 1, /* start @1 */
|
|
sizeof(filter[biquad_index].biquad.bytes),
|
|
filter[biquad_index].biquad.bytes);
|
|
else
|
|
error = tfa98xx_dsp_biquad_disable(tfa,
|
|
biquad_index + 1);
|
|
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa98xx_dsp_biquad_disable(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
|
|
|| biquad_index < 1)
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
|
|
mutex_lock(&dsp_msg_lock);
|
|
|
|
/* make opcode */
|
|
bytes[nr++] = 0;
|
|
bytes[nr++] = MODULE_BIQUADFILTERBANK + 0x80;
|
|
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(BIQUAD_COEFF_SIZE, coeff_buffer, &bytes[nr]);
|
|
nr += BIQUAD_COEFF_SIZE * 3;
|
|
|
|
error = dsp_msg(tfa, nr, (char *)bytes);
|
|
|
|
mutex_unlock(&dsp_msg_lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
/* wrapper for dsp_msg that adds opcode */
|
|
enum tfa98xx_error tfa_dsp_cmd_id_write(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;
|
|
|
|
mutex_lock(&dsp_msg_lock);
|
|
|
|
buffer[nr++] = tfa->spkr_select;
|
|
buffer[nr++] = module_id + 0x80;
|
|
buffer[nr++] = param_id;
|
|
|
|
if (data != NULL && num_bytes > 0) {
|
|
memcpy(&buffer[nr], data, num_bytes);
|
|
nr += num_bytes;
|
|
}
|
|
|
|
error = dsp_msg(tfa, nr, (char *)buffer);
|
|
|
|
mutex_unlock(&dsp_msg_lock);
|
|
|
|
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(struct tfa_device *tfa,
|
|
unsigned char module_id,
|
|
unsigned char param_id, int num_bytes,
|
|
unsigned char data[])
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
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;
|
|
}
|
|
|
|
mutex_lock(&dsp_msg_lock);
|
|
|
|
if ((tfa->is_probus_device) && (tfa->dev_count == 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 */
|
|
pr_debug("%s: CC bit: 4 (DS) for mono\n", __func__);
|
|
/* CC: 4 (DS) for mono */
|
|
buffer[nr++] = 4;
|
|
} else {
|
|
pr_debug("%s: CC bit: %d\n", __func__, tfa->spkr_select);
|
|
/* CC: 0 (reset all) for stereo */
|
|
buffer[nr++] = tfa->spkr_select;
|
|
}
|
|
|
|
buffer[nr++] = module_id + 0x80;
|
|
buffer[nr++] = param_id;
|
|
|
|
tfa->individual_msg = 1;
|
|
error = dsp_msg(tfa, nr, (char *)buffer);
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
mutex_unlock(&dsp_msg_lock);
|
|
return error;
|
|
}
|
|
|
|
/* read the data from the dsp */
|
|
error = dsp_msg_read(tfa, num_bytes, data);
|
|
|
|
mutex_unlock(&dsp_msg_lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
/* wrapper for dsp_msg that adds opcode and 3 bytes required for coefs */
|
|
enum tfa98xx_error tfa_dsp_cmd_id_coefs(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;
|
|
|
|
mutex_lock(&dsp_msg_lock);
|
|
|
|
buffer[nr++] = tfa->spkr_select;
|
|
buffer[nr++] = module_id + 0x80;
|
|
buffer[nr++] = param_id;
|
|
|
|
buffer[nr++] = 0;
|
|
buffer[nr++] = 0;
|
|
buffer[nr++] = 0;
|
|
|
|
tfa->individual_msg = 1;
|
|
error = dsp_msg(tfa, nr, (char *)buffer);
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
mutex_unlock(&dsp_msg_lock);
|
|
return error;
|
|
}
|
|
|
|
/* read the data from the dsp */
|
|
error = dsp_msg_read(tfa, num_bytes, data);
|
|
|
|
mutex_unlock(&dsp_msg_lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* wrapper for dsp_msg
|
|
* adds opcode and 3 bytes required for MBDrcDynamics
|
|
*/
|
|
enum tfa98xx_error tfa_dsp_cmd_id_mbdrc_dynamics(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;
|
|
|
|
mutex_lock(&dsp_msg_lock);
|
|
|
|
buffer[nr++] = tfa->spkr_select;
|
|
buffer[nr++] = module_id + 0x80;
|
|
buffer[nr++] = param_id;
|
|
|
|
buffer[nr++] = 0;
|
|
buffer[nr++] = 0;
|
|
buffer[nr++] = (unsigned char)index_subband;
|
|
|
|
tfa->individual_msg = 1;
|
|
error = dsp_msg(tfa, nr, (char *)buffer);
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
mutex_unlock(&dsp_msg_lock);
|
|
return error;
|
|
}
|
|
|
|
/* read the data from the dsp */
|
|
error = dsp_msg_read(tfa, num_bytes, data);
|
|
|
|
mutex_unlock(&dsp_msg_lock);
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa98xx_dsp_write_preset(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(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(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(tfa, &status);
|
|
if (!status) {
|
|
get_hw_features_from_cnt(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(tfa, mtpbf) & 0xffff;
|
|
*features = tfa->hw_feature_bits = value;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa98xx_dsp_get_sw_feature_bits(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(tfa, &status);
|
|
if (!status) {
|
|
get_sw_features_from_cnt(tfa, features);
|
|
/* skip reading MTP: */
|
|
return (features[0] == -1)
|
|
? TFA98XX_ERROR_FAIL : TFA98XX_ERROR_OK;
|
|
}
|
|
}
|
|
|
|
error = tfa_dsp_cmd_id_write_read(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_RPC_PARAM_ID
|
|
*/
|
|
return error;
|
|
|
|
tfa98xx_convert_bytes2data(byte_size, bytes, features);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa98xx_dsp_get_state_info(struct tfa_device *tfa,
|
|
unsigned char bytes[], unsigned int *statesize)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
int b_support_framework = 0;
|
|
unsigned int state_size = 9;
|
|
|
|
err = tfa98xx_dsp_support_framework(tfa, &b_support_framework);
|
|
if (err == TFA98XX_ERROR_OK) {
|
|
if (b_support_framework) {
|
|
err = tfa_dsp_cmd_id_write_read(tfa,
|
|
MODULE_FRAMEWORK,
|
|
FW_PARAM_GET_STATE, 3 * state_size, bytes);
|
|
} else {
|
|
/* old ROM code, ask SpeakerBoost and
|
|
* only do first portion
|
|
*/
|
|
state_size = 8;
|
|
err = tfa_dsp_cmd_id_write_read(tfa,
|
|
MODULE_SPEAKERBOOST,
|
|
SB_PARAM_GET_STATE, 3 * state_size, bytes);
|
|
}
|
|
}
|
|
|
|
*statesize = state_size;
|
|
|
|
return err;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa98xx_dsp_support_drc(struct tfa_device *tfa, int *pb_support_drc)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
|
|
*pb_support_drc = 0;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
if (tfa->support_drc != SUPPORT_NOT_SET) {
|
|
*pb_support_drc = (tfa->support_drc == SUPPORT_YES);
|
|
} else {
|
|
int feature_bits[2];
|
|
|
|
error = tfa98xx_dsp_get_sw_feature_bits(tfa, feature_bits);
|
|
if (error == TFA98XX_ERROR_OK) {
|
|
/* easy case: new API available */
|
|
/* bit=0 means DRC enabled */
|
|
*pb_support_drc
|
|
= (feature_bits[0] & FEATURE1_DRC) == 0;
|
|
} else if (error == TFA98XX_ERROR_RPC_PARAM_ID) {
|
|
/* older ROM code, doesn't support it */
|
|
*pb_support_drc = 0;
|
|
error = TFA98XX_ERROR_OK;
|
|
}
|
|
/* else some other error, return transparently */
|
|
/* pb_support_drc only changed when error == TFA98XX_ERROR_OK */
|
|
|
|
if (error == TFA98XX_ERROR_OK)
|
|
tfa->support_drc = *pb_support_drc
|
|
? SUPPORT_YES : SUPPORT_NO;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa98xx_dsp_support_framework(struct tfa_device *tfa,
|
|
int *pb_support_framework)
|
|
{
|
|
int feature_bits[2] = {0, 0};
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
|
|
_ASSERT(pb_support_framework != 0);
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
if (tfa->support_framework != SUPPORT_NOT_SET) {
|
|
if (tfa->support_framework == SUPPORT_NO)
|
|
*pb_support_framework = 0;
|
|
else
|
|
*pb_support_framework = 1;
|
|
} else {
|
|
error = tfa98xx_dsp_get_sw_feature_bits(tfa, feature_bits);
|
|
if (error == TFA98XX_ERROR_OK) {
|
|
*pb_support_framework = 1;
|
|
tfa->support_framework = SUPPORT_YES;
|
|
} else {
|
|
*pb_support_framework = 0;
|
|
tfa->support_framework = SUPPORT_NO;
|
|
error = TFA98XX_ERROR_OK;
|
|
}
|
|
}
|
|
|
|
/* *pb_support_framework only changed when error == TFA98XX_ERROR_OK */
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa98xx_dsp_write_speaker_parameters(struct tfa_device *tfa,
|
|
int length, const unsigned char *p_speaker_bytes)
|
|
{
|
|
enum tfa98xx_error error;
|
|
int b_support_drc;
|
|
|
|
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(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(tfa, &b_support_drc);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
return error;
|
|
|
|
if (b_support_drc) {
|
|
/* 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(tfa,
|
|
MODULE_SPEAKERBOOST,
|
|
SB_PARAM_SET_AGCINS,
|
|
3,
|
|
bytes);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa98xx_dsp_write_config(struct tfa_device *tfa, int length,
|
|
const unsigned char *p_config_bytes)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
int b_support_drc;
|
|
|
|
error = tfa_dsp_cmd_id_write(tfa,
|
|
MODULE_SPEAKERBOOST,
|
|
SB_PARAM_SET_CONFIG, length,
|
|
p_config_bytes);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
return error;
|
|
|
|
error = tfa98xx_dsp_support_drc(tfa, &b_support_drc);
|
|
if (error != TFA98XX_ERROR_OK)
|
|
return error;
|
|
|
|
if (b_support_drc) {
|
|
/* 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(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(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(tfa,
|
|
MODULE_SPEAKERBOOST,
|
|
SB_PARAM_SET_DRC, length,
|
|
p_drc_bytes);
|
|
else
|
|
error = TFA98XX_ERROR_BAD_PARAMETER;
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error tfa98xx_powerdown(struct tfa_device *tfa, int powerdown)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
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(struct tfa_device *tfa, enum tfa98xx_mode mode)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
|
|
if (tfa->in_use == 0)
|
|
return TFA98XX_ERROR_NOT_OPEN;
|
|
|
|
if (error == TFA98XX_ERROR_OK) {
|
|
switch (mode) {
|
|
default:
|
|
error = TFA98XX_ERROR_BAD_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
int tfa_set_bf(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 = reg_read(tfa, address, ®value);
|
|
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 = reg_write(tfa, address, regvalue);
|
|
if (err) {
|
|
pr_err("Error setting bf :%d\n", -err);
|
|
return -err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tfa_set_bf_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 = reg_read(tfa, address, ®value);
|
|
if (err) {
|
|
pr_err("Error getting bf :%d\n", -err);
|
|
return -err;
|
|
}
|
|
|
|
msk = ((1 << (len + 1)) - 1) << pos;
|
|
regvalue &= ~msk;
|
|
regvalue |= value << pos;
|
|
|
|
err = reg_write(tfa, address, regvalue);
|
|
if (err) {
|
|
pr_err("Error setting bf :%d\n", -err);
|
|
return -err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tfa_get_bf(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 = reg_read(tfa, address, ®value);
|
|
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_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_value(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(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 = reg_write(tfa, address, reg_value);
|
|
if (err)
|
|
return -err;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tfa_read_reg(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 = reg_read(tfa, address, ®value);
|
|
if (err)
|
|
return -err;
|
|
|
|
return regvalue;
|
|
}
|
|
|
|
/*
|
|
* powerup the coolflux subsystem and wait for it
|
|
*/
|
|
enum tfa98xx_error tfa_cf_powerup(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(tfa, &status);
|
|
_ASSERT(err == TFA98XX_ERROR_OK);
|
|
if (status)
|
|
break;
|
|
|
|
/* wait 10ms to avoid busload */
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
}
|
|
|
|
if (tries == 0) { /* time out */
|
|
pr_err("DSP subsystem start timed out\n");
|
|
return TFA98XX_ERROR_STATE_TIMED_OUT;
|
|
}
|
|
|
|
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)
|
|
/* No action for TDM interface; only for I2S */
|
|
return err;
|
|
|
|
if (tfa->tfa_family == 1)
|
|
err = -tfa_set_bf(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
|
|
*/
|
|
#if defined(USE_TFA9894N2)
|
|
int is_94_N2_device(struct tfa_device *tfa)
|
|
{
|
|
return ((((tfa->rev) & 0xff) == 0x94)
|
|
&& (((tfa->rev >> 8) & 0xff) > 0x1a));
|
|
}
|
|
#endif
|
|
|
|
enum tfa98xx_error show_current_state(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
int manstate = -1;
|
|
|
|
if (tfa->tfa_family == 2 && tfa->verbose) {
|
|
#if defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
manstate = tfa_get_bf(tfa, TFA9894N2_BF_MANSTATE);
|
|
else
|
|
manstate = TFA_GET_BF(tfa, MANSTATE);
|
|
#else
|
|
manstate = TFA_GET_BF(tfa, MANSTATE);
|
|
#endif
|
|
if (manstate < 0)
|
|
return -manstate;
|
|
|
|
pr_debug("%s: tfa (dev %d): current HW manager state: %d\n",
|
|
__func__, tfa->dev_idx, manstate);
|
|
|
|
switch (manstate) {
|
|
case 0:
|
|
pr_debug("%s: power_down_state\n",
|
|
__func__);
|
|
break;
|
|
case 1:
|
|
pr_debug("%s: wait_for_source_settings_state\n",
|
|
__func__);
|
|
break;
|
|
case 2:
|
|
pr_debug("%s: connnect_pll_input_state\n",
|
|
__func__);
|
|
break;
|
|
case 3:
|
|
pr_debug("%s: disconnect_pll_input_state\n",
|
|
__func__);
|
|
break;
|
|
case 4:
|
|
pr_debug("%s: enable_pll_state\n",
|
|
__func__);
|
|
break;
|
|
case 5:
|
|
pr_debug("%s: enable_cgu_state\n",
|
|
__func__);
|
|
break;
|
|
case 6:
|
|
pr_debug("%s: init_cf_state\n",
|
|
__func__);
|
|
break;
|
|
case 7:
|
|
pr_debug("%s: enable_amplifier_state\n",
|
|
__func__);
|
|
break;
|
|
case 8:
|
|
pr_debug("%s: alarm_state\n",
|
|
__func__);
|
|
break;
|
|
case 9:
|
|
pr_debug("%s: operating_state\n",
|
|
__func__);
|
|
break;
|
|
case 10:
|
|
pr_debug("%s: mute_audio_state\n",
|
|
__func__);
|
|
break;
|
|
case 11:
|
|
pr_debug("%s: disable_cgu_pll_state\n",
|
|
__func__);
|
|
break;
|
|
default:
|
|
pr_debug("%s: unable to find current state\n",
|
|
__func__);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
#define VERSION_BIG_M_FILTER 0xff0000
|
|
#define VERSION_BIG_M_INDEX 16
|
|
#define VERSION_SMALL_M_FILTER 0x00ff00
|
|
#define VERSION_SMALL_M_INDEX 8
|
|
#define VERSION_BIG_U_FILTER 0x0000c0
|
|
#define VERSION_BIG_U_INDEX 6
|
|
#define VERSION_SMALL_U_FILTER 0x00003f
|
|
#define VERSION_SMALL_U_INDEX 0
|
|
|
|
enum tfa98xx_error tfa_get_fw_api_version(struct tfa_device *tfa,
|
|
unsigned char *pfw_version)
|
|
{
|
|
enum tfa98xx_error err = 0;
|
|
int res_len = 3;
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
unsigned char buf[2 * 3] = {0};
|
|
#else
|
|
unsigned char buf[3] = {0};
|
|
#endif /* TFA_CUSTOM_FORMAT_AT_RESPONSE */
|
|
int data[2], vitf;
|
|
|
|
if (tfa == NULL)
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
|
|
if (!tfa->is_probus_device) {
|
|
err = mem_read(tfa, FW_VAR_API_VERSION,
|
|
1, (int *)buf);
|
|
if (err) {
|
|
pr_debug("%s Error: Unable to get API Version from DSP\n",
|
|
__func__);
|
|
return err;
|
|
}
|
|
} else {
|
|
/* Read the API Value */
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
res_len = 2 * 3;
|
|
#else
|
|
res_len = 3;
|
|
#endif /* TFA_CUSTOM_FORMAT_AT_RESPONSE */
|
|
err = tfa_dsp_cmd_id_write_read(tfa,
|
|
MODULE_FRAMEWORK,
|
|
FW_PAR_ID_GET_API_VERSION,
|
|
res_len, buf);
|
|
if (err != 0) {
|
|
pr_err("%s: failed to read value\n",
|
|
__func__);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
tfa98xx_convert_bytes2data(res_len, buf, data);
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
memcpy(pfw_version, buf + 3, 3);
|
|
vitf = data[1];
|
|
#else
|
|
memcpy(pfw_version, buf, 3);
|
|
vitf = data[0];
|
|
#endif /* TFA_CUSTOM_FORMAT_AT_RESPONSE */
|
|
|
|
pr_info("%s: fw api (itf) version %d.%d.%d.%d\n",
|
|
__func__,
|
|
(vitf & VERSION_BIG_M_FILTER) >> VERSION_BIG_M_INDEX,
|
|
(vitf & VERSION_SMALL_M_FILTER) >> VERSION_SMALL_M_INDEX,
|
|
(vitf & VERSION_BIG_U_FILTER) >> VERSION_BIG_U_INDEX,
|
|
(vitf & VERSION_SMALL_U_FILTER) >> VERSION_SMALL_U_INDEX);
|
|
|
|
return err;
|
|
}
|
|
|
|
#define VERSION_STRING_LENGTH 20
|
|
#define VERSION_WORD "tfadsp"
|
|
|
|
enum tfa98xx_error tfa_get_fw_lib_version(struct tfa_device *tfa,
|
|
unsigned char *plib_version)
|
|
{
|
|
enum tfa98xx_error err = 0;
|
|
int res_len;
|
|
unsigned char buf[VERSION_STRING_LENGTH * 3] = {'\0'};
|
|
char *version_word = VERSION_WORD;
|
|
int i, j = 0, k = 0;
|
|
int num = 0;
|
|
|
|
if (tfa == NULL)
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
|
|
/*
|
|
* need to set the address to read library version
|
|
* if (!tfa->is_probus_device) {
|
|
* err = mem_read(tfa, FW_VAR_LIB_VERSION,
|
|
* VERSION_STRING_LENGTH, (int *)buf);
|
|
* if (err) {
|
|
* pr_debug("%s Error: Unable to get LIB Version from DSP\n",
|
|
* __func__);
|
|
* return err;
|
|
* }
|
|
* } else { ... }
|
|
*/
|
|
/* Read the LIB version string */
|
|
res_len = VERSION_STRING_LENGTH * 3;
|
|
err = tfa_dsp_cmd_id_write_read(tfa,
|
|
MODULE_FRAMEWORK,
|
|
FW_PAR_ID_GET_LIBRARY_VERSION,
|
|
res_len, buf);
|
|
if (err != 0) {
|
|
pr_err("%s: failed to read value\n",
|
|
__func__);
|
|
return err;
|
|
}
|
|
|
|
for (i = 0; i < VERSION_STRING_LENGTH; i++) {
|
|
char token = buf[i * 3 + 2];
|
|
|
|
if (j < strlen(version_word)) {
|
|
if (version_word[j] == token)
|
|
j++;
|
|
continue;
|
|
}
|
|
if (!(token >= '0' && token <= '9')
|
|
&& token != '.'
|
|
&& !(token == 0 && num > 0))
|
|
continue;
|
|
|
|
if (token == '.' || token == 0) {
|
|
plib_version[k++] = num;
|
|
num = 0;
|
|
continue;
|
|
}
|
|
num = num * 10 + (token - '0');
|
|
}
|
|
|
|
if (k != 3) {
|
|
pr_err("%s: invalid format for library version (%d entities)\n",
|
|
__func__, k);
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
pr_info("%s: %s library version %d.%d.%d\n",
|
|
__func__, version_word,
|
|
plib_version[0], plib_version[1], plib_version[2]);
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Write calibration values for probus / ext_dsp, to feed RE25C to algorithm
|
|
*/
|
|
enum tfa98xx_error tfa_set_calibration_values(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
unsigned char bytes[2 * 3] = {0};
|
|
unsigned short value = 0, channel = 0;
|
|
int mtpex = 0;
|
|
static int need_cal, is_bypass, is_damaged;
|
|
struct tfa_device *ntfa;
|
|
int i;
|
|
char reg_state[50] = {0};
|
|
#if defined(CHECK_CALIBRATION_DATA_RANGE)
|
|
enum tfa_error ret = tfa_error_ok;
|
|
#endif
|
|
#if defined(TFA_USE_DUMMY_CAL)
|
|
int cal_ready = 1;
|
|
#endif
|
|
|
|
/* at initial device only: to reset need_cal and is_bypass */
|
|
if (tfa_count_status_flag(tfa, TFA_SET_CONFIG) == 0) {
|
|
need_cal = 0;
|
|
is_bypass = 0;
|
|
is_damaged = 0;
|
|
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
if ((ntfa->active_handle != -1)
|
|
&& (ntfa->active_handle != i))
|
|
continue;
|
|
|
|
mtpex = tfa_dev_mtp_get(ntfa, TFA_MTP_EX);
|
|
need_cal |= (mtpex == 0) ? 1 : 0;
|
|
is_bypass |= ntfa->is_bypass;
|
|
is_damaged |= ntfa->spkr_damaged;
|
|
}
|
|
|
|
pr_debug("%s: device %s calibrated; session %s; speaker %s\n",
|
|
__func__,
|
|
(need_cal) ? "needs to be" : "is already",
|
|
(is_bypass) ? "runs in bypass" : "needs configuration",
|
|
(is_damaged) ? "is damaged" : "has no problem");
|
|
}
|
|
|
|
tfa_set_status_flag(tfa, TFA_SET_CONFIG, 1);
|
|
|
|
tfa->is_bypass = is_bypass;
|
|
|
|
if (is_bypass
|
|
|| (is_damaged && tfa->is_configured < 0)) {
|
|
pr_info("%s: [%d] skip sending calibration data: bypass %d, damaged %d\n",
|
|
__func__, tfa->dev_idx, is_bypass, is_damaged);
|
|
|
|
#if defined(TFA_BLACKBOX_LOGGING)
|
|
tfa->unset_log = 1;
|
|
#endif
|
|
|
|
/* CHECK: only once after buffering fully */
|
|
/* at last device only: to reset and flush buffer */
|
|
if (tfa_count_status_flag(tfa, TFA_SET_CONFIG)
|
|
== tfa->active_count) {
|
|
tfa_set_status_flag(tfa, TFA_SET_CONFIG, -1);
|
|
|
|
if (tfa->ext_dsp == 1) {
|
|
pr_info("%s: flush buffer in blob, in bypass\n",
|
|
__func__);
|
|
err = tfa_tib_dsp_msgmulti(tfa, -2, NULL);
|
|
}
|
|
|
|
goto set_calibration_values_exit;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
mtpex = tfa_dev_mtp_get(tfa, TFA_MTP_EX);
|
|
channel = tfa98xx_get_cnt_bitfield(tfa,
|
|
TFA7x_FAM(tfa, TDMSPKS)) % MAX_CHANNELS;
|
|
pr_info("%s: dev %d, channel %d, MTPEX=%d\n",
|
|
__func__, tfa->dev_idx, channel, mtpex);
|
|
|
|
value = tfa_dev_mtp_get(tfa, TFA_MTP_RE25);
|
|
pr_info("%s: extract from MTP - %d mOhms\n", __func__, value);
|
|
|
|
if (dsp_cal_value[channel] != -1) {
|
|
/* void counter if duplicated */
|
|
pr_debug("%s: channel %d - duplicated (%d - %d mOhm), skip counting config_count\n",
|
|
__func__, channel, value,
|
|
TFA_ReZ_FP_INT(dsp_cal_value[channel],
|
|
TFA_FW_ReZ_SHIFT) * 1000
|
|
+ TFA_ReZ_FP_FRAC(dsp_cal_value[channel],
|
|
TFA_FW_ReZ_SHIFT));
|
|
}
|
|
|
|
dsp_cal_value[channel] = TFA_ReZ_CALC(value, TFA_FW_ReZ_SHIFT);
|
|
#if defined(CHECK_CALIBRATION_DATA_RANGE)
|
|
if (mtpex) {
|
|
err = tfa_calibration_range_check(tfa, channel, value);
|
|
if (err) {
|
|
need_cal |= 1;
|
|
err = TFA98XX_ERROR_OK;
|
|
pr_info("%s: run calibration because of out-of-range\n",
|
|
__func__);
|
|
|
|
/* reset MTPEX to force calibration */
|
|
ret = tfa_dev_mtp_set(tfa, TFA_MTP_EX, 0);
|
|
if (ret != tfa_error_ok) {
|
|
pr_err("%s: resetting MPTEX failed, device %d err (%d)\n",
|
|
__func__, tfa->dev_idx, ret);
|
|
tfa->reset_mtpex = 1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (value == 0) /* run equivalent with calibration */
|
|
need_cal |= 1;
|
|
|
|
pr_info("%s: dev %d, channel %d - calibration data: %d [%s]\n",
|
|
__func__, tfa->dev_idx, channel, value,
|
|
(channel == 0) ? "Primary" : "Secondary");
|
|
pr_info("%s: config_count=%d\n", __func__,
|
|
tfa_count_status_flag(tfa, TFA_SET_CONFIG));
|
|
|
|
#if defined(TFA_USE_DUMMY_CAL)
|
|
/* calibration is not available if not all devices are active */
|
|
cal_ready = (tfa->active_count < tfa->dev_count) ? 0 : 1;
|
|
#if defined(TFA_DISABLE_AUTO_CAL)
|
|
cal_ready &= (tfa->disable_auto_cal) ? 0 : 1;
|
|
#endif
|
|
if (need_cal == 1 && cal_ready == 0) {
|
|
value = tfa->dummy_cal;
|
|
if (value == 0) /* use default calibration data */
|
|
value = DUMMY_CALIBRATION_DATA;
|
|
dsp_cal_value[channel] = TFA_ReZ_CALC(value, TFA_FW_ReZ_SHIFT);
|
|
need_cal = 0;
|
|
pr_info("%s: dev %d, use dummy value (%d) instead, when not available\n",
|
|
__func__, tfa->dev_idx, value);
|
|
}
|
|
#endif /* TFA_USE_DUMMY_CAL */
|
|
|
|
#if !defined(TRACE_STATUS_AT_CALIBRATION)
|
|
if (tfa->verbose)
|
|
#endif
|
|
{
|
|
/* check the configuration for cal profile */
|
|
snprintf(reg_state, 50, "DCA %d", TFA_GET_BF(tfa, DCA));
|
|
#if defined(USE_TFA9878)
|
|
snprintf(reg_state + strlen(reg_state),
|
|
50 - strlen(reg_state), ", IPM %d",
|
|
TFA7x_GET_BF(tfa, IPM));
|
|
#endif /* USE_TFA9878 */
|
|
switch (tfa->rev & 0xff) {
|
|
case 0x78:
|
|
case 0x74:
|
|
case 0x72:
|
|
case 0x94:
|
|
snprintf(reg_state + strlen(reg_state),
|
|
50 - strlen(reg_state), ", LPM1MODE %d",
|
|
TFA7x_GET_BF(tfa, LPM1MODE));
|
|
snprintf(reg_state + strlen(reg_state),
|
|
50 - strlen(reg_state), ", LNMODE %d",
|
|
TFA7x_GET_BF(tfa, LNMODE));
|
|
break;
|
|
default:
|
|
/* neither TFA987x */
|
|
break;
|
|
}
|
|
pr_debug("%s: %s\n", __func__, reg_state);
|
|
}
|
|
|
|
#if defined(TFA_BLACKBOX_LOGGING)
|
|
tfa->unset_log = (need_cal) ? 1 : 0;
|
|
#endif
|
|
|
|
#if defined(SET_CALIBRATION_AT_ALL_DEVICE_READY)
|
|
if (need_cal == 1) {
|
|
int tfa_state = tfa_dev_get_state(tfa);
|
|
|
|
if (tfa_state != TFA_STATE_OPERATING) {
|
|
pr_debug("%s: [%d] device is not ready though calibration is required\n",
|
|
__func__, tfa->dev_idx);
|
|
/* discount for retrial */
|
|
tfa_set_status_flag(tfa, TFA_SET_CONFIG, 0);
|
|
dsp_cal_value[channel] = -1;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* trigger at the last device */
|
|
if (tfa_count_status_flag(tfa, TFA_SET_CONFIG)
|
|
< tfa->active_count) {
|
|
pr_debug("%s: suspend setting calibration data till all device is enabled\n",
|
|
__func__);
|
|
return err;
|
|
}
|
|
#endif /* SET_CALIBRATION_AT_ALL_DEVICE_READY */
|
|
|
|
pr_info("%s: device count=%d, active count=%d\n",
|
|
__func__, tfa->dev_count, tfa->active_count);
|
|
|
|
/* If calibration is set to once we load from MTP, else send zero's */
|
|
if (need_cal == 0) {
|
|
pr_info("%s: last dev %d - MTPEX=%d\n",
|
|
__func__, tfa->dev_idx, mtpex);
|
|
if (tfa->dev_count == 1) { /* mono */
|
|
dsp_cal_value[1] = dsp_cal_value[0];
|
|
} else if (tfa->dev_count == 2) { /* stereo */
|
|
switch (tfa->active_handle) {
|
|
case 0:
|
|
pr_info("%s: copy cal from dev 0 to dev 1\n",
|
|
__func__);
|
|
dsp_cal_value[1] = dsp_cal_value[0];
|
|
break;
|
|
case 1:
|
|
pr_info("%s: copy cal from dev 1 to dev 0\n",
|
|
__func__);
|
|
dsp_cal_value[0] = dsp_cal_value[1];
|
|
break;
|
|
case -1:
|
|
/* individually configured */
|
|
default:
|
|
/* wrong handle */
|
|
break;
|
|
}
|
|
} else {
|
|
pr_err("%s: more than 2 devices were selected (%d devices)\n",
|
|
__func__, tfa->dev_count);
|
|
}
|
|
|
|
/* We have to copy it for both channels. Even when MONO! */
|
|
bytes[0] = (uint8_t)((dsp_cal_value[0] >> 16) & 0xffff);
|
|
bytes[1] = (uint8_t)((dsp_cal_value[0] >> 8) & 0xff);
|
|
bytes[2] = (uint8_t)(dsp_cal_value[0] & 0xff);
|
|
|
|
bytes[3] = (uint8_t)((dsp_cal_value[1] >> 16) & 0xffff);
|
|
bytes[4] = (uint8_t)((dsp_cal_value[1] >> 8) & 0xff);
|
|
bytes[5] = (uint8_t)(dsp_cal_value[1] & 0xff);
|
|
|
|
#if defined(TFA_BLACKBOX_LOGGING)
|
|
if (tfa->blackbox_enable) {
|
|
/* set logging once before configuring */
|
|
pr_info("%s: set blackbox logging\n", __func__);
|
|
tfa_configure_log(tfa->blackbox_enable);
|
|
}
|
|
#endif
|
|
} else { /* calibration is required */
|
|
pr_info("%s: config ResetRe25C to do calibration\n", __func__);
|
|
bytes[0] = 0;
|
|
bytes[1] = 0;
|
|
bytes[2] = 0;
|
|
bytes[3] = 0;
|
|
bytes[4] = 0;
|
|
bytes[5] = 0;
|
|
|
|
/* force UNMUTE state before calibration, when INIT_CF done */
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
ntfa->is_calibrating = 1;
|
|
ntfa->spkr_damaged = 0; /* reset before calibration */
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
ntfa->vval_result = VVAL_PASS;
|
|
#endif
|
|
|
|
pr_debug("%s: [%d] force UNMUTE before calibration\n",
|
|
__func__, ntfa->dev_idx);
|
|
tfa_dev_set_state(ntfa, TFA_STATE_UNMUTE, 1);
|
|
}
|
|
}
|
|
|
|
tfa_set_status_flag(tfa, TFA_SET_CONFIG, -1);
|
|
dsp_cal_value[0] = dsp_cal_value[1] = -1;
|
|
|
|
#if defined(TFA_USE_TFASTC_NODE)
|
|
#if defined(TFA_USE_STC_VOLUME_TABLE)
|
|
/* initialize volume control */
|
|
err = tfa_write_volume(tfa, NULL);
|
|
#endif /* TFA_USE_STC_VOLUME_TABLE */
|
|
#endif /* TFA_USE_TFASTC_NODE */
|
|
|
|
err = tfa_dsp_cmd_id_write
|
|
(tfa, MODULE_SPEAKERBOOST, SB_PARAM_SET_RE25C,
|
|
sizeof(bytes), bytes);
|
|
if (err != TFA98XX_ERROR_OK)
|
|
goto set_calibration_values_exit;
|
|
|
|
#if defined(WRITE_CALIBRATION_DATA_TO_MTP)
|
|
if (need_cal == 0)
|
|
goto set_calibration_values_exit;
|
|
|
|
#if !defined(TFA_WAIT_CAL_IN_WORKQUEUE)
|
|
err = tfa_wait_cal(tfa);
|
|
#else
|
|
pr_info("%s: [%d] queue post-process to calibration\n",
|
|
__func__, tfa->dev_idx);
|
|
queue_delayed_work(tfa->tfacal_wq, &tfa->wait_cal_work, 0);
|
|
#endif
|
|
#endif /* WRITE_CALIBRATION_DATA_TO_MTP */
|
|
|
|
set_calibration_values_exit:
|
|
dsp_cal_value[0] = dsp_cal_value[1] = -1;
|
|
|
|
#if defined(TFA_USE_WAITQUEUE_SEQ)
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
wake_up_interruptible(&ntfa->waitq_seq);
|
|
}
|
|
#endif
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Call tfa_set_calibration_values at once with loop
|
|
*/
|
|
enum tfa98xx_error tfa_set_calibration_values_once(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
struct tfa_device *ntfa;
|
|
int i;
|
|
|
|
/* first device or stopped */
|
|
if (tfa_count_status_flag(tfa, TFA_SET_DEVICE) == 1
|
|
|| tfa_count_status_flag(tfa, TFA_SET_CONFIG) > 0) {
|
|
pr_info("%s: tfa_set_calibration_values\n", __func__);
|
|
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
if ((ntfa->active_handle != -1)
|
|
&& (ntfa->active_handle != i))
|
|
continue;
|
|
|
|
ntfa->is_bypass = tfa->is_bypass;
|
|
|
|
err = tfa_set_calibration_values(ntfa);
|
|
if (err)
|
|
pr_err("%s: dev %d, set calibration values error = %d\n",
|
|
__func__, i, err);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* start the speakerboost algorithm
|
|
* this implies a full system startup when the system was not already started
|
|
*/
|
|
enum tfa98xx_error tfa_run_speaker_boost(struct tfa_device *tfa,
|
|
int force, int profile)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
int value;
|
|
|
|
if (force) {
|
|
tfa->is_cold = 1;
|
|
err = tfa_run_coldstartup(tfa, profile);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* Returns 1 when device is "cold" and 0 when device is warm */
|
|
value = tfa_is_cold(tfa);
|
|
value |= tfa->reset_mtpex; /* forced cold start */
|
|
if (value < 0)
|
|
err = value;
|
|
else
|
|
tfa->is_cold = value;
|
|
|
|
pr_info("%s: %s boot, ext_dsp = %d, profile = %d\n",
|
|
__func__, value ? "cold" : "warm",
|
|
tfa->ext_dsp, profile);
|
|
|
|
pr_debug("Startup of device [%s] is a %sstart\n",
|
|
tfa_cont_device_name(tfa->cnt, tfa->dev_idx),
|
|
value ? "cold" : "warm");
|
|
|
|
/* CHECK: only once before buffering */
|
|
/* at initial device only: to flush buffer */
|
|
if (tfa_count_status_flag(tfa, TFA_SET_DEVICE) == 1) {
|
|
/* flush message buffer */
|
|
pr_debug("%s: flush buffer in blob, in cold start\n",
|
|
__func__);
|
|
err = tfa_tib_dsp_msgmulti(tfa, -2, NULL);
|
|
}
|
|
|
|
/* cold start */
|
|
if (value) {
|
|
/* Run startup and write all files */
|
|
pr_info("%s: cold start, speaker startup\n", __func__);
|
|
err = tfa_run_speaker_startup(tfa, force, profile);
|
|
if (err) {
|
|
pr_info("%s: dev %d, speaker startup error = %d\n",
|
|
__func__, tfa->dev_idx, err);
|
|
return err;
|
|
}
|
|
|
|
/* Save the current profile and set the vstep to 0 */
|
|
/* This needs to be overwritten even in CF bypass */
|
|
tfa_dev_set_swprof(tfa, (unsigned short)profile);
|
|
tfa_dev_set_swvstep(tfa, 0);
|
|
|
|
#if defined(TFADSP_CONFIGURE_AT_FIRST_DEVICE)
|
|
/* always send the SetRe25 message
|
|
* to indicate all messages are sent
|
|
*/
|
|
if (tfa->ext_dsp == 1)
|
|
err = tfa_set_calibration_values_once(tfa);
|
|
#endif /* TFADSP_CONFIGURE_AT_FIRST_DEVICE */
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* cold start */
|
|
/* always send the SetRe25 message
|
|
* to indicate all messages are sent
|
|
*/
|
|
if (value) {
|
|
#if !defined(TFADSP_CONFIGURE_AT_FIRST_DEVICE)
|
|
if (tfa->ext_dsp == 1) {
|
|
pr_info("%s: [%d] tfa_set_calibration_values\n",
|
|
__func__, tfa->dev_idx);
|
|
|
|
err = tfa_set_calibration_values(tfa);
|
|
if (err)
|
|
pr_err("%s: set calibration values error = %d\n",
|
|
__func__, err);
|
|
}
|
|
#endif /* TFADSP_CONFIGURE_AT_FIRST_DEVICE */
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
enum tfa98xx_error
|
|
tfa_run_speaker_startup(struct tfa_device *tfa, int force, int profile)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
enum tfa_error ret = tfa_error_ok;
|
|
#if !defined(TFA_VOID_APIV_IN_FILE) || !defined(TFA_VOID_LIBV_CHECK)
|
|
struct tfa_device *ntfa;
|
|
int i;
|
|
#endif
|
|
struct tfa_device *tfa0 = NULL;
|
|
#if defined(TFA_START_CAL_IN_OPERATING_STATE)
|
|
int mtpex = 0, tries = 0;
|
|
int tfa_state;
|
|
#endif
|
|
|
|
pr_debug("coldstart%s :", force ? " (forced)" : "");
|
|
|
|
if (!force) { /* in case of force CF already running */
|
|
err = tfa_run_startup(tfa, profile);
|
|
PRINT_ASSERT(err);
|
|
if (err) {
|
|
pr_info("%s: tfa_run_startup error = %d\n",
|
|
__func__, 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 = tfa_run_start_dsp(tfa);
|
|
if (err)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
#if defined(TFA_START_CAL_IN_OPERATING_STATE)
|
|
mtpex = tfa_dev_mtp_get(tfa, TFA_MTP_EX);
|
|
#if defined(TFA_USE_DUMMY_CAL)
|
|
if ((mtpex == 0 || tfa->reset_mtpex)
|
|
&& (tfa->state != TFA_STATE_OPERATING)
|
|
&& (tfa->active_count == tfa->dev_count))
|
|
#else
|
|
if ((mtpex == 0 || tfa->reset_mtpex)
|
|
&& (tfa->state != TFA_STATE_OPERATING))
|
|
#endif
|
|
{
|
|
pr_info("%s: suspend calibration until device %d is in operating state\n",
|
|
__func__, tfa->dev_idx);
|
|
|
|
tries = 0;
|
|
while (tries < CFSTABLE_TRIES) {
|
|
tfa_state = tfa_dev_get_state(tfa);
|
|
if (tfa_state == TFA_STATE_OPERATING)
|
|
break;
|
|
|
|
/* tfa7x_status(tfa); */
|
|
pr_info("%s: dev %d - [%d] - AREFS %d, NOCLK %d, CLKS %d, AMPS %d, PLLS %d\n",
|
|
__func__, tfa->dev_idx, ++tries,
|
|
TFA7x_GET_BF(tfa, AREFS),
|
|
TFA7x_GET_BF(tfa, NOCLK),
|
|
TFA7x_GET_BF(tfa, CLKS),
|
|
TFA7x_GET_BF(tfa, AMPS),
|
|
TFA7x_GET_BF(tfa, PLLS));
|
|
|
|
#if defined(RAMPING_WITH_USLEEP)
|
|
usleep_range(BUSLOAD_INTERVAL * 1000,
|
|
BUSLOAD_INTERVAL * 1000 + 5);
|
|
#else
|
|
/*
|
|
* practically, msleep takes 20 msec
|
|
* need to use usleep_range if it works
|
|
*/
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
#endif
|
|
}
|
|
|
|
if (tries >= CFSTABLE_TRIES) {
|
|
pr_err("%s: timeout - device %d is still waiting\n",
|
|
__func__, tfa->dev_idx);
|
|
tfa_state = tfa_dev_get_state(tfa);
|
|
}
|
|
}
|
|
#endif /* TFA_START_CAL_IN_OPERATING_STATE */
|
|
|
|
if (tfa->reset_mtpex) { /* reset MTPEX, if suspended */
|
|
pr_info("%s: reset MTPEX (device %d)\n",
|
|
__func__, tfa->dev_idx);
|
|
tfa->reset_mtpex = 0;
|
|
ret = tfa_dev_mtp_set(tfa, TFA_MTP_EX, 0);
|
|
ret |= tfa_dev_mtp_set(tfa, TFA_MTP_OTC, 1);
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
if (!tfa->vval_active)
|
|
ret |= tfa_dev_mtp_set(tfa, TFA_MTP_RE25, 0);
|
|
#else
|
|
ret |= tfa_dev_mtp_set(tfa, TFA_MTP_RE25, 0);
|
|
#endif
|
|
if (ret)
|
|
pr_err("%s: resetting MTP failed (%d)\n",
|
|
__func__, ret);
|
|
}
|
|
|
|
/* Set auto_copy_mtp_to_iic (bit 5 of 0xa3) to 1 */
|
|
tfa98xx_auto_copy_mtp_to_iic(tfa);
|
|
|
|
if (tfa->is_probus_device)
|
|
/* write files only if it's not loaded */
|
|
if (tfa_count_status_flag(tfa, TFA_SET_DEVICE) > 1
|
|
|| tfa_count_status_flag(tfa, TFA_SET_CONFIG) > 0)
|
|
return err;
|
|
|
|
#if !defined(TFA_VOID_APIV_IN_FILE)
|
|
if (tfa->fw_itf_ver[0] == 0xff) {
|
|
err = tfa_get_fw_api_version(tfa,
|
|
(unsigned char *)&tfa->fw_itf_ver[0]);
|
|
if (err) {
|
|
pr_debug("[%s] cannot get FWAPI error = %d\n",
|
|
__func__, err);
|
|
err = TFA98XX_ERROR_OK;
|
|
} else {
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
if (ntfa->dev_idx == tfa->dev_idx)
|
|
continue;
|
|
|
|
memcpy(&ntfa->fw_itf_ver[0],
|
|
&tfa->fw_itf_ver[0], 4);
|
|
}
|
|
}
|
|
} else {
|
|
pr_debug("%s: checked - itf v%d.%d.%d.%d\n",
|
|
__func__,
|
|
tfa->fw_itf_ver[0],
|
|
tfa->fw_itf_ver[1],
|
|
tfa->fw_itf_ver[2],
|
|
tfa->fw_itf_ver[3]);
|
|
}
|
|
#endif /* TFA_VOID_APIV_IN_FILE */
|
|
|
|
#if !defined(TFA_VOID_LIBV_CHECK)
|
|
if (tfa->fw_lib_ver[0] == 0xff) {
|
|
err = tfa_get_fw_lib_version(tfa,
|
|
(unsigned char *)&tfa->fw_lib_ver[0]);
|
|
if (err) {
|
|
pr_debug("[%s] cannot get FWLIB error = %d\n",
|
|
__func__, err);
|
|
err = TFA98XX_ERROR_OK;
|
|
} else {
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
if (ntfa->dev_idx == tfa->dev_idx)
|
|
continue;
|
|
|
|
memcpy(&ntfa->fw_lib_ver[0],
|
|
&tfa->fw_lib_ver[0], 3);
|
|
}
|
|
}
|
|
} else {
|
|
pr_debug("%s: checked - library v%d.%d.%d\n",
|
|
__func__,
|
|
tfa->fw_lib_ver[0],
|
|
tfa->fw_lib_ver[1],
|
|
tfa->fw_lib_ver[2]);
|
|
}
|
|
#endif /* TFA_VOID_LIBV_CHECK */
|
|
|
|
if (tfa->is_probus_device)
|
|
tfa0 = tfa98xx_get_tfa_device_from_index(0);
|
|
|
|
/* DSP is running now */
|
|
/* write all the files from the device list */
|
|
if (tfa0 != NULL) {
|
|
pr_info("%s: load dev files from main device %d\n",
|
|
__func__, tfa0->dev_idx);
|
|
/* CHECK: loading main device */
|
|
err = tfa_cont_write_files(tfa0);
|
|
} else {
|
|
pr_info("%s: load dev files from individual device %d\n",
|
|
__func__, tfa->dev_idx);
|
|
err = tfa_cont_write_files(tfa);
|
|
}
|
|
if (err) {
|
|
pr_debug("[%s] tfa_cont_write_files error = %d\n",
|
|
__func__, err);
|
|
return err;
|
|
}
|
|
|
|
tfa->is_bypass = 1; /* reset before start */
|
|
/* write all the files from the profile list (use volumstep 0) */
|
|
pr_info("%s: load prof files (device %d, profile %d)\n",
|
|
__func__, tfa->dev_idx, profile);
|
|
err = tfa_cont_write_files_prof(tfa, profile, 0);
|
|
if (err) {
|
|
pr_debug("[%s] tfa_cont_write_files_prof error = %d\n",
|
|
__func__, err);
|
|
return err;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Run calibration
|
|
*/
|
|
enum tfa98xx_error
|
|
tfa_run_speaker_calibration(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
int calibrate_done;
|
|
|
|
/* return if there is no audio running */
|
|
if ((tfa->tfa_family == 2) && TFA_GET_BF(tfa, NOCLK))
|
|
return TFA98XX_ERROR_NO_CLOCK;
|
|
|
|
/* When MTPOTC is set (cal=once) unlock key2 */
|
|
if (TFA_GET_BF(tfa, MTPOTC) == 1)
|
|
tfa98xx_key2(tfa, 0);
|
|
|
|
/* await calibration, this should return ok */
|
|
err = tfa_run_wait_calibration(tfa, &calibrate_done);
|
|
#if defined(WRITE_CALIBRATION_DATA_PARTLY)
|
|
if (!tfa->spkr_damaged)
|
|
#else
|
|
if (err == TFA98XX_ERROR_OK)
|
|
#endif /* WRITE_CALIBRATION_DATA_PARTLY */
|
|
{
|
|
err = tfa_dsp_get_calibration_impedance(tfa);
|
|
PRINT_ASSERT(err);
|
|
}
|
|
|
|
/* When MTPOTC is set (cal=once) re-lock key2 */
|
|
if (TFA_GET_BF(tfa, MTPOTC) == 1)
|
|
tfa98xx_key2(tfa, 1);
|
|
|
|
return err;
|
|
}
|
|
|
|
enum tfa98xx_error tfa_run_coldboot(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 tfa_cont_write_patch(tfa);
|
|
}
|
|
|
|
/*
|
|
* this will load the patch witch will implicitly start the DSP
|
|
* if no patch is available the DSP is started immediately
|
|
*/
|
|
enum tfa98xx_error tfa_run_start_dsp(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 */
|
|
pr_err("%s: failed to load patch\n", __func__);
|
|
return err;
|
|
}
|
|
|
|
/* Clear count_boot, should be reset to 0
|
|
* before 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(tfa, 0);
|
|
/* in pair of tfa98xx_init() - tfa_run_startup() */
|
|
PRINT_ASSERT(err);
|
|
}
|
|
|
|
/* Sample rate is needed to set the correct tables */
|
|
err = tfa98xx_dsp_write_tables(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 tfa_run_startup(struct tfa_device *tfa, int profile)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
struct tfa_device_list *dev = tfa_cont_device(tfa->cnt, tfa->dev_idx);
|
|
int i, noinit = 0, audfs = 0, fractdel = 0;
|
|
char prof_name[MAX_CONTROL_NAME] = {0};
|
|
#if defined(REDUCED_REGISTER_SETTING)
|
|
int is_cold_amp;
|
|
#endif
|
|
int tfa_state;
|
|
|
|
if (dev == NULL)
|
|
return TFA98XX_ERROR_FAIL;
|
|
|
|
if (dev->bus) /* no i2c device, do nothing */
|
|
return TFA98XX_ERROR_OK;
|
|
|
|
#if defined(REDUCED_REGISTER_SETTING)
|
|
is_cold_amp = tfa_is_cold_amp(tfa);
|
|
pr_info("%s: is_cold_amp %d, first_after_boot %d\n",
|
|
__func__, is_cold_amp, tfa->first_after_boot);
|
|
#if defined(TFA_PRELOAD_SETTING_AT_PROBING)
|
|
if ((tfa->first_after_boot != 2)
|
|
&& (tfa->first_after_boot == 1 || is_cold_amp == 1))
|
|
#else
|
|
if (tfa->first_after_boot || is_cold_amp == 1)
|
|
#endif
|
|
#endif /* REDUCED_REGISTER_SETTING */
|
|
{
|
|
/* process the device list
|
|
* to see if the user implemented the noinit
|
|
*/
|
|
for (i = 0; i < dev->length; i++) {
|
|
if (dev->list[i].type == dsc_no_init) {
|
|
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(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("Warning: 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
|
|
*/
|
|
pr_info("%s: write registers under dev to device %d\n",
|
|
__func__, tfa->dev_idx);
|
|
err = tfa_cont_write_regs_dev(tfa);
|
|
/* write device register settings */
|
|
PRINT_ASSERT(err);
|
|
}
|
|
#if defined(REDUCED_REGISTER_SETTING)
|
|
else {
|
|
pr_info("%s: skip_init and writing registers under dev (%d:%d)\n",
|
|
__func__, tfa->first_after_boot, is_cold_amp);
|
|
}
|
|
#endif /* REDUCED_REGISTER_SETTING */
|
|
|
|
#if defined(REDUCED_REGISTER_SETTING)
|
|
#if defined(TFA_PRELOAD_SETTING_AT_PROBING)
|
|
if (((tfa->first_after_boot != 2)
|
|
&& (tfa->first_after_boot == 1 || is_cold_amp == 1))
|
|
|| (profile != tfa_dev_get_swprof(tfa)))
|
|
#else
|
|
if ((tfa->first_after_boot || (is_cold_amp == 1))
|
|
|| (profile != tfa_dev_get_swprof(tfa)))
|
|
#endif
|
|
#endif /* REDUCED_REGISTER_SETTING */
|
|
{
|
|
/* also write register the settings from the default profile
|
|
* NOTE we may still have ACS=1
|
|
* so we can switch sample rate here
|
|
*/
|
|
pr_info("%s: write registers under profile (%d) to device %d\n",
|
|
__func__, profile, tfa->dev_idx);
|
|
err = tfa_cont_write_regs_prof(tfa, profile);
|
|
PRINT_ASSERT(err);
|
|
}
|
|
#if defined(REDUCED_REGISTER_SETTING)
|
|
else {
|
|
pr_info("%s: skip writing registers under profile (%d) to device %d\n",
|
|
__func__, profile, tfa->dev_idx);
|
|
}
|
|
#endif /* REDUCED_REGISTER_SETTING */
|
|
|
|
/* Factory trimming for the Boost converter */
|
|
tfa98xx_factory_trimmer(tfa);
|
|
|
|
#if defined(REDUCED_REGISTER_SETTING)
|
|
tfa->first_after_boot = 0;
|
|
#endif
|
|
|
|
#if defined(TFA_CHANGE_PCM_FORMAT)
|
|
/* update PCM format, if changed, before power up */
|
|
tfa_overwrite_pcm_format(tfa);
|
|
#endif
|
|
|
|
tfa_state = tfa_dev_get_state(tfa);
|
|
if (tfa_state == TFA_STATE_INIT_CF
|
|
|| tfa_state == TFA_STATE_INIT_FW
|
|
|| tfa_state == TFA_STATE_OPERATING) {
|
|
pr_info("%s: skip setting state to INIT_CF (%d)\n",
|
|
__func__, tfa_state);
|
|
goto tfa_run_startup_exit;
|
|
}
|
|
|
|
/* Go to the initCF state */
|
|
strlcpy(prof_name, tfa_cont_profile_name(tfa->cnt,
|
|
tfa->dev_idx, profile), MAX_CONTROL_NAME);
|
|
tfa_dev_set_state(tfa, TFA_STATE_INIT_CF,
|
|
strnstr(prof_name, ".cal", strlen(prof_name)) != NULL);
|
|
|
|
tfa_run_startup_exit:
|
|
#if defined(TFA_MUTE_CONTROL)
|
|
if (tfa->mute_state) {
|
|
pr_info("%s: MUTE dev %d (by force)\n",
|
|
__func__, tfa->dev_idx);
|
|
tfa_dev_set_state(tfa, TFA_STATE_MUTE, 0);
|
|
}
|
|
#endif
|
|
|
|
err = show_current_state(tfa);
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* run the startup/init sequence and set ACS bit
|
|
*/
|
|
enum tfa98xx_error tfa_run_coldstartup(struct tfa_device *tfa, int profile)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
|
|
#if defined(REDUCED_REGISTER_SETTING)
|
|
tfa->first_after_boot = 1;
|
|
#endif
|
|
err = tfa_run_startup(tfa, profile);
|
|
PRINT_ASSERT(err);
|
|
if (err)
|
|
return err;
|
|
|
|
if (!tfa->is_probus_device) {
|
|
/* force cold boot */
|
|
err = tfa_run_coldboot(tfa, 1); /* set ACS */
|
|
PRINT_ASSERT(err);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (tfa->ext_dsp == -1) {
|
|
/* start */
|
|
err = tfa_run_start_dsp(tfa);
|
|
PRINT_ASSERT(err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* run mute (optionally, with ramping down)
|
|
*/
|
|
enum tfa98xx_error tfa_run_mute(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error ret = TFA98XX_ERROR_OK;
|
|
enum tfa_error err = tfa_error_ok;
|
|
int status;
|
|
int tries = 0;
|
|
#if defined(TFA_RAMPDOWN_BEFORE_MUTE)
|
|
int i = 0, cur_ampe;
|
|
|
|
cur_ampe = TFA_GET_BF(tfa, AMPE);
|
|
if (cur_ampe == 0)
|
|
tfa_gain_rampdown(tfa, 0, -1);
|
|
else
|
|
for (i = 0; i < RAMPDOWN_MAX; i++) {
|
|
ret = tfa_gain_rampdown(tfa, i, RAMPDOWN_MAX);
|
|
if (ret == TFA98XX_ERROR_OTHER)
|
|
break;
|
|
#if defined(RAMPING_WITH_USLEEP)
|
|
usleep_range(RAMPING_INTERVAL * 1000,
|
|
RAMPING_INTERVAL * 1000 + 5);
|
|
#else
|
|
/*
|
|
* practically, msleep takes 20 msec
|
|
* need to use usleep_range if it works
|
|
*/
|
|
msleep_interruptible(RAMPING_INTERVAL);
|
|
#endif
|
|
}
|
|
#endif /* TFA_RAMPDOWN_BEFORE_MUTE */
|
|
|
|
/* signal the TFA98XX to mute */
|
|
/* err = tfa98xx_set_mute(tfa, TFA98XX_MUTE_AMPLIFIER); */
|
|
err = tfa_dev_set_state(tfa, TFA_STATE_MUTE, 0);
|
|
if (err != tfa_error_ok) {
|
|
pr_err("%s: failed to set mute state (err %d)\n",
|
|
__func__, err);
|
|
return TFA98XX_ERROR_OTHER;
|
|
}
|
|
|
|
if (tfa->tfa_family == 1) {
|
|
/* now wait for the amplifier to turn off */
|
|
do {
|
|
status = TFA_GET_BF(tfa, SWS);
|
|
if (status == 0)
|
|
break;
|
|
|
|
/* wait 10ms to avoid busload */
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
|
|
tries++;
|
|
} while (tries < AMPOFFWAIT_TRIES);
|
|
|
|
/*The amplifier is always switching*/
|
|
if (tries == AMPOFFWAIT_TRIES) {
|
|
pr_err("%s: timeout in stopping amplifier switching\n",
|
|
__func__);
|
|
return TFA98XX_ERROR_OTHER;
|
|
}
|
|
}
|
|
|
|
if (tfa->verbose)
|
|
pr_debug("-------------------- muted ------------------\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* run unmute
|
|
*/
|
|
enum tfa98xx_error tfa_run_unmute(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error ret = TFA98XX_ERROR_OK;
|
|
enum tfa_error err = tfa_error_ok;
|
|
#if defined(TFA_RAMPDOWN_BEFORE_MUTE)
|
|
int i = 0, cur_ampe;
|
|
|
|
cur_ampe = TFA_GET_BF(tfa, AMPE);
|
|
if (cur_ampe == 0)
|
|
tfa_gain_restore(tfa, 0, -1);
|
|
else
|
|
for (i = 0; i < RAMPDOWN_MAX; i++) {
|
|
ret = tfa_gain_restore(tfa, i, RAMPDOWN_MAX);
|
|
if (ret == TFA98XX_ERROR_OTHER)
|
|
break;
|
|
#if defined(RAMPING_WITH_USLEEP)
|
|
usleep_range(RAMPING_INTERVAL * 1000,
|
|
RAMPING_INTERVAL * 1000 + 5);
|
|
#else
|
|
/*
|
|
* practically, msleep takes 20 msec
|
|
* need to use usleep_range if it works
|
|
*/
|
|
msleep_interruptible(RAMPING_INTERVAL);
|
|
#endif
|
|
}
|
|
#endif /* TFA_RAMPDOWN_BEFORE_MUTE */
|
|
|
|
/* signal the TFA98XX to mute */
|
|
/* err = tfa98xx_set_mute(tfa, TFA98XX_MUTE_OFF); */
|
|
err = tfa_dev_set_state(tfa, TFA_STATE_UNMUTE, 0);
|
|
if (err != tfa_error_ok) {
|
|
pr_err("%s: failed to set unmute state (err %d)\n",
|
|
__func__, err);
|
|
return TFA98XX_ERROR_OTHER;
|
|
}
|
|
|
|
if (tfa->verbose)
|
|
pr_debug("------------------- unmuted -----------------\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if defined(TFA_RAMPDOWN_BEFORE_MUTE)
|
|
enum tfa98xx_error tfa_gain_rampdown(struct tfa_device *tfa,
|
|
int step, int count)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
int cur_ampgain;
|
|
|
|
if (tfa->tfa_family != 2) {
|
|
pr_debug("%s: rampdown only for tfa2\n",
|
|
__func__);
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
if (step == 0) {
|
|
cur_ampgain = TFA7x_GET_BF(tfa, AMPGAIN);
|
|
if (cur_ampgain <= 0) {
|
|
pr_debug("%s: ampgain is already rampdown (%d)\n",
|
|
__func__, cur_ampgain);
|
|
return TFA98XX_ERROR_OTHER;
|
|
}
|
|
tfa->ampgain = cur_ampgain;
|
|
if (count < 0)
|
|
pr_debug("%s: direct drop ampgain (%d to 0)\n",
|
|
__func__, tfa->ampgain);
|
|
else
|
|
pr_debug("%s: ramp down ampgain (%d)\n",
|
|
__func__, tfa->ampgain);
|
|
}
|
|
|
|
if (tfa->ampgain == -1) {
|
|
pr_debug("%s: reference gain is not valid\n", __func__);
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
/* ramp down amplifier gain for "count" msec */
|
|
if (count < 0) /* direct set */
|
|
err = TFA7x_SET_BF(tfa, AMPGAIN, 0);
|
|
else /* stepwise set */
|
|
err = TFA7x_SET_BF(tfa, AMPGAIN,
|
|
tfa->ampgain * (count - step - 1) / count);
|
|
if (err)
|
|
pr_err("%s: error in setting AMPGAIN\n",
|
|
__func__);
|
|
|
|
return err;
|
|
}
|
|
|
|
enum tfa98xx_error tfa_gain_restore(struct tfa_device *tfa,
|
|
int step, int count)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
int cur_ampgain;
|
|
|
|
if (tfa->tfa_family != 2) {
|
|
pr_debug("%s: restore only for tfa2\n",
|
|
__func__);
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
if (step == 0) {
|
|
cur_ampgain = TFA7x_GET_BF(tfa, AMPGAIN);
|
|
if ((cur_ampgain == tfa->ampgain)
|
|
|| (cur_ampgain > 0 && tfa->ampgain == -1)) {
|
|
pr_debug("%s: ampgain is already restorted (%d; %d)\n",
|
|
__func__, cur_ampgain, tfa->ampgain);
|
|
return TFA98XX_ERROR_OTHER;
|
|
}
|
|
if (count < 0)
|
|
pr_debug("%s: direct set ampgain (0 to %d)\n",
|
|
__func__, tfa->ampgain);
|
|
else
|
|
pr_debug("%s: restore ampgain (%d)\n",
|
|
__func__, tfa->ampgain);
|
|
}
|
|
|
|
/* ramp up amplifier gain for "count" msec */
|
|
if (count < 0) /* direct set */
|
|
err = TFA7x_SET_BF(tfa, AMPGAIN, tfa->ampgain);
|
|
else /* stepwise set */
|
|
err = TFA7x_SET_BF(tfa, AMPGAIN,
|
|
tfa->ampgain * (step + 1) / count);
|
|
if (err)
|
|
pr_err("%s: error in setting AMPGAIN\n",
|
|
__func__);
|
|
|
|
return err;
|
|
}
|
|
#endif /* TFA_RAMPDOWN_BEFORE_MUTE */
|
|
|
|
#if defined(USE_TFA9888)
|
|
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");
|
|
/* Disable the sound for the left speaker */
|
|
TFA_SET_BF_VOLATILE(tfa, SSLEFTE, 0);
|
|
} else if (value_s != 1) {
|
|
pr_debug("Calibration failed on Secondary (Right) channel!\n");
|
|
/* Disable the sound for the right speaker */
|
|
TFA_SET_BF_VOLATILE(tfa, SSRIGHTE, 0);
|
|
}
|
|
|
|
/* Set amplifier input to TDM */
|
|
TFA_SET_BF_VOLATILE(tfa, AMPINSEL, 0);
|
|
TFA_SET_BF_VOLATILE(tfa, SBSL, 1);
|
|
}
|
|
#endif /* USE_TFA9888 */
|
|
|
|
enum tfa98xx_error tfa_wait_cal(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error cal_err = TFA98XX_ERROR_OK;
|
|
int calibration_done = 0;
|
|
int need_restore = 0;
|
|
#if defined(WRITE_CALIBRATION_DATA_TO_MTP)
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
#endif /* WRITE_CALIBRATION_DATA_TO_MTP */
|
|
struct tfa_device *ntfa;
|
|
int i;
|
|
|
|
pr_info("%s: [%d] triggered\n",
|
|
__func__, tfa->dev_idx);
|
|
|
|
cal_err = tfa_run_wait_calibration(tfa, &calibration_done);
|
|
if (cal_err != TFA98XX_ERROR_OK || calibration_done == 0) {
|
|
pr_err("%s: calibration is not done; stop processing\n",
|
|
__func__);
|
|
|
|
#if defined(TRACE_STATUS_AT_CALIBRATION)
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
if ((ntfa->active_handle != -1)
|
|
&& (ntfa->active_handle != i))
|
|
continue;
|
|
|
|
#if defined(USE_TFA9874) || defined(USE_TFA9878) || defined(USE_TFA9894)
|
|
tfa7x_status(ntfa);
|
|
#else
|
|
tfa_status(ntfa);
|
|
#endif
|
|
}
|
|
#endif /* TRACE_STATUS_AT_CALIBRATION */
|
|
|
|
#if !defined(WRITE_CALIBRATION_DATA_PARTLY)
|
|
return cal_err;
|
|
#endif
|
|
}
|
|
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
if ((ntfa->active_handle != -1)
|
|
&& (ntfa->active_handle != i))
|
|
continue;
|
|
|
|
#if !defined(TFA_WAIT_CAL_IN_WORKQUEUE)
|
|
/* force MUTE after calibration, to set UNMUTE at sync later */
|
|
pr_debug("%s: [%d] force MUTE after calibration\n",
|
|
__func__, ntfa->dev_idx);
|
|
tfa_dev_set_state(ntfa, TFA_STATE_MUTE, 1);
|
|
#endif
|
|
|
|
#if defined(WRITE_CALIBRATION_DATA_TO_MTP)
|
|
#if defined(WRITE_CALIBRATION_DATA_PARTLY)
|
|
if (ntfa->spkr_damaged)
|
|
continue;
|
|
#endif
|
|
|
|
pr_debug("%s: [%d] process calibration data\n",
|
|
__func__, ntfa->dev_idx);
|
|
err = tfa_dsp_get_calibration_impedance(ntfa);
|
|
if (err != TFA98XX_ERROR_OK) {
|
|
cal_err = err;
|
|
PRINT_ASSERT(err);
|
|
}
|
|
#endif /* WRITE_CALIBRATION_DATA_TO_MTP */
|
|
|
|
if (ntfa->next_profile == ntfa->profile
|
|
|| ntfa->next_profile < 0)
|
|
continue;
|
|
|
|
need_restore = 1;
|
|
}
|
|
|
|
if (!need_restore)
|
|
return cal_err;
|
|
|
|
/* reset counter */
|
|
tfa_set_status_flag(tfa, TFA_SET_DEVICE, -1);
|
|
tfa_set_status_flag(tfa, TFA_SET_CONFIG, -1);
|
|
|
|
/*
|
|
* restore profile after calibration.
|
|
* typically, when calibration is done,
|
|
* profile should be updated in warm state.
|
|
*/
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
if ((ntfa->active_handle != -1)
|
|
&& (ntfa->active_handle != i))
|
|
continue;
|
|
|
|
tfa_set_status_flag(ntfa, TFA_SET_DEVICE, 1);
|
|
|
|
pr_info("%s: [%d] restore profile after calibration (active %d; next %d)\n",
|
|
__func__, ntfa->dev_idx,
|
|
ntfa->profile, ntfa->next_profile);
|
|
|
|
/* switch profile */
|
|
if (cal_err != TFA98XX_ERROR_OK || calibration_done == 0) {
|
|
/* only with the register setting in failure case */
|
|
pr_info("%s: apply only register setting at failure\n",
|
|
__func__);
|
|
|
|
err = tfa_cont_write_regs_prof(ntfa,
|
|
ntfa->next_profile);
|
|
if (err != TFA98XX_ERROR_OK)
|
|
pr_err("%s: error in writing regs (%d)\n",
|
|
__func__, err);
|
|
} else {
|
|
/* with the entire setting in success case */
|
|
pr_info("%s: apply the whole profile setting at success\n",
|
|
__func__);
|
|
|
|
err = tfa_dev_switch_profile(ntfa,
|
|
ntfa->next_profile, ntfa->vstep);
|
|
if (err != TFA98XX_ERROR_OK)
|
|
pr_err("%s: error in switch profile (%d)\n",
|
|
__func__, err);
|
|
}
|
|
}
|
|
|
|
return cal_err;
|
|
}
|
|
|
|
int tfa_run_damage_check(struct tfa_device *tfa,
|
|
int dsp_event, int dsp_status)
|
|
{
|
|
int damaged = 0, damage_event = 0;
|
|
struct tfa_device *ntfa = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_channel(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
damage_event
|
|
= (TFA_GET_BIT_VALUE(dsp_event,
|
|
i + 1)) ? 1 : 0;
|
|
#if defined(DETECT_DAMAGE_WITH_EVENT)
|
|
if (!damage_event)
|
|
continue;
|
|
|
|
/* set damage flag with event */
|
|
ntfa->spkr_damaged |= damage_event;
|
|
#else
|
|
/* set damage flag with status */
|
|
ntfa->spkr_damaged
|
|
= (TFA_GET_BIT_VALUE(dsp_status,
|
|
i + 1)) ? 1 : 0;
|
|
#endif /* DETECT_DAMAGE_WITH_EVENT */
|
|
pr_info("%s: damage flag update %d (dev %d, channel %d)\n",
|
|
__func__,
|
|
ntfa->spkr_damaged,
|
|
ntfa->dev_idx, i);
|
|
damaged |= ntfa->spkr_damaged;
|
|
}
|
|
|
|
return damaged;
|
|
}
|
|
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
int tfa_run_vval_result_check(struct tfa_device *tfa,
|
|
int dsp_event, int dsp_status)
|
|
{
|
|
int vvaL_result = 0, vval_event = 0;
|
|
struct tfa_device *ntfa = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_channel(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
vval_event
|
|
= (TFA_GET_BIT_VALUE(dsp_event,
|
|
i + 3)) ? VVAL_FAIL : VVAL_PASS;
|
|
#if defined(DETECT_VVAL_WITH_EVENT)
|
|
if (!vval_event)
|
|
continue;
|
|
|
|
/* set vval flag with event */
|
|
ntfa->vval_result |= vval_event;
|
|
#else
|
|
/* set vval flag with status */
|
|
ntfa->vval_result
|
|
= (TFA_GET_BIT_VALUE(dsp_status,
|
|
i + 3)) ? VVAL_FAIL : VVAL_PASS;
|
|
#endif /* DETECT_VVAL_WITH_EVENT */
|
|
pr_info("%s: V validation flag update %d (dev %d, channel %d)\n",
|
|
__func__,
|
|
ntfa->vval_result,
|
|
ntfa->dev_idx, i);
|
|
vvaL_result |= ntfa->vval_result;
|
|
}
|
|
|
|
return vvaL_result;
|
|
}
|
|
#endif /* TFA_USE_TFAVVAL_NODE */
|
|
|
|
/*
|
|
* wait for calibrate_done
|
|
*/
|
|
enum tfa98xx_error
|
|
tfa_run_wait_calibration(struct tfa_device *tfa, int *calibrate_done)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
int tries = 0, mtp_busy = 1, tries_mtp_busy = 0;
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
char buffer[(1 + 2) * 3] = {0};
|
|
#else
|
|
char buffer[2 * 3] = {0};
|
|
#endif /* TFA_CUSTOM_FORMAT_AT_RESPONSE */
|
|
int res_len;
|
|
int fw_status[2] = {0};
|
|
int dsp_event = 0, dsp_status = 0;
|
|
struct tfa_device *ntfa;
|
|
int i;
|
|
int damaged = 0;
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
int vval_result = 0;
|
|
#endif
|
|
|
|
*calibrate_done = 0;
|
|
|
|
/* in case of calibrate once wait for MTPEX */
|
|
if (!tfa->is_probus_device
|
|
&& 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)
|
|
/* wait 10ms to avoid busload */
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
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
|
|
* at least 25*50ms = 1.25 sec
|
|
*/
|
|
while ((*calibrate_done == 0)
|
|
&& (tries < MTPEX_WAIT_NTRIES)) {
|
|
*calibrate_done = TFA_GET_BF(tfa, MTPEX);
|
|
if (*calibrate_done == 1)
|
|
break;
|
|
/* wait 50ms to avoid busload */
|
|
msleep_interruptible(5 * BUSLOAD_INTERVAL);
|
|
tries++;
|
|
}
|
|
|
|
if (tries >= MTPEX_WAIT_NTRIES)
|
|
tries = TFA98XX_API_WAITRESULT_NTRIES;
|
|
} else {
|
|
pr_err("MTP busy after %d tries\n", MTPBWAIT_TRIES);
|
|
}
|
|
}
|
|
|
|
if (!tfa->is_probus_device) {
|
|
/* poll xmem for calibrate always
|
|
* calibrate_done = 0 means "calibrating",
|
|
* calibrate_done = -1 (or 0xffffff) means "fails"
|
|
* calibrate_done = 1 means calibration done
|
|
*/
|
|
while ((*calibrate_done != 1)
|
|
&& (tries < TFA98XX_API_WAITRESULT_NTRIES)) {
|
|
err = mem_read(tfa, TFA_FW_XMEM_CALIBRATION_DONE,
|
|
1, calibrate_done);
|
|
if (*calibrate_done == -1)
|
|
break;
|
|
tries++;
|
|
}
|
|
} else {
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
res_len = (1 + 2) * 3;
|
|
#else
|
|
res_len = 2 * 3;
|
|
#endif /* TFA_CUSTOM_FORMAT_AT_RESPONSE */
|
|
|
|
while ((*calibrate_done != 1)
|
|
&& (tries < TFA98XX_API_WAITCAL_NTRIES)) {
|
|
msleep_interruptible(CAL_STATUS_INTERVAL);
|
|
|
|
err = tfa_dsp_cmd_id_write_read
|
|
(tfa, MODULE_FRAMEWORK,
|
|
FW_PAR_ID_GET_STATUS_CHANGE,
|
|
res_len, (unsigned char *)buffer);
|
|
if (err != TFA98XX_ERROR_OK)
|
|
break;
|
|
|
|
tfa98xx_convert_bytes2data(res_len,
|
|
buffer, fw_status);
|
|
dsp_event = fw_status[0];
|
|
dsp_status = fw_status[1];
|
|
pr_debug("%s: err (%d), status (0x%06x:0x%06x), count (%d)\n",
|
|
__func__, err,
|
|
dsp_event, dsp_status,
|
|
tries + 1);
|
|
|
|
/* wait until calibration done status is set */
|
|
if (!(dsp_status & 0x1)) {
|
|
tries++;
|
|
continue;
|
|
}
|
|
|
|
*calibrate_done = 1;
|
|
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
#if defined(DETECT_DAMAGE_WITH_EVENT)
|
|
if ((dsp_event & 0x18) != 0) /* V validation event */
|
|
#else
|
|
if ((dsp_status & 0x18) != 0) /* V validation status */
|
|
#endif
|
|
vval_result = tfa_run_vval_result_check(tfa,
|
|
dsp_event, dsp_status);
|
|
#endif /* TFA_USE_TFAVVAL_NODE */
|
|
#if defined(DETECT_DAMAGE_WITH_EVENT)
|
|
if ((dsp_event & 0x6) != 0) /* damage event */
|
|
#else
|
|
if ((dsp_status & 0x6) != 0) /* damage event */
|
|
#endif
|
|
damaged = tfa_run_damage_check(tfa,
|
|
dsp_event, dsp_status);
|
|
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
if (vval_result || damaged)
|
|
*calibrate_done = 0; /* failure */
|
|
#else
|
|
if (damaged)
|
|
*calibrate_done = 0; /* failure */
|
|
#endif /* TFA_USE_TFAVVAL_NODE */
|
|
break;
|
|
}
|
|
|
|
if (tries >= TFA98XX_API_WAITCAL_NTRIES)
|
|
tries = TFA98XX_API_WAITRESULT_NTRIES;
|
|
}
|
|
|
|
if (*calibrate_done != 1) {
|
|
pr_err("Calibration failed!\n");
|
|
err = TFA98XX_ERROR_BAD_PARAMETER;
|
|
} else if (tries == TFA98XX_API_WAITRESULT_NTRIES) {
|
|
pr_err("Calibration has timedout!\n");
|
|
err = TFA98XX_ERROR_STATE_TIMED_OUT;
|
|
} else if (tries_mtp_busy == MTPBWAIT_TRIES) {
|
|
pr_err("Calibrate Failed: MTP_busy stays high!\n");
|
|
err = TFA98XX_ERROR_STATE_TIMED_OUT;
|
|
} else {
|
|
pr_info("Calibration succeeded!\n");
|
|
}
|
|
|
|
/* 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");
|
|
}
|
|
|
|
if (!tfa->is_probus_device) {
|
|
#if defined(USE_TFA9888)
|
|
/* Check which speaker calibration failed. Only for 88C */
|
|
if ((err != TFA98XX_ERROR_OK) && ((tfa->rev & 0x0fff) == 0xc88))
|
|
individual_calibration_results(tfa);
|
|
#endif
|
|
return err;
|
|
}
|
|
|
|
/* success: probus device */
|
|
if (err == TFA98XX_ERROR_OK) {
|
|
/* reset damage flag at success */
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
ntfa->is_calibrating = 0;
|
|
ntfa->spkr_damaged = 0;
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
ntfa->vval_result = VVAL_PASS;
|
|
#endif
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/* falure: probus device */
|
|
#if defined(DETECT_DAMAGE_WITH_EVENT)
|
|
/* show damage status at failure */
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_channel(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
ntfa->is_calibrating = 0;
|
|
if (ntfa->spkr_damaged)
|
|
pr_info("%s: speaker damage is detected [%s] (dev %d, channel %d)\n",
|
|
__func__,
|
|
tfa_cont_device_name(ntfa->cnt, ntfa->dev_idx),
|
|
ntfa->dev_idx, i);
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
if (ntfa->vval_result == VVAL_FAIL)
|
|
pr_info("%s: V validation failed [%s] (dev %d, channel %d)\n",
|
|
__func__,
|
|
tfa_cont_device_name(ntfa->cnt, ntfa->dev_idx),
|
|
ntfa->dev_idx, i);
|
|
else
|
|
ntfa->vval_result = VVAL_PASS;
|
|
#endif
|
|
}
|
|
#else
|
|
/* set damage status with status at failure */
|
|
pr_info("%s: speaker damage flag 0x%06x:0x%06x\n",
|
|
__func__, fw_status[0], fw_status[1]);
|
|
|
|
if ((fw_status[1] & 0x6) == 0)
|
|
return err;
|
|
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_channel(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
ntfa->is_calibrating = 0;
|
|
ntfa->spkr_damaged
|
|
= (TFA_GET_BIT_VALUE(fw_status[1], i + 1))
|
|
? 1 : 0;
|
|
if (ntfa->spkr_damaged)
|
|
pr_info("%s: speaker damage is detected [%s] (dev %d, channel %d)\n",
|
|
__func__,
|
|
tfa_cont_device_name(ntfa->cnt, ntfa->dev_idx),
|
|
ntfa->dev_idx, i);
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
ntfa->vval_result
|
|
= (TFA_GET_BIT_VALUE(fw_status[1], i + 3))
|
|
? VVAL_FAIL : VVAL_PASS;
|
|
if (ntfa->vval_result == VVAL_FAIL)
|
|
pr_info("%s: V validation failed [%s] (dev %d, channel %d)\n",
|
|
__func__,
|
|
tfa_cont_device_name(ntfa->cnt, ntfa->dev_idx),
|
|
ntfa->dev_idx, i);
|
|
#endif
|
|
}
|
|
#endif /* DETECT_DAMAGE_WITH_EVENT */
|
|
|
|
return err;
|
|
}
|
|
|
|
void tfa_set_active_handle(struct tfa_device *tfa, int profile)
|
|
{
|
|
int dev;
|
|
int active_handle = -1;
|
|
int count = 0;
|
|
struct tfa_device *ntfa = NULL;
|
|
|
|
#if defined(TFA_MIXER_ON_DEVICE)
|
|
for (dev = 0; dev < tfa->dev_count; dev++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(dev);
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
if (ntfa->set_active != 0) {
|
|
active_handle = dev;
|
|
count++;
|
|
}
|
|
}
|
|
if (count == tfa->dev_count)
|
|
active_handle = -1;
|
|
#elif defined(TFA_PROFILE_ON_DEVICE)
|
|
for (dev = 0; dev < tfa->dev_count; dev++) {
|
|
if (tfa_cont_is_dev_specific_profile(tfa->cnt,
|
|
dev, profile) != 0) {
|
|
active_handle = dev;
|
|
count++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (active_handle == -1)
|
|
count = tfa->dev_count;
|
|
|
|
pr_info("%s: active handle: %d, active count %d\n",
|
|
__func__, active_handle, count);
|
|
|
|
for (dev = 0; dev < tfa->dev_count; dev++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(dev);
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
ntfa->active_handle = active_handle;
|
|
ntfa->active_count = count;
|
|
ntfa->is_bypass = 0; /* reset at start */
|
|
}
|
|
}
|
|
|
|
void tfa_reset_active_handle(struct tfa_device *tfa)
|
|
{
|
|
int dev;
|
|
struct tfa_device *ntfa = NULL;
|
|
|
|
for (dev = 0; dev < tfa->dev_count; dev++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(dev);
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
ntfa->active_handle = -1;
|
|
ntfa->active_count = -1;
|
|
|
|
#if defined(TFA_TDMSPKG_CONTROL)
|
|
/* reload setting afterwards, if speaker gain is forced */
|
|
if (ntfa->spkgain != -1)
|
|
ntfa->first_after_boot = 1;
|
|
ntfa->spkgain = -1;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* tfa_dev_start will only do the basics:
|
|
* Going from powerdown to operating or a profile switch.
|
|
* for calibrating or acoustic shock
|
|
* handling use 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;
|
|
int mtpex = 0, cal_profile = -1;
|
|
static int tfa98xx_log_start_cnt;
|
|
int forced = 0;
|
|
int tfa_state_before, tfa_state;
|
|
char prof_name[MAX_CONTROL_NAME] = {0};
|
|
int cal_ready = 1;
|
|
|
|
tfa98xx_log_start_cnt++;
|
|
|
|
pr_debug("%s: tfa98xx_log_tfa_family=%d,",
|
|
__func__, tfa->tfa_family);
|
|
pr_debug("%s: tfa98xx_log_revision=0x%x,",
|
|
__func__, tfa->rev & 0xff);
|
|
pr_debug("%s: tfa98xx_log_subrevision=0x%x,",
|
|
__func__, (tfa->rev >> 8) & 0xff);
|
|
pr_debug("%s: tfa98xx_log_i2c_responder_address=0x%x,",
|
|
__func__, tfa->resp_address);
|
|
pr_info("%s: tfa98xx_log_start_cnt=%d next_profile %d\n",
|
|
__func__, tfa98xx_log_start_cnt, next_profile);
|
|
|
|
tfa->next_profile = next_profile;
|
|
|
|
if (tfa_count_status_flag(tfa, TFA_SET_DEVICE) < 1) {
|
|
pr_info("%s: initialize active handle\n", __func__);
|
|
/* check activeness with profile */
|
|
tfa_set_active_handle(tfa, next_profile);
|
|
}
|
|
|
|
if (tfa->active_handle != -1) {
|
|
pr_info("%s: active handle [%s]", __func__,
|
|
tfa_cont_device_name(tfa->cnt, tfa->active_handle));
|
|
|
|
if (tfa->dev_idx != tfa->active_handle) {
|
|
pr_info("%s: keep profile %d instead of changing to %d\n",
|
|
__func__, tfa_dev_get_swprof(tfa),
|
|
next_profile);
|
|
|
|
pr_info("%s: stop unused dev %d, by force\n",
|
|
__func__, tfa->dev_idx);
|
|
err = (enum tfa98xx_error)
|
|
tfa_dev_stop(tfa); /* stop inactive handle */
|
|
|
|
#if defined(TFA_USE_DUMMY_CAL)
|
|
/* skip resetting MTPEX unless all are active */
|
|
if (tfa->is_probus_device)
|
|
tfa->reset_mtpex = 0;
|
|
#endif
|
|
|
|
goto tfa_dev_start_exit;
|
|
}
|
|
} else {
|
|
pr_info("%s: all the handles active [%s]", __func__,
|
|
tfa_cont_device_name(tfa->cnt, tfa->dev_idx));
|
|
}
|
|
|
|
/* If the profile contains the .standby suffix go
|
|
* to powerdown else we should be in operating state
|
|
*/
|
|
strlcpy(prof_name, tfa_cont_profile_name(tfa->cnt,
|
|
tfa->dev_idx, next_profile), MAX_CONTROL_NAME);
|
|
if (strnstr(prof_name, ".standby", strlen(prof_name)) != NULL) {
|
|
tfa_dev_set_swprof(tfa, (unsigned short)next_profile);
|
|
tfa_dev_set_swvstep(tfa, (unsigned short)vstep);
|
|
|
|
pr_info("%s: skip starting dev %d for standby profile\n",
|
|
__func__, tfa->dev_idx);
|
|
|
|
goto tfa_dev_start_exit;
|
|
}
|
|
|
|
/* Get currentprofile */
|
|
active_profile = tfa_dev_get_swprof(tfa);
|
|
if (active_profile == 0xff)
|
|
active_profile = -1;
|
|
|
|
#if defined(TFA_RAMPDOWN_BEFORE_MUTE)
|
|
/* restore amplifier gain, if it's touched before */
|
|
if (tfa->ampgain != -1) {
|
|
int i = 0, cur_ampe;
|
|
|
|
cur_ampe = TFA_GET_BF(tfa, AMPE);
|
|
if (cur_ampe == 0)
|
|
tfa_gain_restore(tfa, 0, -1);
|
|
else
|
|
for (i = 0; i < RAMPDOWN_MAX; i++) {
|
|
err = tfa_gain_restore(tfa, i, RAMPDOWN_MAX);
|
|
if (err == TFA98XX_ERROR_OTHER)
|
|
break;
|
|
#if defined(RAMPING_WITH_USLEEP)
|
|
usleep_range(RAMPING_INTERVAL * 1000,
|
|
RAMPING_INTERVAL * 1000 + 5);
|
|
#else
|
|
/*
|
|
* practically, msleep takes 20 msec
|
|
* need to use usleep_range if it works
|
|
*/
|
|
msleep_interruptible(RAMPING_INTERVAL);
|
|
#endif
|
|
}
|
|
}
|
|
tfa->ampgain = -1;
|
|
#endif /* TFA_RAMPDOWN_BEFORE_MUTE */
|
|
|
|
mtpex = tfa_dev_mtp_get(tfa, TFA_MTP_EX);
|
|
if (mtpex == 0 || tfa->reset_mtpex) {
|
|
pr_info("%s: dev %d, MTPEX=%d%s\n",
|
|
__func__, tfa->dev_idx, mtpex,
|
|
(tfa->reset_mtpex) ? " (forced)" : "");
|
|
if (!tfa->is_probus_device) {
|
|
pr_info("%s: set cold by force in non-probus case\n",
|
|
__func__);
|
|
forced = 1;
|
|
tfa->reset_mtpex = 1;
|
|
}
|
|
#if defined(TFA_USE_DUMMY_CAL)
|
|
cal_ready = (tfa->active_count < tfa->dev_count) ? 0 : 1;
|
|
#if defined(TFA_DISABLE_AUTO_CAL)
|
|
cal_ready &= (tfa->disable_auto_cal) ? 0 : 1;
|
|
#endif
|
|
#endif /* TFA_USE_DUMMY_CAL */
|
|
if (cal_ready) {
|
|
cal_profile = tfa_cont_get_cal_profile(tfa);
|
|
if (cal_profile >= 0) {
|
|
pr_info("%s: set profile for calibration profile %d\n",
|
|
__func__, cal_profile);
|
|
next_profile = cal_profile;
|
|
}
|
|
tfa->first_after_boot = 1;
|
|
} else {
|
|
pr_info("%s: keep using profile (%d) and use dummy value if unavailable\n",
|
|
__func__, next_profile);
|
|
/* skip resetting MTPEX unless all are active */
|
|
if (tfa->is_probus_device)
|
|
tfa->reset_mtpex = 0;
|
|
}
|
|
}
|
|
|
|
/* TfaRun_SpeakerBoost implies un-mute */
|
|
pr_debug("active_profile:%s, next_profile:%s\n",
|
|
tfa_cont_profile_name(tfa->cnt, tfa->dev_idx, active_profile),
|
|
tfa_cont_profile_name(tfa->cnt, tfa->dev_idx, next_profile));
|
|
pr_debug("Starting device [%s]\n",
|
|
tfa_cont_device_name(tfa->cnt, tfa->dev_idx));
|
|
|
|
err = show_current_state(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 tfa_dev_start_exit;
|
|
}
|
|
|
|
mutex_lock(&dev_lock);
|
|
|
|
tfa_set_status_flag(tfa, TFA_SET_DEVICE, 1);
|
|
|
|
if (tfa->bus != 0) { /* non i2c */
|
|
#ifndef __KERNEL__
|
|
tfadsp_fw_start(tfa, next_profile, vstep);
|
|
#endif /* __KERNEL__ */
|
|
} else {
|
|
pr_debug("%s: device[%d] [%s] - tfa_run_speaker_boost profile=%d\n",
|
|
__func__, tfa->dev_idx,
|
|
tfa_cont_device_name(tfa->cnt, tfa->dev_idx),
|
|
next_profile);
|
|
|
|
tfa_state_before = tfa_dev_get_state(tfa);
|
|
|
|
/* Check if we need coldstart or ACS is set */
|
|
err = tfa_run_speaker_boost(tfa, forced, next_profile);
|
|
if (err != TFA98XX_ERROR_OK) {
|
|
mutex_unlock(&dev_lock);
|
|
goto tfa_dev_start_exit;
|
|
}
|
|
|
|
pr_info("%s:[%s] device:%s profile:%s\n",
|
|
__func__, (tfa->is_cold) ? "cold" : "warm",
|
|
tfa_cont_device_name(tfa->cnt, tfa->dev_idx),
|
|
tfa_cont_profile_name(tfa->cnt, tfa->dev_idx,
|
|
next_profile));
|
|
|
|
/* 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 */
|
|
tfa_state = tfa_dev_get_state(tfa);
|
|
if (tfa_state == TFA_STATE_OPERATING) {
|
|
pr_info("%s: skip setting state to OPERATING (%d)\n",
|
|
__func__, tfa_state);
|
|
if (tfa_state_before == TFA_STATE_OPERATING) {
|
|
pr_debug("%s: device already active\n",
|
|
__func__);
|
|
} else {
|
|
/* at last device only: to skip mute */
|
|
if (tfa_count_status_flag(tfa, TFA_SET_DEVICE)
|
|
>= tfa->active_count) {
|
|
pr_debug("%s: skip MUTE at the last device\n",
|
|
__func__);
|
|
} else {
|
|
pr_debug("%s: set MUTE until all are ready\n",
|
|
__func__);
|
|
tfa_dev_set_state(tfa,
|
|
TFA_STATE_MUTE, 0);
|
|
}
|
|
}
|
|
} else {
|
|
tfa_dev_set_state(tfa,
|
|
TFA_STATE_OPERATING | TFA_STATE_MUTE,
|
|
(cal_profile >= 0) ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&dev_lock);
|
|
|
|
if (cal_profile >= 0) {
|
|
if (tfa->is_probus_device) {
|
|
/* skip as profile is restored by tfa_wait_cal */
|
|
goto tfa_dev_start_exit;
|
|
} else {
|
|
#if defined(CHECK_CALIBRATION_DONE_MANUALLY)
|
|
/* tfa_wait_cal for DSP */
|
|
err = tfa_run_wait_calibration
|
|
(tfa, &calibration_done);
|
|
if (err != TFA98XX_ERROR_OK
|
|
|| calibration_done == 0) {
|
|
pr_err("%s: calibration is not done; stop processing\n",
|
|
__func__);
|
|
goto tfa_dev_start_exit;
|
|
}
|
|
#endif /* CHECK_CALIBRATION_DONE_MANUALLY */
|
|
|
|
err = tfa_process_re25(tfa);
|
|
}
|
|
}
|
|
|
|
/* Profile switching in call */
|
|
mutex_lock(&dev_lock);
|
|
err = (enum tfa98xx_error)
|
|
tfa_dev_switch_profile(tfa, next_profile, vstep);
|
|
mutex_unlock(&dev_lock);
|
|
if (err != TFA98XX_ERROR_OK)
|
|
goto tfa_dev_start_exit;
|
|
|
|
#if (defined(USE_TFA9892) || defined(USE_TFA9888))
|
|
/* 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");
|
|
}
|
|
#endif
|
|
|
|
#if defined(TFA_PAUSE_CONTROL)
|
|
tfa->pause_state = 0;
|
|
#endif
|
|
|
|
tfa_dev_start_exit:
|
|
show_current_state(tfa);
|
|
|
|
/* set ready for next action, once the current one completes */
|
|
mutex_lock(&dev_lock);
|
|
if (tfa_count_status_flag(tfa, TFA_SET_DEVICE) == tfa->active_count)
|
|
/* reset counter */
|
|
tfa_set_status_flag(tfa, TFA_SET_DEVICE, -1);
|
|
mutex_unlock(&dev_lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
enum tfa_error tfa_dev_switch_profile(struct tfa_device *tfa,
|
|
int next_profile, int vstep)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
int active_profile = -1;
|
|
char prof_name[MAX_CONTROL_NAME] = {0};
|
|
|
|
#if defined(TFA_USE_WAITQUEUE_SEQ)
|
|
if (tfa->ext_dsp == 1) {
|
|
int rc;
|
|
|
|
pr_info("%s: wait until set_calibration\n",
|
|
__func__);
|
|
|
|
/* wait until done for last device */
|
|
rc = wait_event_interruptible_timeout(tfa->waitq_seq,
|
|
(tfa_count_status_flag(tfa, TFA_SET_DEVICE)
|
|
== tfa->active_count) ? 1 : 0,
|
|
msecs_to_jiffies(TFA98XX_API_WAITCAL_NTRIES * 120));
|
|
|
|
pr_info("%s: waken up at set_calibration\n",
|
|
__func__);
|
|
}
|
|
#endif
|
|
|
|
active_profile = tfa_dev_get_swprof(tfa);
|
|
pr_info("%s: profile (active %d; next %d)\n",
|
|
__func__, active_profile, next_profile);
|
|
|
|
/* Profile switching */
|
|
if (next_profile != active_profile && active_profile >= 0) {
|
|
pr_debug("%s: switch profile from %d to %d\n",
|
|
__func__, active_profile, next_profile);
|
|
|
|
/* at initial device only: write files only once */
|
|
if (tfa_count_status_flag(tfa, TFA_SET_DEVICE) == 1
|
|
&& tfa_count_status_flag(tfa, TFA_SET_CONFIG) == 0)
|
|
tfa->is_bypass = 1; /* reset beforehand */
|
|
|
|
err = tfa_cont_write_profile(tfa, next_profile, vstep);
|
|
if (err != TFA98XX_ERROR_OK)
|
|
return err;
|
|
}
|
|
|
|
/* If the profile contains the .standby suffix go
|
|
* to powerdown else we should be in operating state
|
|
*/
|
|
strlcpy(prof_name, tfa_cont_profile_name(tfa->cnt,
|
|
tfa->dev_idx, next_profile), MAX_CONTROL_NAME);
|
|
if (strnstr(prof_name, ".standby", strlen(prof_name)) != NULL) {
|
|
tfa_dev_set_swprof(tfa, (unsigned short)next_profile);
|
|
tfa_dev_set_swvstep(tfa, (unsigned short)vstep);
|
|
|
|
pr_info("%s: skip switching dev %d for standby profile\n",
|
|
__func__, tfa->dev_idx);
|
|
|
|
return err;
|
|
} else if (TFA_GET_BF(tfa, PWDN) != 0) {
|
|
err = tfa98xx_powerdown(tfa, 0);
|
|
}
|
|
|
|
err = show_current_state(tfa);
|
|
|
|
tfa->vstep = tfa_dev_get_swvstep(tfa);
|
|
if ((TFA_GET_BF(tfa, CFE) != 0)
|
|
&& (vstep != tfa->vstep) && (vstep != -1)) {
|
|
err = tfa_cont_write_files_vstep(tfa, next_profile, vstep);
|
|
if (err != TFA98XX_ERROR_OK)
|
|
return err;
|
|
}
|
|
|
|
/* Always search and apply filters after a startup */
|
|
err = tfa_set_filters(tfa, next_profile);
|
|
if (err != TFA98XX_ERROR_OK)
|
|
return err;
|
|
|
|
tfa_dev_set_swprof(tfa, (unsigned short)next_profile);
|
|
tfa_dev_set_swvstep(tfa, (unsigned short)vstep);
|
|
|
|
return err;
|
|
}
|
|
|
|
enum tfa_error tfa_dev_stop(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
|
|
pr_debug("Stopping device [%s]\n",
|
|
tfa_cont_device_name(tfa->cnt, tfa->dev_idx));
|
|
|
|
/* mute */
|
|
tfa_run_mute(tfa);
|
|
|
|
#if defined(TFA_WAIT_CAL_IN_WORKQUEUE)
|
|
/* cancel other pending wait_cal works */
|
|
cancel_delayed_work(&tfa->wait_cal_work);
|
|
#endif
|
|
|
|
/* 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(tfa, 1);
|
|
if (err != TFA98XX_ERROR_OK)
|
|
return err;
|
|
|
|
/* disable I2S output on TFA1 devices without TDM */
|
|
err = tfa98xx_aec_output(tfa, 0);
|
|
|
|
/* CHECK: only once after buffering fully */
|
|
/* at last device only: to flush buffer */
|
|
if (tfa->ext_dsp == 1) {
|
|
if (tfa_count_status_flag(tfa, TFA_SET_DEVICE)
|
|
== tfa->active_count
|
|
|| tfa_count_status_flag(tfa, TFA_SET_CONFIG) > 0) {
|
|
/* flush message buffer */
|
|
pr_debug("%s: flush buffer in blob, at stop\n",
|
|
__func__);
|
|
err = tfa_tib_dsp_msgmulti(tfa, -2, NULL);
|
|
}
|
|
tfa->is_bypass = 0; /* reset at stop */
|
|
}
|
|
|
|
#if defined(TFA_PAUSE_CONTROL)
|
|
tfa->pause_state = 1;
|
|
#endif
|
|
|
|
if (tfa98xx_count_active_stream(BIT_PSTREAM) == 0
|
|
&& tfa98xx_count_active_stream(BIT_CSTREAM) == 0) {
|
|
pr_info("%s: all stopped: no active stream\n",
|
|
__func__);
|
|
/* reset counters */
|
|
tfa_set_status_flag(tfa, TFA_SET_DEVICE, -1);
|
|
tfa_set_status_flag(tfa, TFA_SET_CONFIG, -1);
|
|
/* reset cal data */
|
|
dsp_cal_value[0] = dsp_cal_value[1] = -1;
|
|
|
|
tfa_reset_active_handle(tfa);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
#if defined(TFA_CHANGE_PCM_FORMAT)
|
|
enum tfa_error tfa_dev_config_pcm_format(struct tfa_device *tfa,
|
|
int ndev, int hw_rate, int sample_size, int slot_size)
|
|
{
|
|
pr_info("%s: ndev %d, sample %d bits, slot %d bits\n",
|
|
__func__, ndev, sample_size, slot_size);
|
|
|
|
tfa->tdm_config.ssize = sample_size - 1;
|
|
tfa->tdm_config.slln = slot_size - 1;
|
|
tfa->tdm_config.srcmap = 0xff;
|
|
|
|
if (sample_size > slot_size)
|
|
goto config_pcm_format_error_exit;
|
|
|
|
tfa->tdm_config.audfs = tfa98xx_get_fssel(hw_rate);
|
|
if (tfa->tdm_config.audfs < 0) {
|
|
/* set the current sampling rate in use */
|
|
tfa->tdm_config.audfs = TFA_GET_BF(tfa, AUDFS);
|
|
goto config_pcm_format_error_exit;
|
|
}
|
|
|
|
/* read # of slots from container / device */
|
|
/* 2 slots per channel for V/I (non-compress mode) */
|
|
/* 1 slot per channel for V/I (compress mode) */
|
|
tfa->tdm_config.slots = tfa98xx_get_cnt_bitfield(tfa,
|
|
TFA7x_FAM(tfa, TDMSLOTS));
|
|
pr_info("%s: number of slots %d\n",
|
|
__func__, tfa->tdm_config.slots + 1);
|
|
|
|
switch (slot_size) {
|
|
case 16:
|
|
/* compress mode should have 32-bit slot */
|
|
if (ndev > 1 && tfa->is_probus_device
|
|
&& tfa->tdm_config.slots == ndev - 1) /* compr */
|
|
goto config_pcm_format_error_exit;
|
|
|
|
switch (tfa->tdm_config.slots) {
|
|
case 1: /* 2-slot */
|
|
tfa->tdm_config.nbck = 0; /* 32 fs */
|
|
break;
|
|
case 3: /* 4-slot */
|
|
tfa->tdm_config.nbck = 2; /* 64 fs */
|
|
break;
|
|
default:
|
|
goto config_pcm_format_error_exit;
|
|
}
|
|
break;
|
|
case 24:
|
|
/* compress mode should have 32-bit slot */
|
|
if (ndev > 1 && tfa->is_probus_device
|
|
&& tfa->tdm_config.slots == ndev - 1) /* compr */
|
|
goto config_pcm_format_error_exit;
|
|
|
|
switch (tfa->tdm_config.slots) {
|
|
case 1: /* 2-slot */
|
|
tfa->tdm_config.nbck = 1; /* 48 fs */
|
|
break;
|
|
case 3: /* 4-slot */
|
|
tfa->tdm_config.nbck = 3; /* 96 fs */
|
|
break;
|
|
default:
|
|
goto config_pcm_format_error_exit;
|
|
}
|
|
break;
|
|
case 32:
|
|
switch (tfa->tdm_config.slots) {
|
|
case 0: /* 1-slot */
|
|
tfa->tdm_config.nbck = 0; /* 32 fs */
|
|
break;
|
|
case 1: /* 2-slot */
|
|
tfa->tdm_config.nbck = 2; /* 64 fs */
|
|
break;
|
|
case 3: /* 4-slot */
|
|
tfa->tdm_config.nbck = 4; /* 128 fs */
|
|
break;
|
|
default:
|
|
goto config_pcm_format_error_exit;
|
|
}
|
|
|
|
if (!tfa->is_probus_device) {
|
|
tfa->tdm_config.ssize = (sample_size > 24)
|
|
? 23 : tfa->tdm_config.ssize; /* CF */
|
|
break;
|
|
}
|
|
|
|
if (tfa->tdm_config.slots == ndev - 1) { /* compr */
|
|
tfa->tdm_config.ssize = 31; /* full 32-bit for V/I */
|
|
tfa->tdm_config.srcmap = 3;
|
|
}
|
|
break;
|
|
default:
|
|
goto config_pcm_format_error_exit;
|
|
}
|
|
|
|
if (tfa->tdm_config.srcmap == 0xff) {
|
|
/* non-compress mode requires 2 x channels for slots */
|
|
if (tfa->is_probus_device
|
|
&& tfa->tdm_config.slots != ndev * 2 - 1)
|
|
goto config_pcm_format_error_exit;
|
|
|
|
switch (sample_size) {
|
|
case 16:
|
|
tfa->tdm_config.srcmap = 2; /* 16-bit */
|
|
break;
|
|
case 24:
|
|
tfa->tdm_config.srcmap = 1; /* 24-bit */
|
|
break;
|
|
case 32:
|
|
tfa->tdm_config.srcmap = 0; /* 32-bit */
|
|
break;
|
|
default:
|
|
goto config_pcm_format_error_exit;
|
|
}
|
|
}
|
|
|
|
pr_info("%s: nbck %u, slln %u, ssize %u, slots %u, srcmap %u\n",
|
|
__func__, tfa->tdm_config.nbck,
|
|
tfa->tdm_config.slln, tfa->tdm_config.ssize,
|
|
tfa->tdm_config.slots, tfa->tdm_config.srcmap);
|
|
|
|
return tfa_error_ok;
|
|
|
|
config_pcm_format_error_exit:
|
|
pr_err("%s: invalid configuration\n", __func__);
|
|
tfa->tdm_config.ssize = 0;
|
|
tfa->tdm_config.slln = 0;
|
|
tfa->tdm_config.srcmap = 0xff;
|
|
|
|
return tfa_error_bad_param;
|
|
}
|
|
#endif /* TFA_CHANGE_PCM_FORMAT */
|
|
|
|
/*
|
|
* int registers and coldboot dsp
|
|
*/
|
|
int tfa_reset(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(tfa, 0);
|
|
if (err)
|
|
return err;
|
|
err = tfa_cf_powerup(tfa);
|
|
if (err) {
|
|
PRINT_ASSERT(err);
|
|
return err;
|
|
}
|
|
err = tfa_run_coldboot(tfa, 1);
|
|
if (err) {
|
|
PRINT_ASSERT(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 except in powerdown state
|
|
*/
|
|
if (tfa->is_probus_device && state != TFA_STATE_POWERDOWN) {
|
|
err = TFA_SET_BF_VOLATILE(tfa, AMPE, 0);
|
|
if (err)
|
|
return err;
|
|
err = tfa98xx_powerdown(tfa, 1);
|
|
if (err) {
|
|
PRINT_ASSERT(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 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(tfa, 0);
|
|
if (err) {
|
|
PRINT_ASSERT(err);
|
|
return err;
|
|
}
|
|
|
|
/* For clock */
|
|
err = tfa_cf_powerup(tfa);
|
|
if (err) {
|
|
PRINT_ASSERT(err);
|
|
return err;
|
|
}
|
|
|
|
/* Force cold boot */
|
|
err = tfa_run_coldboot(tfa, 1); /* Set ACS */
|
|
if (err) {
|
|
PRINT_ASSERT(err);
|
|
return err;
|
|
}
|
|
|
|
/* Set PWDN = 1, set 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 defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
state = tfa_get_bf(tfa,
|
|
TFA9894N2_BF_MANSTATE);
|
|
else
|
|
state = TFA_GET_BF(tfa, MANSTATE);
|
|
#else
|
|
state = TFA_GET_BF(tfa, MANSTATE);
|
|
#endif
|
|
if (state < 0)
|
|
return err;
|
|
|
|
/* Check for MANSTATE=Powerdown (0) */
|
|
if (state == 0)
|
|
break;
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
}
|
|
|
|
/* 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(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(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(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
int calibrate_done, i, spkr_count = 0;
|
|
int tries = 0;
|
|
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
if (tfa->vval_active) {
|
|
pr_info("%s: skip processing data when V validation is running\n",
|
|
__func__);
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
error = tfa_supported_speakers(tfa, &spkr_count);
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
pr_err("error in checking supported speakers\n");
|
|
return error;
|
|
}
|
|
|
|
if (tfa_dev_mtp_get(tfa, TFA_MTP_OTC)
|
|
&& tfa_dev_mtp_get(tfa, TFA_MTP_EX) != 0) {
|
|
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);
|
|
}
|
|
|
|
if (tfa->mohm[0] != 0)
|
|
return error;
|
|
}
|
|
|
|
pr_debug("Getting calibration values from Speakerboost\n");
|
|
|
|
/* Make sure the calibrate_done 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
|
|
* calibrate_done = 0 means "calibrating",
|
|
* calibrate_done = -1 (or 0xffffff) means "fails"
|
|
* calibrate_done = 1 means calibration done
|
|
*/
|
|
calibrate_done = 0;
|
|
while ((calibrate_done != 1)
|
|
&& (tries < TFA98XX_API_WAITRESULT_NTRIES)) {
|
|
error = mem_read(tfa,
|
|
TFA_FW_XMEM_CALIBRATION_DONE,
|
|
1, &calibrate_done);
|
|
if (calibrate_done == 1)
|
|
break;
|
|
tries++;
|
|
}
|
|
|
|
if (calibrate_done != 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_STATE_TIMED_OUT;
|
|
}
|
|
}
|
|
|
|
error = tfa_process_re25(tfa);
|
|
|
|
return error;
|
|
}
|
|
|
|
static enum tfa98xx_error tfa_process_re25(struct tfa_device *tfa)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
enum tfa_error ret = tfa_error_ok;
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
unsigned char bytes[(1 + 2) * 3] = {0};
|
|
#else
|
|
unsigned char bytes[2 * 3] = {0};
|
|
#endif /* TFA_CUSTOM_FORMAT_AT_RESPONSE */
|
|
int data[2];
|
|
int nr_bytes, i, spkr_count = 0, cal_idx = 0;
|
|
int scaled_data;
|
|
unsigned int channel;
|
|
#if defined(WRITE_CALIBRATION_DATA_TO_MTP)
|
|
int tries = 0;
|
|
int readback = -1;
|
|
#endif
|
|
|
|
error = tfa_supported_speakers(tfa, &spkr_count);
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
pr_err("error in checking supported speakers\n");
|
|
return error;
|
|
}
|
|
|
|
/* SoftDSP interface differs from hw-dsp interfaces */
|
|
if (tfa->is_probus_device && tfa->dev_count > 1)
|
|
spkr_count = tfa->dev_count;
|
|
|
|
pr_info("%s: read SB_PARAM_GET_RE25C\n", __func__);
|
|
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
nr_bytes = (1 + spkr_count) * 3;
|
|
#else
|
|
nr_bytes = spkr_count * 3;
|
|
#endif /* TFA_CUSTOM_FORMAT_AT_RESPONSE */
|
|
channel = tfa98xx_get_cnt_bitfield(tfa,
|
|
TFA7x_FAM(tfa, TDMSPKS)) % MAX_CHANNELS;
|
|
error = tfa_dsp_cmd_id_write_read(tfa,
|
|
MODULE_SPEAKERBOOST, SB_PARAM_GET_RE25C, nr_bytes, bytes);
|
|
if (error == TFA98XX_ERROR_OK) {
|
|
tfa98xx_convert_bytes2data(nr_bytes, bytes, data);
|
|
|
|
pr_debug("%s: RE25C - spkr_count %d, dev %d, channel %d\n",
|
|
__func__, spkr_count, tfa->dev_idx, channel);
|
|
pr_debug("%s: RE25C - data[0]=%d - data[1]=%d\n",
|
|
__func__, data[0], data[1]);
|
|
|
|
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;
|
|
if (tfa->is_probus_device)
|
|
cal_idx = 0;
|
|
|
|
/* signed data has a limit of 30 Ohm */
|
|
scaled_data = data[channel];
|
|
|
|
tfa->mohm[cal_idx]
|
|
= TFA_ReZ_FP_INT(scaled_data,
|
|
TFA_FW_ReZ_SHIFT) * 1000
|
|
+ TFA_ReZ_FP_FRAC(scaled_data,
|
|
TFA_FW_ReZ_SHIFT);
|
|
}
|
|
|
|
pr_info("%s: %d mOhms\n", __func__, tfa->mohm[cal_idx]);
|
|
|
|
/* special case for stereo calibration, from SB 3.5 PRC1 */
|
|
if (tfa->mohm[cal_idx] == -128000) {
|
|
pr_err("%s: wrong calibration! (damaged speaker)\n",
|
|
__func__);
|
|
tfa->spkr_damaged = 1;
|
|
}
|
|
} else {
|
|
pr_err("%s: tfa_dsp_cmd_id_write_read is failed, err=%d\n",
|
|
__func__, error);
|
|
|
|
for (i = 0; i < spkr_count; i++) {
|
|
cal_idx = i;
|
|
if (tfa->is_probus_device)
|
|
cal_idx = 0;
|
|
|
|
tfa->mohm[cal_idx] = -1;
|
|
}
|
|
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
#if defined(CHECK_CALIBRATION_DATA_RANGE)
|
|
error = tfa_calibration_range_check(tfa,
|
|
channel, tfa->mohm[cal_idx]);
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
pr_err("%s: calibration data is out of range: device %d\n",
|
|
__func__, tfa->dev_idx);
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
}
|
|
#endif /* CHECK_CALIBRATION_DATA_RANGE */
|
|
|
|
#if defined(WRITE_CALIBRATION_DATA_TO_MTP)
|
|
if (!tfa->is_probus_device)
|
|
return error;
|
|
|
|
/*
|
|
* if (tfa_dev_mtp_get(tfa, TFA_MTP_OTC) != 0) {
|
|
* pr_debug("%s: skip writing calibration data to MTP\n",
|
|
* __func__);
|
|
* return error;
|
|
* }
|
|
*/
|
|
/* store calibration data to MTP */
|
|
ret = tfa_dev_mtp_set(tfa, TFA_MTP_OTC, 1);
|
|
if (ret != tfa_error_ok)
|
|
pr_debug("%s: error in setting MTPOTC\n",
|
|
__func__);
|
|
|
|
while (++tries < TFA98XX_API_REWRTIE_MTP_NTRIES) {
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
|
|
/* set RE25 in shadow regiser */
|
|
ret = tfa_dev_mtp_set(tfa,
|
|
TFA_MTP_RE25, tfa->mohm[cal_idx]);
|
|
if (ret != tfa_error_ok) {
|
|
pr_err("%s: writing calibration data failed to MTP, device %d err (%d)\n",
|
|
__func__, tfa->dev_idx, ret);
|
|
return TFA98XX_ERROR_RPC_CALIB_FAILED;
|
|
}
|
|
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
|
|
readback = tfa_dev_mtp_get(tfa, TFA_MTP_RE25);
|
|
if (readback < 0) {
|
|
pr_err("%s: reading calibration data back failed from MTP, device %d readback (%d)\n",
|
|
__func__, tfa->dev_idx, readback);
|
|
return TFA98XX_ERROR_RPC_CALIB_FAILED;
|
|
}
|
|
pr_info("%s: readback from MTP - %d mOhms\n",
|
|
__func__, readback);
|
|
#if defined(CHECK_CALIBRATION_DATA_RANGE)
|
|
error = tfa_calibration_range_check(tfa,
|
|
channel, readback);
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
pr_err("%s: calibration data is out of range: device %d (to rewrite)\n",
|
|
__func__, tfa->dev_idx);
|
|
continue;
|
|
}
|
|
#endif /* CHECK_CALIBRATION_DATA_RANGE */
|
|
|
|
if (readback != tfa->mohm[cal_idx]) {
|
|
pr_err("%s: calibration data was not written to MTP, device %d (%d != %d)\n",
|
|
__func__, tfa->dev_idx,
|
|
tfa->mohm[cal_idx], readback);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (tries >= TFA98XX_API_REWRTIE_MTP_NTRIES) {
|
|
pr_err("%s: writing calibration data timed out, device %d\n",
|
|
__func__, tfa->dev_idx);
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
/* set MTPEX */
|
|
tries = 0;
|
|
while (++tries < TFA98XX_API_REWRTIE_MTP_NTRIES) {
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
|
|
ret = tfa_dev_mtp_set(tfa, TFA_MTP_EX, 1);
|
|
if (ret != tfa_error_ok) {
|
|
pr_err("%s: setting MPTEX failed, device %d err (%d)\n",
|
|
__func__, tfa->dev_idx, ret);
|
|
continue;
|
|
}
|
|
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
|
|
readback = tfa_dev_mtp_get(tfa, TFA_MTP_EX);
|
|
if (readback < 0) {
|
|
pr_err("%s: reading MTPEX back failed from MTP, device %d readback (%d)\n",
|
|
__func__, tfa->dev_idx, readback);
|
|
return TFA98XX_ERROR_RPC_CALIB_FAILED;
|
|
}
|
|
|
|
if (readback != 1) {
|
|
pr_err("%s: setting MTPEX failed, device %d (readback %d)\n",
|
|
__func__, tfa->dev_idx, readback);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (tries >= TFA98XX_API_REWRTIE_MTP_NTRIES) {
|
|
pr_err("%s: setting MTPEX timed out, device %d\n",
|
|
__func__, tfa->dev_idx);
|
|
return TFA98XX_ERROR_BAD_PARAMETER;
|
|
}
|
|
#endif /* WRITE_CALIBRATION_DATA_TO_MTP */
|
|
|
|
return error;
|
|
}
|
|
|
|
/* start count from 1, 0 is invalid */
|
|
int tfa_dev_get_swprof(struct tfa_device *tfa)
|
|
{
|
|
if (!tfa->dev_ops.get_swprof)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
return (tfa->dev_ops.get_swprof)(tfa);
|
|
}
|
|
|
|
int tfa_dev_set_swprof(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
if (!tfa->dev_ops.set_swprof)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
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)
|
|
{
|
|
if (!tfa->dev_ops.get_swvstep)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
return (tfa->dev_ops.get_swvstep)(tfa);
|
|
}
|
|
|
|
int tfa_dev_set_swvstep(struct tfa_device *tfa, unsigned short new_value)
|
|
{
|
|
if (!tfa->dev_ops.set_swvstep)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
return (tfa->dev_ops.set_swvstep)(tfa, new_value + 1);
|
|
}
|
|
|
|
/*
|
|
* function overload for MTPB
|
|
*/
|
|
int tfa_dev_get_mtpb(struct tfa_device *tfa)
|
|
{
|
|
if (!tfa->dev_ops.get_mtpb)
|
|
return TFA98XX_ERROR_NOT_IMPLEMENTED;
|
|
|
|
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 !defined(TFA_USE_MANSTATE_TO_CHECK_COLD)
|
|
if (tfa->ext_dsp == 2)
|
|
value = 0; /* warm */
|
|
else /* no dsp or cold */
|
|
value = 1; /* cold */
|
|
#else
|
|
int tfa_state;
|
|
|
|
tfa_state = tfa_dev_get_state(tfa);
|
|
if (tfa_state == TFA_STATE_OPERATING)
|
|
value = 0; /* warm */
|
|
else
|
|
value = 1; /* cold */
|
|
#endif
|
|
} else {
|
|
value = (TFA_GET_BF(tfa, MANSCONF) == 0);
|
|
}
|
|
} else {
|
|
value = TFA_GET_BF(tfa, ACS);
|
|
}
|
|
|
|
#if defined(TFA_CHANGE_PCM_FORMAT)
|
|
/* set cold by force if PCM format is changed */
|
|
value |= tfa_is_pcm_format_changed(tfa);
|
|
#endif
|
|
|
|
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_is_cold_amp(struct tfa_device *tfa)
|
|
{
|
|
int value;
|
|
|
|
if (tfa->tfa_family == 2)
|
|
/*
|
|
* for non-dsp device reading MANSCONF
|
|
* (src_set_configured) is a way
|
|
* to check for cold boot status
|
|
*/
|
|
value = (TFA_GET_BF(tfa, MANSCONF) == 0);
|
|
else
|
|
value = TFA_GET_BF(tfa, ACS);
|
|
|
|
return value;
|
|
}
|
|
|
|
int tfa_count_status_flag(struct tfa_device *tfa, int type)
|
|
{
|
|
struct tfa_device *ntfa = NULL;
|
|
int i, status, value = 0;
|
|
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
switch (type) {
|
|
case TFA_SET_DEVICE:
|
|
status = (ntfa->set_device) ? 1 : 0;
|
|
break;
|
|
case TFA_SET_CONFIG:
|
|
status = (ntfa->set_config) ? 1 : 0;
|
|
break;
|
|
default:
|
|
status = 0;
|
|
break;
|
|
}
|
|
|
|
value += status;
|
|
}
|
|
|
|
if (tfa->verbose)
|
|
pr_debug("%s: count (type %d): %d\n",
|
|
__func__, type, value);
|
|
|
|
return value;
|
|
}
|
|
|
|
void tfa_set_status_flag(struct tfa_device *tfa, int type, int value)
|
|
{
|
|
struct tfa_device *ntfa = NULL;
|
|
int i;
|
|
|
|
if (value == 0 || value == 1) {
|
|
switch (type) {
|
|
case TFA_SET_DEVICE:
|
|
tfa->set_device = value;
|
|
break;
|
|
case TFA_SET_CONFIG:
|
|
tfa->set_config = value;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (tfa->verbose)
|
|
pr_debug("%s: set flag (type %d, dev %d) with %d\n",
|
|
__func__, type, tfa->dev_idx, value);
|
|
|
|
return;
|
|
}
|
|
|
|
/* reset all */
|
|
for (i = 0; i < tfa->dev_count; i++) {
|
|
ntfa = tfa98xx_get_tfa_device_from_index(i);
|
|
|
|
if (ntfa == NULL)
|
|
continue;
|
|
|
|
switch (type) {
|
|
case TFA_SET_DEVICE:
|
|
ntfa->set_device = 0;
|
|
break;
|
|
case TFA_SET_CONFIG:
|
|
ntfa->set_config = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tfa->verbose)
|
|
pr_debug("%s: reset flag (type %d) for all\n",
|
|
__func__, type);
|
|
}
|
|
|
|
#if defined(TFA_CHANGE_PCM_FORMAT)
|
|
int tfa_is_pcm_format_changed(struct tfa_device *tfa)
|
|
{
|
|
int value;
|
|
|
|
if (tfa->tdm_config.slln == 0
|
|
|| tfa->tdm_config.ssize == 0
|
|
|| tfa->tdm_config.srcmap == 0xff) {
|
|
pr_debug("%s: no valid config to update\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
switch (tfa->rev & 0xff) {
|
|
case 0x78:
|
|
case 0x74:
|
|
case 0x72:
|
|
case 0x94:
|
|
value = TFA7x_GET_BF(tfa, TDMNBCK);
|
|
if (value != tfa->tdm_config.nbck)
|
|
goto is_pcm_format_changed_exit;
|
|
value = TFA7x_GET_BF(tfa, AUDFS);
|
|
if (value != tfa->tdm_config.audfs)
|
|
goto is_pcm_format_changed_exit;
|
|
value = TFA7x_GET_BF(tfa, TDMSLLN);
|
|
if (value != tfa->tdm_config.slln)
|
|
goto is_pcm_format_changed_exit;
|
|
value = TFA7x_GET_BF(tfa, TDMSSIZE);
|
|
if (value != tfa->tdm_config.ssize)
|
|
goto is_pcm_format_changed_exit;
|
|
value = TFA7x_GET_BF(tfa, TDMSRCMAP);
|
|
if (value != tfa->tdm_config.srcmap)
|
|
goto is_pcm_format_changed_exit;
|
|
break;
|
|
default:
|
|
value = TFA_GET_BF(tfa, TDMNBCK);
|
|
if (value != tfa->tdm_config.nbck)
|
|
goto is_pcm_format_changed_exit;
|
|
value = TFA_GET_BF(tfa, AUDFS);
|
|
if (value != tfa->tdm_config.audfs)
|
|
goto is_pcm_format_changed_exit;
|
|
value = TFA_GET_BF(tfa, TDMSLLN);
|
|
if (value != tfa->tdm_config.slln)
|
|
goto is_pcm_format_changed_exit;
|
|
value = TFA_GET_BF(tfa, TDMSSIZE);
|
|
if (value != tfa->tdm_config.ssize)
|
|
goto is_pcm_format_changed_exit;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
|
|
is_pcm_format_changed_exit:
|
|
pr_info("%s: config is updated for overwriting (dev %d)\n",
|
|
__func__, tfa->dev_idx);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static const int fractdel_tbl[] = {
|
|
#if defined(USE_TFA9878)
|
|
-1, -1, -1, 59, -1, -1, 54, 50, 49,
|
|
#elif defined(USE_TFA9874)
|
|
-1, -1, -1, 61, -1, -1, 25, 23, 22,
|
|
#elif defined(USE_TFA9894)
|
|
-1, -1, -1, 61, -1, -1, 25, 21, 20,
|
|
#elif defined(USE_TFA9872)
|
|
-1, -1, -1, 56, -1, -1, -1, 41, 40,
|
|
#else
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
#endif
|
|
#if defined(TFA_NO_SND_FORMAT_CHECK)
|
|
/* out of range */
|
|
-1, -1, -1, -1, -1,
|
|
#endif
|
|
};
|
|
|
|
void tfa_overwrite_pcm_format(struct tfa_device *tfa)
|
|
{
|
|
int tdm_state;
|
|
int dirt = 0;
|
|
int nbck = 0, audfs = 0, slln = 0, ssize = 0, srcmap = -1;
|
|
static struct tfa98xx_tdm_format prev_config;
|
|
|
|
if (tfa->tdm_config.slln == 0
|
|
|| tfa->tdm_config.ssize == 0
|
|
|| tfa->tdm_config.srcmap == 0xff) {
|
|
pr_debug("%s: no valid config to update\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (tfa->tdm_config.nbck == prev_config.nbck
|
|
&& tfa->tdm_config.audfs == prev_config.audfs
|
|
&& tfa->tdm_config.slln == prev_config.slln
|
|
&& tfa->tdm_config.ssize == prev_config.ssize
|
|
&& tfa->tdm_config.slots == prev_config.slots
|
|
&& tfa->tdm_config.srcmap == prev_config.srcmap) {
|
|
pr_debug("%s: no change in config\n", __func__);
|
|
return;
|
|
}
|
|
|
|
/* disable for reconfig */
|
|
switch (tfa->rev & 0xff) {
|
|
case 0x78:
|
|
case 0x74:
|
|
case 0x94:
|
|
tdm_state = TFA7x_GET_BF(tfa, TDME);
|
|
if (tdm_state != 0)
|
|
TFA7x_SET_BF(tfa, TDME, 0);
|
|
break;
|
|
case 0x72:
|
|
default:
|
|
tdm_state = TFA_GET_BF(tfa, TDME);
|
|
if (tdm_state != 0)
|
|
TFA_SET_BF(tfa, TDME, 0);
|
|
break;
|
|
}
|
|
|
|
switch (tfa->rev & 0xff) {
|
|
case 0x78:
|
|
case 0x74:
|
|
case 0x72:
|
|
case 0x94:
|
|
nbck = TFA7x_GET_BF(tfa, TDMNBCK);
|
|
if (nbck != tfa->tdm_config.nbck) {
|
|
dirt = 1;
|
|
TFA7x_SET_BF(tfa, TDMNBCK,
|
|
tfa->tdm_config.nbck);
|
|
}
|
|
audfs = TFA7x_GET_BF(tfa, AUDFS);
|
|
if (audfs != tfa->tdm_config.audfs) {
|
|
dirt = 1;
|
|
TFA7x_SET_BF(tfa, AUDFS,
|
|
tfa->tdm_config.audfs);
|
|
if (fractdel_tbl[tfa->tdm_config.audfs] > 0)
|
|
TFA7x_SET_BF(tfa, FRACTDEL,
|
|
fractdel_tbl[tfa->tdm_config.audfs]);
|
|
}
|
|
slln = TFA7x_GET_BF(tfa, TDMSLLN);
|
|
if (slln != tfa->tdm_config.slln) {
|
|
dirt = 1;
|
|
TFA7x_SET_BF(tfa, TDMSLLN,
|
|
tfa->tdm_config.slln);
|
|
}
|
|
ssize = TFA7x_GET_BF(tfa, TDMSSIZE);
|
|
if (ssize != tfa->tdm_config.ssize) {
|
|
dirt = 1;
|
|
TFA7x_SET_BF(tfa, TDMSSIZE,
|
|
tfa->tdm_config.ssize);
|
|
}
|
|
srcmap = TFA7x_GET_BF(tfa, TDMSRCMAP);
|
|
if (srcmap != tfa->tdm_config.srcmap) {
|
|
dirt = 1;
|
|
TFA7x_SET_BF(tfa, TDMSRCMAP,
|
|
tfa->tdm_config.srcmap);
|
|
}
|
|
break;
|
|
default:
|
|
nbck = TFA_GET_BF(tfa, TDMNBCK);
|
|
if (nbck != tfa->tdm_config.nbck) {
|
|
dirt = 1;
|
|
TFA_SET_BF(tfa, TDMNBCK,
|
|
tfa->tdm_config.nbck);
|
|
}
|
|
audfs = TFA_GET_BF(tfa, AUDFS);
|
|
if (audfs != tfa->tdm_config.audfs) {
|
|
dirt = 1;
|
|
TFA_SET_BF(tfa, AUDFS,
|
|
tfa->tdm_config.audfs);
|
|
}
|
|
slln = TFA_GET_BF(tfa, TDMSLLN);
|
|
if (slln != tfa->tdm_config.slln) {
|
|
dirt = 1;
|
|
TFA_SET_BF(tfa, TDMSLLN,
|
|
tfa->tdm_config.slln);
|
|
}
|
|
ssize = TFA_GET_BF(tfa, TDMSSIZE);
|
|
if (ssize != tfa->tdm_config.ssize) {
|
|
dirt = 1;
|
|
TFA_SET_BF(tfa, TDMSSIZE,
|
|
tfa->tdm_config.ssize);
|
|
}
|
|
break;
|
|
}
|
|
|
|
pr_debug("%s: [original] nbck %u, audfs %u, slln %u, ssize %u srcmap %u\n",
|
|
__func__, nbck, audfs, slln, ssize, srcmap);
|
|
|
|
/* read back */
|
|
if (dirt) {
|
|
switch (tfa->rev & 0xff) {
|
|
case 0x78:
|
|
case 0x74:
|
|
case 0x72:
|
|
case 0x94:
|
|
nbck = TFA7x_GET_BF(tfa, TDMNBCK);
|
|
audfs = TFA7x_GET_BF(tfa, AUDFS);
|
|
slln = TFA7x_GET_BF(tfa, TDMSLLN);
|
|
ssize = TFA7x_GET_BF(tfa, TDMSSIZE);
|
|
srcmap = TFA7x_GET_BF(tfa, TDMSRCMAP);
|
|
break;
|
|
default:
|
|
nbck = TFA_GET_BF(tfa, TDMNBCK);
|
|
audfs = TFA_GET_BF(tfa, AUDFS);
|
|
slln = TFA_GET_BF(tfa, TDMSLLN);
|
|
ssize = TFA_GET_BF(tfa, TDMSSIZE);
|
|
break;
|
|
}
|
|
|
|
pr_info("%s: [changed] nbck %u, audfs %u, slln %u, ssize %u, srcmap %u\n",
|
|
__func__, nbck, audfs, slln, ssize, srcmap);
|
|
}
|
|
|
|
/* restore after config */
|
|
if (tdm_state != 0) {
|
|
switch (tfa->rev & 0xff) {
|
|
case 0x78:
|
|
case 0x74:
|
|
case 0x94:
|
|
TFA7x_SET_BF(tfa, TDME, 1);
|
|
break;
|
|
case 0x72:
|
|
default:
|
|
TFA_SET_BF(tfa, TDME, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
memcpy(&prev_config, &tfa->tdm_config,
|
|
sizeof(prev_config));
|
|
}
|
|
#endif /* TFA_CHANGE_PCM_FORMAT */
|
|
|
|
int tfa_cf_enabled(struct tfa_device *tfa)
|
|
{
|
|
int value;
|
|
|
|
/* For probus, 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] = MODULE_BIQUADFILTERBANK + 0x80;
|
|
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);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* (new_cost < old_cost) */
|
|
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] = MODULE_BIQUADFILTERBANK + 0x80;
|
|
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) {
|
|
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;
|
|
|
|
for (bq = 0; bq < eq_biquads[eq]; bq++) {
|
|
if (eq_biquad_mask[eq] & (1 << bq)) {
|
|
uint8_t *bq2
|
|
= &eq2[bq * BQ_SIZE];
|
|
|
|
/* cmd id */
|
|
msg[0] = 0x00;
|
|
msg[1] = MODULE_BIQUADFILTERBANK + 0x80;
|
|
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);
|
|
|
|
if (err) {
|
|
kmem_cache_free
|
|
(tfa->cachep, msg);
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
kmem_cache_free(tfa->cachep, msg);
|
|
}
|
|
eq_offset += eq_biquads[eq];
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/* fill context info */
|
|
int tfa_dev_probe(int resp_addr, struct tfa_device *tfa)
|
|
{
|
|
uint16_t rev;
|
|
|
|
tfa->resp_address = (unsigned char)resp_addr;
|
|
|
|
/* read revid via low level hal, register 3 */
|
|
if (tfa98xx_read_register16(tfa, 0x03, &rev)
|
|
!= TFA98XX_ERROR_OK) {
|
|
pr_debug("Error: Unable to read revid from responder:0x%02x\n",
|
|
resp_addr);
|
|
return TFA_ERROR;
|
|
}
|
|
|
|
tfa->rev = rev;
|
|
tfa->dev_idx = -1;
|
|
tfa->state = TFA_STATE_UNKNOWN;
|
|
tfa->p_reg_info = NULL;
|
|
|
|
#if defined(TFA_MIXER_ON_DEVICE)
|
|
tfa->set_active = -1; /* undefined by default */
|
|
#endif
|
|
#if defined(TFA_MUTE_CONTROL)
|
|
tfa->mute_state = 0; /* unmute by default */
|
|
#endif
|
|
#if defined(TFA_PAUSE_CONTROL)
|
|
tfa->pause_state = 0; /* not paused by default */
|
|
#endif
|
|
#if defined(TFA_TDMSPKG_CONTROL)
|
|
tfa->spkgain = -1; /* undefined */
|
|
#endif
|
|
|
|
tfa_set_query_info(tfa);
|
|
|
|
tfa->in_use = 1;
|
|
tfa->is_calibrating = 0;
|
|
#if defined(TFA_DISABLE_AUTO_CAL)
|
|
tfa->disable_auto_cal = 1;
|
|
#endif
|
|
#if defined(TFA_USE_TFAVVAL_NODE)
|
|
tfa->vval_active = 0;
|
|
tfa->vval_result = VVAL_UNTESTED;
|
|
#endif
|
|
|
|
tfa->dev_count = tfa->cnt->ndev;
|
|
|
|
tfa->set_device = 0;
|
|
tfa->set_config = 0;
|
|
|
|
tfa->dev_idx = tfa_cont_get_idx(tfa);
|
|
if (tfa->dev_idx < 0)
|
|
return TFA_ERROR;
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum tfa_error tfa_dev_set_state(struct tfa_device *tfa,
|
|
enum tfa_state state, int is_calibration)
|
|
{
|
|
enum tfa98xx_error ret = TFA98XX_ERROR_OK;
|
|
int loop = 50, ready = 0;
|
|
int count;
|
|
|
|
pr_info("%s: [%d] state = 0x%02x\n",
|
|
__func__, tfa->dev_idx, state & 0xff);
|
|
|
|
/* 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 {
|
|
ret = tfa98xx_dsp_system_stable(tfa, &ready);
|
|
if (ret != TFA98XX_ERROR_OK)
|
|
return tfa_error_dsp;
|
|
if (ready)
|
|
break;
|
|
} while (loop--);
|
|
|
|
if ((!tfa->is_probus_device && is_calibration)
|
|
|| ((tfa->rev & 0xff) == 0x13)) {
|
|
/*
|
|
* Enable FAIM when clock is stable,
|
|
* to avoid MTP corruption
|
|
*/
|
|
ret = tfa98xx_faim_protect(tfa, 1);
|
|
if (tfa->verbose)
|
|
pr_debug("FAIM enabled (err %d).\n", ret);
|
|
}
|
|
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)
|
|
/* Coming from state 6 */
|
|
TFA_SET_BF_VOLATILE(tfa, SBSL, 1);
|
|
else
|
|
/* No SBSL for probus device, we set AMPE to 1 */
|
|
TFA_SET_BF(tfa, AMPE, 1);
|
|
|
|
/*
|
|
* Disable MTP clock to protect memory.
|
|
* However in case of calibration wait for DSP!
|
|
* (This should be case only during calibration).
|
|
*/
|
|
if (!tfa->is_probus_device && is_calibration) {
|
|
count = MTPEX_WAIT_NTRIES * 4;
|
|
if (TFA_GET_BF(tfa, MTPOTC) == 1
|
|
&& tfa->tfa_family == 2) {
|
|
/* Calibration takes a lot of time */
|
|
while ((TFA_GET_BF(tfa, MTPEX) != 1) && count) {
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
count--;
|
|
}
|
|
}
|
|
if (count <= 0)
|
|
pr_err("%s: MTPEX is not set - timeout\n",
|
|
__func__);
|
|
|
|
if ((tfa->rev & 0xff) == 0x13) {
|
|
ret = tfa98xx_faim_protect(tfa, 0);
|
|
if (tfa->verbose)
|
|
pr_debug("%s: FAIM disabled (err %d).\n",
|
|
__func__, ret);
|
|
}
|
|
}
|
|
|
|
/* Synchonize I/V delay on 96/97 at cold start */
|
|
if (tfa->sync_iv_delay) {
|
|
if (tfa->verbose)
|
|
pr_debug("%s: syncing I/V delay for %x\n",
|
|
__func__, (tfa->rev & 0xff));
|
|
|
|
/* wait for ACS to be cleared */
|
|
count = ACS_RESET_WAIT_NTRIES;
|
|
while ((TFA_GET_BF(tfa, ACS) == 1) &&
|
|
(count-- > 0))
|
|
msleep_interruptible(BUSLOAD_INTERVAL / 10);
|
|
|
|
tfa98xx_dsp_reset(tfa, 1);
|
|
tfa98xx_dsp_reset(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(tfa);
|
|
break;
|
|
default:
|
|
if (state & 0x0f)
|
|
return tfa_error_bad_param;
|
|
break;
|
|
}
|
|
|
|
/* state modifiers */
|
|
|
|
if (state & TFA_STATE_MUTE)
|
|
tfa98xx_set_mute(tfa, TFA98XX_MUTE_AMPLIFIER);
|
|
|
|
#if defined(TFA_MUTE_CONTROL)
|
|
if (state & TFA_STATE_UNMUTE) {
|
|
if (tfa->mute_state)
|
|
pr_info("%s: skip UNMUTE dev %d (by force)\n",
|
|
__func__, tfa->dev_idx);
|
|
else
|
|
tfa98xx_set_mute(tfa, TFA98XX_MUTE_OFF);
|
|
}
|
|
#else
|
|
if (state & TFA_STATE_UNMUTE)
|
|
tfa98xx_set_mute(tfa, TFA98XX_MUTE_OFF);
|
|
#endif
|
|
|
|
/* tfa->state = state; */ /* to correct with real state of device */
|
|
tfa->state = tfa_dev_get_state(tfa);
|
|
|
|
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 defined(USE_TFA9894N2)
|
|
if (is_94_N2_device(tfa))
|
|
manstate = tfa_get_bf(tfa, TFA9894N2_BF_MANSTATE);
|
|
else
|
|
manstate = TFA_GET_BF(tfa, MANSTATE);
|
|
#else
|
|
manstate = TFA_GET_BF(tfa, MANSTATE);
|
|
#endif
|
|
|
|
pr_debug("%s: [%d] manstate = %d\n",
|
|
__func__, tfa->dev_idx, 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:
|
|
tfa->state = TFA_STATE_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return tfa->state;
|
|
}
|
|
|
|
int tfa_dev_mtp_get(struct tfa_device *tfa, enum tfa_mtp item)
|
|
{
|
|
int value = 0;
|
|
|
|
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);
|
|
#if defined(USE_TFA9912)
|
|
else if ((tfa->rev & 0xff) == 0x13)
|
|
value = tfa_get_bf(tfa, TFA9912_BF_R25C);
|
|
#endif
|
|
else
|
|
value = TFA_GET_BF(tfa, R25C);
|
|
} else {
|
|
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;
|
|
default:
|
|
/* wrong item */
|
|
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;
|
|
|
|
switch (item) {
|
|
case TFA_MTP_OTC:
|
|
err = tfa98xx_set_mtp(tfa, (uint16_t)
|
|
(value << TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS),
|
|
TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK);
|
|
break;
|
|
case TFA_MTP_EX:
|
|
err = tfa98xx_set_mtp(tfa, (uint16_t)
|
|
(value << TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS),
|
|
TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK);
|
|
if (err == tfa_error_ok && value == 0)
|
|
tfa->reset_mtpex = 0;
|
|
break;
|
|
case TFA_MTP_RE25:
|
|
case TFA_MTP_RE25_PRIM:
|
|
if (tfa->tfa_family == 2) {
|
|
tfa98xx_key2(tfa, 0); /* unlock */
|
|
if ((tfa->rev & 0xff) == 0x88)
|
|
TFA_SET_BF(tfa, R25CL, (uint16_t)value);
|
|
else {
|
|
TFA_SET_BF(tfa, R25C, (uint16_t)value);
|
|
if (tfa->is_probus_device == 1
|
|
&& TFA_GET_BF(tfa, MTPOTC) == 1) {
|
|
pr_info("%s: write Re25 to MTP\n",
|
|
__func__);
|
|
tfa2_manual_mtp_cpy(tfa,
|
|
0xf4, value, 2);
|
|
} else
|
|
TFA_SET_BF(tfa, CIMTP, 1);
|
|
}
|
|
tfa98xx_key2(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;
|
|
default:
|
|
/* wrong item */
|
|
break;
|
|
}
|
|
|
|
if (err != tfa_error_ok)
|
|
pr_err("%s: error (%d) in setting MTP (item %d with %d)\n",
|
|
__func__, err, item, value);
|
|
|
|
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;
|
|
}
|
|
|
|
/* instance for TFA9874 / TFA9878 / TFA9894 */
|
|
#if defined(USE_TFA9874) || defined(USE_TFA9878) || defined(USE_TFA9894)
|
|
enum tfa98xx_error tfa7x_status(struct tfa_device *tfa)
|
|
{
|
|
int value;
|
|
uint16_t val;
|
|
int state, control;
|
|
char reg_state[50] = {0};
|
|
int idle_power = 0, low_power = 0, low_noise = 0;
|
|
|
|
/*
|
|
* check IC status bits: cold start
|
|
* and DSP watch dog bit to re init
|
|
*/
|
|
value = TFA7x_READ_REG(tfa, VDDS); /* STATUS_FLAGS0 */
|
|
if (value < 0)
|
|
return -value;
|
|
val = (uint16_t)value;
|
|
|
|
/* Check secondary errors */
|
|
if (!TFA7x_GET_BF_VALUE(tfa, CLKS, val)
|
|
|| !TFA7x_GET_BF_VALUE(tfa, UVDS, val)
|
|
|| !TFA7x_GET_BF_VALUE(tfa, OTDS, val)
|
|
|| !((tfa->daimap & TFA98XX_DAI_TDM)
|
|
|| TFA7x_GET_BF_VALUE(tfa, VDDS, val)))
|
|
pr_err("Misc errors detected: STATUS_FLAG0 = 0x%x\n", val);
|
|
|
|
value = TFA7x_READ_REG(tfa, PLLS); /* STATUS_FLAGS1 */
|
|
if (value < 0)
|
|
return -value;
|
|
val = (uint16_t)value;
|
|
|
|
snprintf(reg_state, 50, "device [%d]", tfa->dev_idx);
|
|
|
|
#if defined(USE_TFA9878)
|
|
state = TFA7x_GET_BF(tfa, LP0);
|
|
snprintf(reg_state + strlen(reg_state),
|
|
50 - strlen(reg_state), ", LP0 %d", state);
|
|
control = TFA7x_GET_BF(tfa, IPM);
|
|
if ((control == 0x0 || control == 0x3)
|
|
&& (state == 0x1))
|
|
idle_power = 1;
|
|
#endif /* USE_TFA9878 */
|
|
|
|
switch (tfa->rev & 0xff) {
|
|
case 0x78:
|
|
case 0x74:
|
|
case 0x72:
|
|
case 0x94:
|
|
state = TFA7x_GET_BF(tfa, LP1);
|
|
snprintf(reg_state + strlen(reg_state),
|
|
50 - strlen(reg_state), ", LP1 %d", state);
|
|
control = TFA7x_GET_BF(tfa, LPM1MODE);
|
|
if ((control == 0x0 || control == 0x3)
|
|
&& (state == 0x1))
|
|
low_power = 1;
|
|
state = TFA7x_GET_BF(tfa, LA);
|
|
snprintf(reg_state + strlen(reg_state),
|
|
50 - strlen(reg_state), ", LA %d", state);
|
|
control = TFA7x_GET_BF(tfa, LNMODE);
|
|
if ((control == 0x0)
|
|
&& (state == 0x1))
|
|
low_noise = 1;
|
|
break;
|
|
default:
|
|
/* neither TFA987x */
|
|
break;
|
|
}
|
|
pr_debug("%s: %s\n", __func__, reg_state);
|
|
|
|
if (!TFA7x_GET_BF_VALUE(tfa, SWS, val)) {
|
|
if (idle_power)
|
|
pr_info("%s: idle power disabled amplifier\n",
|
|
__func__);
|
|
else
|
|
pr_err("%s: ERROR: SWS\n", __func__);
|
|
}
|
|
if (!TFA7x_GET_BF_VALUE(tfa, PLLS, val))
|
|
pr_err("%s: ERROR: PLLS\n", __func__);
|
|
|
|
if ((tfa->daimap & TFA98XX_DAI_TDM) && (tfa->tfa_family == 2)) {
|
|
if (TFA7x_GET_BF(tfa, TDMERR)) {
|
|
if ((low_power || idle_power)
|
|
&& TFA7x_GET_BF(tfa, TDMSTAT) == 0x7)
|
|
pr_info("%s: low power disabled sensing block\n",
|
|
__func__);
|
|
else
|
|
pr_err("%s: TDM related errors: STATUS_FLAG0 = 0x%x, STATUS_FLAG4 = 0x%x\n",
|
|
__func__, TFA7x_READ_REG(tfa, TDMERR),
|
|
TFA7x_READ_REG(tfa, TDMSTAT));
|
|
}
|
|
if (TFA7x_GET_BF(tfa, TDMLUTER))
|
|
pr_err("%s: TDM related errors: STATUS_FLAG1 = 0x%x\n",
|
|
__func__, TFA7x_READ_REG(tfa, TDMLUTER));
|
|
}
|
|
|
|
value = TFA7x_READ_REG(tfa, OVDS); /* STATUS_FLAGS3 */
|
|
if (value < 0)
|
|
return -value;
|
|
val = (uint16_t)value;
|
|
|
|
if (!TFA7x_GET_BF_VALUE(tfa, OVDS, val))
|
|
pr_err("Misc errors detected: STATUS_FLAG3 = 0x%x\n", val);
|
|
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
#endif /* (USE_TFA9874) || (USE_TFA9878) || (USE_TFA9894) */
|
|
|
|
#if (defined(USE_TFA9891) || defined(USE_TFA9912))
|
|
int tfa_plop_noise_interrupt(struct tfa_device *tfa,
|
|
int profile, int vstep)
|
|
{
|
|
enum tfa98xx_error err;
|
|
int no_clk = 0;
|
|
char prof_name[MAX_CONTROL_NAME] = {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, 0);
|
|
|
|
/* Remove sticky bit by reading it once */
|
|
tfa_get_noclk(tfa);
|
|
|
|
/* This is only for SAAM on the 72.
|
|
* Since NOCLK interrupt is only enabled for 72
|
|
* it does not harm normal flow!
|
|
*/
|
|
strlcpy(prof_name, tfa_cont_profile_name(tfa->cnt,
|
|
tfa->dev_idx, profile), MAX_CONTROL_NAME);
|
|
if (strnstr(prof_name, ".saam", strlen(prof_name))) {
|
|
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)
|
|
{
|
|
/* FIXME: this 72 interrupt does not exist for 9912 */
|
|
const int irq_stclp0 = 36;
|
|
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);
|
|
}
|
|
}
|
|
#endif /* (USE_TFA9891) || (USE_TFA9912) */
|
|
|
|
int tfa_ext_event_handler(enum tfadsp_event_en tfadsp_event)
|
|
{
|
|
int dirt_flag = 0;
|
|
|
|
pr_info("%s: tfadsp event 0x%04x\n", __func__, tfadsp_event);
|
|
|
|
if (tfadsp_event & TFADSP_CMD_ACK) {
|
|
/* action for TFADSP_CMD_ACK */
|
|
dirt_flag = 1;
|
|
}
|
|
if (tfadsp_event & TFADSP_SOFT_MUTE_READY) {
|
|
/* action for TFADSP_SOFT_MUTE_READY */
|
|
dirt_flag = 1;
|
|
}
|
|
if (tfadsp_event & TFADSP_VOLUME_READY) {
|
|
/* action for TFADSP_VOLUME_READY */
|
|
dirt_flag = 1;
|
|
}
|
|
if (tfadsp_event & TFADSP_DAMAGED_SPEAKER) {
|
|
/* action for TFADSP_DAMAGED_SPEAKER */
|
|
dirt_flag = 1;
|
|
}
|
|
if (tfadsp_event & TFADSP_CALIBRATE_DONE) {
|
|
/* action for TFADSP_CALIBRATE_DONE */
|
|
dirt_flag = 1;
|
|
}
|
|
if (tfadsp_event & TFADSP_SPARSESIG_DETECTED) {
|
|
/* action for TFADSP_SPARSESIG_DETECTED */
|
|
dirt_flag = 1;
|
|
}
|
|
if (tfadsp_event & TFADSP_CMD_READY) {
|
|
/* action for TFADSP_CMD_READY */
|
|
dirt_flag = 1;
|
|
}
|
|
if (tfadsp_event & TFADSP_EXT_PWRUP) {
|
|
/* action for TFADSP_EXT_PWRUP */
|
|
dirt_flag = 1;
|
|
}
|
|
if (tfadsp_event & TFADSP_EXT_PWRDOWN) {
|
|
/* action for TFADSP_EXT_PWRDOWN */
|
|
dirt_flag = 1;
|
|
}
|
|
if (tfadsp_event & TFADSP_EXT_PWRDOWN) {
|
|
/* action for TFADSP_EXT_PWRDOWN */
|
|
dirt_flag = 1;
|
|
}
|
|
|
|
if (!dirt_flag)
|
|
pr_err("%s: undefined (0x%04x)\n", __func__, tfadsp_event);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CHECK_CALIBRATION_DATA_RANGE)
|
|
#if !defined(TFA_LIMIT_CAL_FROM_DTS)
|
|
#define TFA_CAL_RANGE(max_devcnt, channel, type, revid) \
|
|
((max_devcnt != 2) ? type##_LIMIT_CAL_##revid : \
|
|
((channel == 0) ? \
|
|
type##_LIMIT_CAL_P_##revid : type##_LIMIT_CAL_S_##revid))
|
|
#if defined(USE_TFA9878)
|
|
/* mono */
|
|
#define LOWER_LIMIT_CAL_N0A 0
|
|
#define UPPER_LIMIT_CAL_N0A 32000
|
|
#define LOWER_LIMIT_CAL_N1A 0
|
|
#define UPPER_LIMIT_CAL_N1A 32000
|
|
/* stereo */
|
|
#define LOWER_LIMIT_CAL_P_N0A 0
|
|
#define UPPER_LIMIT_CAL_P_N0A 32000
|
|
#define LOWER_LIMIT_CAL_S_N0A 0
|
|
#define UPPER_LIMIT_CAL_S_N0A 32000
|
|
#if defined(QPLATFORM)
|
|
#define LOWER_LIMIT_CAL_P_N1A 0 /* Top */
|
|
#define UPPER_LIMIT_CAL_P_N1A 32000 /* Top */
|
|
#define LOWER_LIMIT_CAL_S_N1A 0 /* Bottom */
|
|
#define UPPER_LIMIT_CAL_S_N1A 32000 /* Bottom */
|
|
#elif defined(MPLATFORM)
|
|
#define LOWER_LIMIT_CAL_P_N1A 0 /* Top */
|
|
#define UPPER_LIMIT_CAL_P_N1A 32000 /* Top */
|
|
#define LOWER_LIMIT_CAL_S_N1A 0 /* Bottom */
|
|
#define UPPER_LIMIT_CAL_S_N1A 32000 /* Bottom */
|
|
#elif defined(SPLATFORM)
|
|
#define LOWER_LIMIT_CAL_P_N1A 0 /* Top */
|
|
#define UPPER_LIMIT_CAL_P_N1A 32000 /* Top */
|
|
#define LOWER_LIMIT_CAL_S_N1A 0 /* Bottom */
|
|
#define UPPER_LIMIT_CAL_S_N1A 32000 /* Bottom */
|
|
#else
|
|
#define LOWER_LIMIT_CAL_P_N1A 0 /* Top */
|
|
#define UPPER_LIMIT_CAL_P_N1A 32000 /* Top */
|
|
#define LOWER_LIMIT_CAL_S_N1A 0 /* Bottom */
|
|
#define UPPER_LIMIT_CAL_S_N1A 32000 /* Bottom */
|
|
#endif
|
|
/* USE_TFA9878 */
|
|
#elif defined(USE_TFA9894)
|
|
/* mono */
|
|
#define LOWER_LIMIT_CAL_N0A 0
|
|
#define UPPER_LIMIT_CAL_N0A 32000
|
|
#define LOWER_LIMIT_CAL_N1A 0
|
|
#define UPPER_LIMIT_CAL_N1A 32000
|
|
/* stereo */
|
|
#define LOWER_LIMIT_CAL_P_N0A 0
|
|
#define UPPER_LIMIT_CAL_P_N0A 32000
|
|
#define LOWER_LIMIT_CAL_S_N0A 0
|
|
#define UPPER_LIMIT_CAL_S_N0A 32000
|
|
#define LOWER_LIMIT_CAL_P_N1A 0 /* Top */
|
|
#define UPPER_LIMIT_CAL_P_N1A 32000 /* Top */
|
|
#define LOWER_LIMIT_CAL_S_N1A 0 /* Bottom */
|
|
#define UPPER_LIMIT_CAL_S_N1A 32000 /* Bottom */
|
|
#if defined(USE_TFA9894N2)
|
|
/* mono */
|
|
#define LOWER_LIMIT_CAL_N2A 0
|
|
#define UPPER_LIMIT_CAL_N2A 32000
|
|
#define LOWER_LIMIT_CAL_N3A 0
|
|
#define UPPER_LIMIT_CAL_N3A 32000
|
|
/* stereo */
|
|
#define LOWER_LIMIT_CAL_P_N2A 0
|
|
#define UPPER_LIMIT_CAL_P_N2A 32000
|
|
#define LOWER_LIMIT_CAL_S_N2A 0
|
|
#define UPPER_LIMIT_CAL_S_N2A 32000
|
|
#define LOWER_LIMIT_CAL_P_N3A 0 /* Top */
|
|
#define UPPER_LIMIT_CAL_P_N3A 32000 /* Top */
|
|
#define LOWER_LIMIT_CAL_S_N3A 0 /* Bottom */
|
|
#define UPPER_LIMIT_CAL_S_N3A 32000 /* Bottom */
|
|
/* USE_TFA9894N2 */
|
|
#endif
|
|
/* USE_TFA9894 */
|
|
#endif
|
|
#endif /* TFA_LIMIT_CAL_FROM_DTS */
|
|
|
|
static enum tfa98xx_error
|
|
tfa_calibration_range_check(struct tfa_device *tfa,
|
|
unsigned int channel, int mohm)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
#if !defined(TFA_LIMIT_CAL_FROM_DTS)
|
|
int spkr_count = 0;
|
|
#endif
|
|
int lower_limit_cal, upper_limit_cal;
|
|
|
|
#if defined(TFA_LIMIT_CAL_FROM_DTS)
|
|
lower_limit_cal = tfa->lower_limit_cal;
|
|
upper_limit_cal = tfa->upper_limit_cal;
|
|
#else
|
|
err = tfa_supported_speakers(tfa, &spkr_count);
|
|
if (err != TFA98XX_ERROR_OK) {
|
|
pr_err("error in checking supported speakers\n");
|
|
return err;
|
|
}
|
|
|
|
/* SoftDSP interface differs from hw-dsp interfaces */
|
|
if (tfa->is_probus_device && tfa->dev_count > 1)
|
|
spkr_count = tfa->dev_count;
|
|
|
|
lower_limit_cal = MIN_CALIBRATION_DATA;
|
|
upper_limit_cal = MAX_CALIBRATION_DATA;
|
|
|
|
switch (tfa->rev) {
|
|
#if defined(USE_TFA9878)
|
|
case 0x0a78: /* Initial revision ID */
|
|
lower_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, LOWER, N0A);
|
|
upper_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, UPPER, N0A);
|
|
break;
|
|
case 0x1a78: /* Initial revision ID */
|
|
lower_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, LOWER, N1A);
|
|
upper_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, UPPER, N1A);
|
|
break;
|
|
/* USE_TFA9878 */
|
|
#elif defined(USE_TFA9894)
|
|
case 0x0a94: /* Initial revision ID */
|
|
lower_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, LOWER, N0A);
|
|
upper_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, UPPER, N0A);
|
|
break;
|
|
case 0x1a94: /* Initial revision ID */
|
|
lower_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, LOWER, N1A);
|
|
upper_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, UPPER, N1A);
|
|
break;
|
|
#if defined(USE_TFA9894N2)
|
|
case 0x2a94: /* Initial revision ID */
|
|
lower_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, LOWER, N2A);
|
|
upper_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, UPPER, N2A);
|
|
break;
|
|
case 0x3a94: /* Initial revision ID */
|
|
lower_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, LOWER, N3A);
|
|
upper_limit_cal
|
|
= TFA_CAL_RANGE(spkr_count, channel, UPPER, N3A);
|
|
break;
|
|
/* USE_TFA9894N2 */
|
|
#endif
|
|
/* USE_TFA9894 */
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
#endif /* TFA_LIMIT_CAL_FROM_DTS */
|
|
|
|
pr_info("%s: 0x%04x [%d] - calibration range check [%d, %d] with %d\n",
|
|
__func__, tfa->rev, channel,
|
|
lower_limit_cal, upper_limit_cal, mohm);
|
|
if (mohm < lower_limit_cal || mohm > upper_limit_cal)
|
|
err = TFA98XX_ERROR_BAD_PARAMETER;
|
|
if (mohm == 0)
|
|
err = TFA98XX_ERROR_BAD_PARAMETER;
|
|
|
|
return err;
|
|
}
|
|
#endif /* CHECK_CALIBRATION_DATA_RANGE */
|
|
|
|
int tfa_wait_until_calibration_done(struct tfa_device *tfa)
|
|
{
|
|
int tries = 0;
|
|
|
|
if (!tfa->is_calibrating)
|
|
return 0; /* not running now */
|
|
|
|
pr_info("%s: calibration / V validation now runs\n",
|
|
__func__);
|
|
while (tries < TFA98XX_API_WAITCAL_NTRIES) {
|
|
msleep_interruptible(CAL_STATUS_INTERVAL);
|
|
if (tfa->is_calibrating == 0)
|
|
return 1; /* done */
|
|
tries++;
|
|
}
|
|
|
|
return 0; /* timeout */
|
|
}
|
|
|
|
#if defined(TFA_BLACKBOX_LOGGING)
|
|
enum tfa98xx_error tfa_configure_log(int enable)
|
|
{
|
|
enum tfa98xx_error err = TFA98XX_ERROR_OK;
|
|
struct tfa_device *tfa = NULL;
|
|
uint8_t cmd_buf[3];
|
|
|
|
tfa = tfa98xx_get_tfa_device_from_index(0);
|
|
if (tfa == NULL)
|
|
return TFA98XX_ERROR_DEVICE; /* unused device */
|
|
|
|
if (!tfa->is_bypass) {
|
|
cmd_buf[0] = 0;
|
|
cmd_buf[1] = 0;
|
|
/* 0 - disable logger, 1 - enable logger */
|
|
cmd_buf[2] = (enable) ? 1 : 0;
|
|
|
|
pr_info("%s: set blackbox (%d)\n",
|
|
__func__, enable);
|
|
err = tfa_dsp_cmd_id_write(tfa, MODULE_SPEAKERBOOST,
|
|
SB_PARAM_SET_DATA_LOGGER, 3, cmd_buf);
|
|
if (err) {
|
|
pr_err("%s: error in setting blackbox, err = %d\n",
|
|
__func__, err);
|
|
return err;
|
|
}
|
|
} else {
|
|
pr_info("%s: skip but store setting (%d) in bypass\n",
|
|
__func__, enable);
|
|
}
|
|
|
|
tfa->blackbox_enable = enable;
|
|
|
|
return err;
|
|
}
|
|
#if defined(TFA_USE_TFALOG_NODE)
|
|
EXPORT_SYMBOL(tfa_configure_log);
|
|
#endif
|
|
|
|
enum tfa98xx_error tfa_update_log(void)
|
|
{
|
|
enum tfa98xx_error err;
|
|
struct tfa_device *tfa = NULL;
|
|
int ndev, idx, offset, group, i;
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
uint8_t cmd_buf[(1 + TFA_LOG_MAX_COUNT * MAX_HANDLES) * 3] = {0};
|
|
#else
|
|
uint8_t cmd_buf[TFA_LOG_MAX_COUNT * MAX_HANDLES * 3] = {0};
|
|
#endif
|
|
int data[MAX_HANDLES * TFA_LOG_MAX_COUNT] = {0};
|
|
int read_size;
|
|
|
|
tfa = tfa98xx_get_tfa_device_from_index(0);
|
|
if (tfa == NULL)
|
|
return TFA98XX_ERROR_DEVICE; /* unused device */
|
|
|
|
if (!tfa->blackbox_enable) {
|
|
pr_info("%s: blackbox is inactive\n", __func__);
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
if (tfa->is_bypass) {
|
|
pr_info("%s: skip updating in bypass\n", __func__);
|
|
return TFA98XX_ERROR_OK;
|
|
}
|
|
|
|
ndev = tfa->dev_count;
|
|
if (ndev < 1)
|
|
return TFA98XX_ERROR_DEVICE;
|
|
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
read_size = (1 + TFA_LOG_MAX_COUNT * ndev) * 3;
|
|
#else
|
|
read_size = TFA_LOG_MAX_COUNT * ndev * 3;
|
|
#endif
|
|
|
|
pr_info("%s: read from blackbox\n", __func__);
|
|
err = tfa_dsp_cmd_id_write_read(tfa, MODULE_SPEAKERBOOST,
|
|
SB_PARAM_GET_DATA_LOGGER, read_size, cmd_buf);
|
|
if (err) {
|
|
pr_err("%s: failed to read data from blackbox, err = %d\n",
|
|
__func__, err);
|
|
return err;
|
|
}
|
|
|
|
tfa98xx_convert_bytes2data(read_size, cmd_buf, data);
|
|
|
|
for (idx = 0; idx < ndev; idx++) {
|
|
offset = idx * TFA_LOG_MAX_COUNT;
|
|
group = idx * ID_BLACKBOX_MAX;
|
|
|
|
pr_info("%s: dev %d - raw blackbox: [X = 0x%08x, T = 0x%08x]\n",
|
|
__func__, idx,
|
|
data[offset + ID_MAXX_LOG],
|
|
data[offset + ID_MAXT_LOG]);
|
|
|
|
/* maximum x (um) */
|
|
data[offset + ID_MAXX_LOG] = (int)
|
|
((unsigned int)data[offset + ID_MAXX_LOG]
|
|
/ TFA2_FW_X_DATA_UM_SCALE);
|
|
if (tfa->log_data[group + ID_MAXX_LOG]
|
|
< data[offset + ID_MAXX_LOG])
|
|
tfa->log_data[group + ID_MAXX_LOG]
|
|
= data[offset + ID_MAXX_LOG];
|
|
|
|
/* maximum x kept without reset */
|
|
if (tfa->log_data[group + ID_MAXX_KEEP_LOG]
|
|
< data[offset + ID_MAXX_LOG])
|
|
tfa->log_data[group + ID_MAXX_KEEP_LOG]
|
|
= data[offset + ID_MAXX_LOG];
|
|
|
|
/* maximum t (degC) */
|
|
data[offset + ID_MAXT_LOG] = (int)
|
|
(data[offset + ID_MAXT_LOG]
|
|
/ TFA2_FW_T_DATA_SCALE);
|
|
if (tfa->log_data[group + ID_MAXT_LOG]
|
|
< data[offset + ID_MAXT_LOG])
|
|
tfa->log_data[group + ID_MAXT_LOG]
|
|
= data[offset + ID_MAXT_LOG];
|
|
|
|
/* maximum t kept without reset */
|
|
if (tfa->log_data[group + ID_MAXT_KEEP_LOG]
|
|
< data[offset + ID_MAXT_LOG])
|
|
tfa->log_data[group + ID_MAXT_KEEP_LOG]
|
|
= data[offset + ID_MAXT_LOG];
|
|
|
|
/* counter of x > x_max */
|
|
tfa->log_data[group + ID_OVERXMAX_COUNT]
|
|
+= data[offset + ID_OVERXMAX_COUNT];
|
|
|
|
/* counter of t > t_max */
|
|
tfa->log_data[group + ID_OVERTMAX_COUNT]
|
|
+= data[offset + ID_OVERTMAX_COUNT];
|
|
|
|
for (i = 0; i < TFA_LOG_MAX_COUNT; i++)
|
|
pr_info("%s: dev %d - blackbox: data[%d] = %d (<- %d)\n",
|
|
__func__, idx, i,
|
|
tfa->log_data[group + i],
|
|
data[offset + i]);
|
|
pr_info("%s: dev %d - blackbox: data[%d] = %d\n",
|
|
__func__, idx, ID_MAXX_KEEP_LOG,
|
|
tfa->log_data[group + ID_MAXX_KEEP_LOG]);
|
|
pr_info("%s: dev %d - blackbox: data[%d] = %d\n",
|
|
__func__, idx, ID_MAXT_KEEP_LOG,
|
|
tfa->log_data[group + ID_MAXT_KEEP_LOG]);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
#endif /* TFA_BLACKBOX_LOGGING */
|
|
|
|
#if defined(TFA_USE_TFASTC_NODE)
|
|
#if defined(TFA_USE_STC_MEMTRACK)
|
|
#define TSPKR_ADDR 0x06
|
|
#define TEMP_INDEX 1
|
|
#else
|
|
#define TEMP_INDEX 0
|
|
#endif /* TFA_USE_STC_MEMTRACK */
|
|
enum tfa98xx_error tfa_read_tspkr(struct tfa_device *tfa, int *spkt)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
#if defined(TFA_USE_STC_MEMTRACK)
|
|
unsigned char buffer[(1 + 2) * 3] = {0};
|
|
int offset = 0, addr = 0;
|
|
#endif /* TFA_USE_STC_MEMTRACK */
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
unsigned char bytes[(TEMP_INDEX + 1 + 2) * 3] = {0};
|
|
#else
|
|
unsigned char bytes[(TEMP_INDEX + 2) * 3] = {0};
|
|
#endif /* TFA_CUSTOM_FORMAT_AT_RESPONSE */
|
|
int data[TEMP_INDEX + 2];
|
|
int nr_bytes, i, spkr_count = 0;
|
|
|
|
error = tfa_supported_speakers(tfa, &spkr_count);
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
pr_err("error in checking supported speakers\n");
|
|
return error;
|
|
}
|
|
|
|
/* SoftDSP interface differs from hw-dsp interfaces */
|
|
if (tfa->is_probus_device && tfa->dev_count > 1)
|
|
spkr_count = tfa->dev_count;
|
|
|
|
#if defined(TFA_USE_STC_MEMTRACK)
|
|
pr_info("%s: write SET_MEMTRACK of TSpkr\n", __func__);
|
|
|
|
offset = 0;
|
|
buffer[offset++] = (uint8_t)((spkr_count >> 16) & 0xffff);
|
|
buffer[offset++] = (uint8_t)((spkr_count >> 8) & 0xff);
|
|
buffer[offset++] = (uint8_t)(spkr_count & 0xff);
|
|
|
|
for (i = 0; i < spkr_count; i++) {
|
|
addr = TSPKR_ADDR + i; /* TSpkr address */
|
|
|
|
/* indexed address + snapshot */
|
|
buffer[offset + i * 3] = (uint8_t)0x23;
|
|
buffer[offset + i * 3 + 1] = (uint8_t)((addr >> 8) & 0xff);
|
|
buffer[offset + i * 3 + 2] = (uint8_t)(addr & 0xff);
|
|
}
|
|
|
|
nr_bytes = (1 + spkr_count) * 3;
|
|
tfa->individual_msg = 1;
|
|
error = tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK,
|
|
FW_PAR_ID_SET_MEMTRACK, nr_bytes, buffer);
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
pr_info("%s: failed to set memtrack for TSpkr (err %d)\n",
|
|
__func__, error);
|
|
return error;
|
|
}
|
|
|
|
msleep_interruptible(BUSLOAD_INTERVAL);
|
|
#endif /* TFA_USE_STC_MEMTRACK */
|
|
|
|
#if defined(TFA_CUSTOM_FORMAT_AT_RESPONSE)
|
|
nr_bytes = (TEMP_INDEX + 1 + spkr_count) * 3;
|
|
#else
|
|
nr_bytes = (TEMP_INDEX + spkr_count) * 3;
|
|
#endif /* TFA_CUSTOM_FORMAT_AT_RESPONSE */
|
|
|
|
#if defined(TFA_USE_STC_MEMTRACK)
|
|
pr_info("%s: read GET_MEMTRACK of TSpkr\n", __func__);
|
|
error = tfa_dsp_cmd_id_write_read(tfa,
|
|
MODULE_FRAMEWORK,
|
|
FW_PAR_ID_GET_MEMTRACK, nr_bytes, bytes);
|
|
#else
|
|
pr_info("%s: read SB_PARAM_GET_TSPKR\n", __func__);
|
|
error = tfa_dsp_cmd_id_write_read(tfa,
|
|
MODULE_SPEAKERBOOST,
|
|
SB_PARAM_GET_TSPKR, nr_bytes, bytes);
|
|
#endif
|
|
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
pr_info("%s: failure in reading speaker temperature (err %d)\n",
|
|
__func__, error);
|
|
return error;
|
|
}
|
|
|
|
tfa98xx_convert_bytes2data(nr_bytes, bytes, data);
|
|
|
|
pr_debug("%s: SPKR_TEMP - spkr_count %d\n",
|
|
__func__, spkr_count);
|
|
pr_debug("%s: SPKR_TEMP - data[0]=%d, data[1]=%d\n",
|
|
__func__, data[TEMP_INDEX], data[TEMP_INDEX + 1]);
|
|
|
|
/* real-time t (degC) */
|
|
for (i = 0; i < spkr_count; i++)
|
|
spkt[i] = (int)(data[TEMP_INDEX + i] / TFA2_FW_T_DATA_SCALE);
|
|
|
|
return error;
|
|
}
|
|
|
|
enum tfa98xx_error tfa_write_volume(struct tfa_device *tfa, int *sknt)
|
|
{
|
|
enum tfa98xx_error error = TFA98XX_ERROR_OK;
|
|
unsigned char bytes[2 * 3] = {0};
|
|
int i, spkr_count = 0;
|
|
int stcontrol[MAX_HANDLES] = {0};
|
|
int data = 0;
|
|
|
|
error = tfa_supported_speakers(tfa, &spkr_count);
|
|
if (error != TFA98XX_ERROR_OK) {
|
|
pr_err("error in checking supported speakers\n");
|
|
return error;
|
|
}
|
|
|
|
/* SoftDSP interface differs from hw-dsp interfaces */
|
|
if (tfa->is_probus_device && tfa->dev_count > 1)
|
|
spkr_count = tfa->dev_count;
|
|
|
|
if (sknt != NULL)
|
|
memcpy(stcontrol, sknt, spkr_count * sizeof(int));
|
|
else
|
|
pr_info("%s: initialize surface temperature control\n",
|
|
__func__);
|
|
|
|
/* We have to copy it for both channels. Even when MONO! */
|
|
if (tfa->dev_count == 1) { /* mono */
|
|
stcontrol[1] = stcontrol[0];
|
|
spkr_count++;
|
|
} else if (tfa->dev_count == 2) { /* stereo */
|
|
switch (tfa->active_handle) {
|
|
case 0:
|
|
pr_info("%s: copy stc from dev 0 to dev 1\n",
|
|
__func__);
|
|
stcontrol[1] = stcontrol[0];
|
|
break;
|
|
case 1:
|
|
pr_info("%s: copy stc from dev 1 to dev 0\n",
|
|
__func__);
|
|
stcontrol[0] = stcontrol[1];
|
|
break;
|
|
case -1:
|
|
/* individually configured */
|
|
default:
|
|
/* wrong handle */
|
|
break;
|
|
}
|
|
} else {
|
|
pr_err("%s: more than 2 devices were selected (%d devices)\n",
|
|
__func__, tfa->dev_count);
|
|
spkr_count = 2;
|
|
}
|
|
|
|
for (i = 0; i < spkr_count; i++) {
|
|
#if defined(TFA_USE_STC_VOLUME_TABLE)
|
|
stcontrol[i] = (stcontrol[i] < 0xff) ? stcontrol[i] : 0xff;
|
|
pr_info("%s: dev %d - volume index (%d)\n",
|
|
__func__, i, stcontrol[i]);
|
|
data = stcontrol[i];
|
|
#else
|
|
/* 10-bit signed integer */
|
|
if (stcontrol[i] >= TFA2_FW_T_DATA_MAX) {
|
|
pr_info("%s: dev %d - data overflow (%d), to set max (%d)\n",
|
|
__func__, i, stcontrol[i],
|
|
TFA2_FW_T_DATA_MAX - 1);
|
|
stcontrol[i] = TFA2_FW_T_DATA_MAX - 1;
|
|
}
|
|
if (stcontrol[i] < -TFA2_FW_T_DATA_MAX) {
|
|
pr_info("%s: dev %d - data overflow (%d), to set min (%d)\n",
|
|
__func__, i, stcontrol[i],
|
|
-TFA2_FW_T_DATA_MAX);
|
|
stcontrol[i] = -TFA2_FW_T_DATA_MAX;
|
|
}
|
|
pr_info("%s: dev %d - surface temperature (%d)\n",
|
|
__func__, i, stcontrol[i]);
|
|
#if defined(TFA_USE_CUSTOM_SET_TSURF)
|
|
data = (int)stcontrol[i];
|
|
#else
|
|
data = (int)stcontrol[i] * TFA2_FW_T_DATA_SCALE;
|
|
#endif /* TFA_USE_CUSTOM_SET_TSURF */
|
|
#endif /* TFA_USE_STC_VOLUME_TABLE */
|
|
|
|
bytes[i * 3] = (uint8_t)((data >> 16) & 0xffff);
|
|
bytes[i * 3 + 1] = (uint8_t)((data >> 8) & 0xff);
|
|
bytes[i * 3 + 2] = (uint8_t)(data & 0xff);
|
|
}
|
|
|
|
#if defined(TFA_USE_STC_VOLUME_TABLE)
|
|
pr_info("%s: write SB_PARAM_SET_VOLUME\n", __func__);
|
|
error = tfa_dsp_cmd_id_write
|
|
(tfa, MODULE_SPEAKERBOOST, SB_PARAM_SET_VOLUME,
|
|
sizeof(bytes), bytes);
|
|
#else
|
|
#if defined(TFA_USE_CUSTOM_SET_TSURF)
|
|
pr_info("%s: write CUSTOM_PARAM_SET_TSURF\n", __func__);
|
|
error = tfa_dsp_cmd_id_write
|
|
(tfa, MODULE_CUSTOM, CUSTOM_PARAM_SET_TSURF,
|
|
sizeof(bytes), bytes);
|
|
#else
|
|
pr_info("%s: write SB_PARAM_SET_TSURF\n", __func__);
|
|
error = tfa_dsp_cmd_id_write
|
|
(tfa, MODULE_SPEAKERBOOST, SB_PARAM_SET_TSURF,
|
|
sizeof(bytes), bytes);
|
|
#endif /* TFA_USE_CUSTOM_SET_TSURF */
|
|
#endif /* TFA_USE_STC_VOLUME_TABLE */
|
|
if (error != TFA98XX_ERROR_OK)
|
|
pr_info("%s: failure in writing surface temperature (err %d)\n",
|
|
__func__, error);
|
|
|
|
return error;
|
|
}
|
|
|
|
#if defined(TFA_USE_STC_VOLUME_TABLE)
|
|
#define STC_TEMP_MIN (35)
|
|
#define STC_TEMP_MAX (44)
|
|
|
|
static int stc_min[MAX_HANDLES] = {-1, -1, -1, -1};
|
|
static int stc_max[MAX_HANDLES] = {-1, -1, -1, -1};
|
|
static int stc_vol_table[MAX_HANDLES][STC_TABLE_MAX] = {
|
|
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, /* dev 0 */
|
|
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, /* dev 1 */
|
|
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, /* dev 2 */
|
|
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, /* dev 3 */
|
|
};
|
|
|
|
int tfa_get_sknt_data_from_table(int idx, int value)
|
|
{
|
|
int data = 0xffff, vol_index = 0;
|
|
|
|
if (idx < 0 || idx >= MAX_HANDLES)
|
|
return data;
|
|
|
|
if (stc_min[idx] == -1 || stc_max[idx] == -1) {
|
|
/* use default min / max */
|
|
if (stc_min[idx] == -1)
|
|
stc_min[idx] = STC_TEMP_MIN;
|
|
if (stc_max[idx] == -1)
|
|
stc_max[idx] = STC_TEMP_MAX;
|
|
|
|
pr_info("%s : tfa_stc - dev %d - stc_min: %d, stc_max: %d\n",
|
|
__func__, idx, stc_min[idx], stc_max[idx]);
|
|
}
|
|
|
|
if (value < stc_min[idx])
|
|
value = stc_min[idx];
|
|
if (value > stc_max[idx])
|
|
value = stc_max[idx];
|
|
|
|
vol_index = ((value - stc_min[idx]) * (STC_TABLE_MAX - 1))
|
|
/ (stc_max[idx] - stc_min[idx]);
|
|
/* actuial gain is defined with STC_GAIN_STEP */
|
|
data = (int)stc_vol_table[idx][vol_index];
|
|
|
|
pr_info("%s: tfa_stc - dev %d - select volume index (%d; %d)\n",
|
|
__func__, idx, vol_index, data);
|
|
|
|
return data;
|
|
}
|
|
|
|
#if defined(TFA_ENABLE_STC_TUNING)
|
|
int tfa_get_stc_minmax(int idx, int minmax)
|
|
{
|
|
if (idx < 0 || idx >= MAX_HANDLES)
|
|
return 0xffff; /* wrong index */
|
|
|
|
switch (minmax) {
|
|
case 0: /* min */
|
|
return stc_min[idx];
|
|
case 1: /* max */
|
|
return stc_max[idx];
|
|
default: /* wrong index */
|
|
return 0xffff;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(tfa_get_stc_minmax);
|
|
|
|
void tfa_set_stc_minmax(int idx, int minmax, int value)
|
|
{
|
|
if (idx < 0 || idx >= MAX_HANDLES)
|
|
return; /* wrong index */
|
|
|
|
switch (minmax) {
|
|
case 0: /* min */
|
|
stc_min[idx] = value;
|
|
case 1: /* max */
|
|
stc_max[idx] = value;
|
|
default: /* wrong index */
|
|
break;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(tfa_set_stc_minmax);
|
|
|
|
void tfa_get_stc_gtable(int idx, int *value)
|
|
{
|
|
if (idx < 0 || idx >= MAX_HANDLES)
|
|
return; /* wrong index */
|
|
|
|
memcpy(value, &stc_vol_table[idx][0], sizeof(int) * STC_TABLE_MAX);
|
|
}
|
|
EXPORT_SYMBOL(tfa_get_stc_gtable);
|
|
|
|
void tfa_set_stc_gtable(int idx, int *value)
|
|
{
|
|
if (idx < 0 || idx >= MAX_HANDLES)
|
|
return; /* wrong index */
|
|
|
|
memcpy(&stc_vol_table[idx][0], value, sizeof(int) * STC_TABLE_MAX);
|
|
}
|
|
EXPORT_SYMBOL(tfa_set_stc_gtable);
|
|
#endif /* TFA_ENABLE_STC_TUNING */
|
|
#endif /* TFA_USE_STC_VOLUME_TABLE */
|
|
#endif /* TFA_USE_TFASTC_NODE */
|
|
|