/* * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "tfa_dsp_fw.h" #include "tfa_service.h" #include "tfa_internal.h" #include "tfa98xx_tfafieldnames.h" static enum Tfa98xx_Error tfa9890_specific(Tfa98xx_handle_t handle) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; unsigned short regRead = 0; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; /* all i2C registers are already set to default for N1C2 */ /* some PLL registers must be set optimal for amplifier behaviour */ error = tfa98xx_write_register16(handle, 0x40, 0x5a6b); if (error) return error; tfa98xx_read_register16(handle, 0x59, ®Read); regRead |= 0x3; tfa98xx_write_register16(handle, 0x59, regRead); error = tfa98xx_write_register16(handle, 0x40, 0x0000); error = tfa98xx_write_register16(handle, 0x47, 0x7BE1); return error; } /* * Tfa9890_DspSystemStable will compensate for the wrong behavior of CLKS * to determine if the DSP subsystem is ready for patch and config loading. * * A MTP calibration register is checked for non-zero. * * Note: This only works after i2c reset as this will clear the MTP contents. * When we are configured then the DSP communication will synchronize access. * */ static enum Tfa98xx_Error tfa9890_dsp_system_stable(Tfa98xx_handle_t handle, int *ready) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; unsigned short status, mtp0; int result, tries; /* check the contents of the STATUS register */ result = TFA_READ_REG(handle, AREFS); if (result < 0) { error = -result; goto errorExit; } status = (unsigned short)result; /* if AMPS is set then we were already configured and running * no need to check further */ *ready = (TFA_GET_BF_VALUE(handle, AMPS, status) == 1); if (*ready) /* if ready go back */ return error; /* will be Tfa98xx_Error_Ok */ /* check AREFS and CLKS: not ready if either is clear */ *ready = !((TFA_GET_BF_VALUE(handle, AREFS, status) == 0) || (TFA_GET_BF_VALUE(handle, CLKS, status) == 0)); if (!*ready) /* if not ready go back */ return error; /* will be Tfa98xx_Error_Ok */ /* check MTPB * mtpbusy will be active when the subsys copies MTP to I2C * 2 times retry avoids catching this short mtpbusy active period */ for (tries = 2; tries > 0; tries--) { result = TFA_GET_BF(handle, MTPB); if (result < 0) { error = -result; goto errorExit; } status = (unsigned short)result; /* check the contents of the STATUS register */ *ready = (result == 0); if (*ready) /* if ready go on */ break; } if (tries == 0) /* ready will be 0 if retries exausted */ return Tfa98xx_Error_Ok; /* check the contents of MTP register for non-zero, * this indicates that the subsys is ready */ error = tfa98xx_read_register16(handle, 0x84, &mtp0); if (error) goto errorExit; *ready = (mtp0 != 0); /* The MTP register written? */ return error; errorExit: *ready = 0; return error; } /* * The CurrentSense4 register is not in the datasheet, define local */ #define TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF (1<<2) #define TFA98XX_CURRENTSENSE4 0x49 /* * Disable clock gating */ static enum Tfa98xx_Error tfa9890_clockgating(Tfa98xx_handle_t handle, int on) { enum Tfa98xx_Error error; unsigned short value; /* for TFA9890 temporarily disable clock gating when dsp reset is used */ error = tfa98xx_read_register16(handle, TFA98XX_CURRENTSENSE4, &value); if (error) return error; if (Tfa98xx_Error_Ok == error) { if (on) /* clock gating on - clear the bit */ value &= ~TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF; else /* clock gating off - set the bit */ value |= TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF; error = tfa98xx_write_register16(handle, TFA98XX_CURRENTSENSE4, value); } return error; } /* * Tfa9890_DspReset will deal with clock gating control in order * to reset the DSP for warm state restart */ static enum Tfa98xx_Error tfa9890_dsp_reset(Tfa98xx_handle_t handle, int state) { enum Tfa98xx_Error error; /* for TFA9890 temporarily disable clock gating when dsp reset is used */ tfa9890_clockgating(handle, 0); TFA_SET_BF(handle, RST, (uint16_t)state); /* clock gating restore */ error = tfa9890_clockgating(handle, 1); return error; } /* * register device specifics functions */ void tfa9890_ops(struct tfa_device_ops *ops) { ops->tfa_init = tfa9890_specific; ops->tfa_dsp_reset = tfa9890_dsp_reset; ops->tfa_dsp_system_stable = tfa9890_dsp_system_stable; }