/* * 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 "config.h" #include "tfa98xx_tfafieldnames.h" #include "tfa_internal.h" #include "tfa.h" #include "tfa_service.h" #include "tfa_container.h" #include "tfa_dsp_fw.h" /* TODO: remove genregs usage? */ #include "tfa98xx_genregs_N1C.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(handle, RST) >> 8) #define FAM_TFA98XX_CF_MEM (TFA_FAM(handle, MEMA) >> 8) #define FAM_TFA98XX_MTP0 (TFA_FAM(handle, MTPOTC) >> 8) #define FAM_TFA98xx_INT_EN (TFA_FAM(handle, INTENVDDS) >> 8) #define CF_STATUS_I2C_CMD_ACK 0x01 #if (defined(TFA9888) || defined(TFA98XX_FULL)) void tfa9888_ops(struct tfa_device_ops *ops); #endif #if (defined(TFA9891) || defined(TFA98XX_FULL)) void tfa9891_ops(struct tfa_device_ops *ops); #endif #if (defined(TFA9897) || defined(TFA98XX_FULL)) void tfa9897_ops(struct tfa_device_ops *ops); #endif #if (defined(TFA9890) || defined(TFA98XX_FULL)) void tfa9890_ops(struct tfa_device_ops *ops); #endif #if (defined(TFA9887B) || defined(TFA98XX_FULL)) int tfa9887B_is87(Tfa98xx_handle_t handle); void tfa9887B_ops(struct tfa_device_ops *ops); #endif #if (defined(TFA9887) || defined(TFA98XX_FULL)) void tfa9887_ops(struct tfa_device_ops *ops); #endif #ifndef MIN #define MIN(A, B) (A < B?A:B) #endif /* retry values */ #define CFSTABLE_TRIES 10 #define PWDNWAIT_TRIES 50 #define AMPOFFWAIT_TRIES 50 #define MTPBWAIT_TRIES 50 static int tfa98xx_runtime_verbose; static int tfa98xx_trace_level; /* 4 possible I2C addresses */ #define MAX_HANDLES 4 TFA_INTERNAL struct Tfa98xx_handle_private handles_local[MAX_HANDLES]; /* * static functions */ TFA_INTERNAL int tfa98xx_handle_is_open(Tfa98xx_handle_t h) { int retval = 0; if ((h >= 0) && (h < MAX_HANDLES)) retval = handles_local[h].in_use != 0; return retval; } int print_calibration(Tfa98xx_handle_t handle, char *str, size_t size) { return snprintf(str, size, " Prim:%d mOhms, Sec:%d mOhms\n", handles_local[handle].mohm[0], handles_local[handle].mohm[1]); } int tfa_get_calibration_info(Tfa98xx_handle_t handle, int channel) { return handles_local[handle].mohm[channel]; } /* * open */ /* * set device info and register device ops */ static void tfa_set_query_info (int dev_idx) { if (dev_idx > MAX_HANDLES) { _ASSERT(dev_idx > MAX_HANDLES); return; } /* invalidate device struct cached values */ handles_local[dev_idx].hw_feature_bits = -1; handles_local[dev_idx].sw_feature_bits[0] = -1; handles_local[dev_idx].sw_feature_bits[1] = -1; handles_local[dev_idx].profile = -1; handles_local[dev_idx].vstep[0] = -1; handles_local[dev_idx].vstep[1] = -1; /* defaults */ handles_local[dev_idx].tfa_family = 1; handles_local[dev_idx].daimap = Tfa98xx_DAI_I2S; /* all others */ handles_local[dev_idx].spkr_count = 1; handles_local[dev_idx].spkr_select = 0; handles_local[dev_idx].support_tcoef = supportYes; handles_local[dev_idx].supportDrc = supportNotSet; handles_local[dev_idx].support_saam = supportNotSet; /* TODO use the getfeatures() for retrieving the features [artf103523] handles_local[dev_idx].supportDrc = supportNotSet;*/ switch (handles_local[dev_idx].rev & 0xff) { case 0x88: /* tfa9888 */ handles_local[dev_idx].tfa_family = 2; handles_local[dev_idx].spkr_count = 2; handles_local[dev_idx].daimap = Tfa98xx_DAI_TDM; tfa9888_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ break; case 0x97: /* tfa9897 */ handles_local[dev_idx].supportDrc = supportNo; handles_local[dev_idx].spkr_count = 1; handles_local[dev_idx].daimap = Tfa98xx_DAI_TDM; tfa9897_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ break; case 0x92: /* tfa9891 */ handles_local[dev_idx].spkr_count = 1; handles_local[dev_idx].daimap = (Tfa98xx_DAI_PDM | Tfa98xx_DAI_I2S); tfa9891_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ break; case 0x91: /* tfa9890B */ handles_local[dev_idx].spkr_count = 1; handles_local[dev_idx].daimap = (Tfa98xx_DAI_PDM | Tfa98xx_DAI_I2S); break; case 0x80: case 0x81: /* tfa9890 */ handles_local[dev_idx].spkr_count = 1; handles_local[dev_idx].daimap = Tfa98xx_DAI_I2S; handles_local[dev_idx].supportDrc = supportNo; handles_local[dev_idx].supportFramework = supportNo; tfa9890_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ break; case 0x12: /* tfa9887 / tfa9887B / tfa9895 */ handles_local[dev_idx].spkr_count = 1; handles_local[dev_idx].daimap = Tfa98xx_DAI_I2S; if (tfa9887B_is87(dev_idx)) { handles_local[dev_idx].support_tcoef = supportNo; tfa9887_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ } else tfa9887B_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ break; default: pr_err("unknown device type : 0x%02x\n", handles_local[dev_idx].rev); _ASSERT(0); break; } } /* * 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: return 1; case 0x88: return 2; case 0x50: return 3; default: return 0; } } int tfa98xx_dev_family(Tfa98xx_handle_t dev_idx) { return handles_local[dev_idx].tfa_family; } unsigned short tfa98xx_dev_revision(Tfa98xx_handle_t dev_idx) { return handles_local[dev_idx].rev; } void tfa98xx_set_spkr_select(Tfa98xx_handle_t dev_idx, char *configuration) { char firstLetter; /* 4=Left, 2=Right, 1=none, 0=default */ if (configuration == NULL) handles_local[dev_idx].spkr_select = 0; else { firstLetter = (char)tolower((unsigned char)configuration[0]); switch (firstLetter) { case 'b': /* SC / both -> apply primary also to secondary */ handles_local[dev_idx].spkr_select = 8; handles_local[dev_idx].spkr_count = 2; break; case 'l': case 'p': /* DS / left -> only use primary channel */ handles_local[dev_idx].spkr_select = 4; handles_local[dev_idx].spkr_count = 1; break; case 'r': case 's': /* DP / right -> only use secondary channel */ handles_local[dev_idx].spkr_select = 2; handles_local[dev_idx].spkr_count = 1; break; case 'd': /* DC / disable -> skip applying configuration for both */ handles_local[dev_idx].spkr_select = 1; handles_local[dev_idx].spkr_count = 2; break; default: handles_local[dev_idx].spkr_select = 0; handles_local[dev_idx].spkr_count = 2; break; } } } void tfa_mock_probe(int dev_idx, unsigned short revid, int slave_address) { handles_local[dev_idx].slave_address = (unsigned char)slave_address*2; handles_local[dev_idx].rev = revid; tfa_set_query_info(dev_idx); } enum Tfa98xx_Error tfa_soft_probe(int dev_idx, int revid) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; error = tfaContGetSlave(dev_idx, &handles_local[dev_idx].slave_address); handles_local[dev_idx].slave_address *= 2; if (error) return error; handles_local[dev_idx].rev = (unsigned short)revid; tfa_set_query_info(dev_idx); return error; } /* * TODO The slave/cnt check in tfa98xx_register_dsp() should be done here in tfa_probe() */ enum Tfa98xx_Error tfa_probe(unsigned char slave_address, Tfa98xx_handle_t *pHandle) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; int rev; int idx; _ASSERT(pHandle != NULL); *pHandle = -1; /* when available select index used in container file */ idx = tfa98xx_cnt_slave2idx(slave_address>>1); if (idx < 0) idx = 0; /* when no container file, use first instance */ if (handles_local[idx].in_use == 1) return Tfa98xx_Error_InUse; handles_local[idx].in_use = 1; switch (slave_address) { case TFA98XX_GENERIC_SLAVE_ADDRESS: /* same as (0x0E<<1) test adr */ case 0x68: case 0x6A: case 0x6C: case 0x6E: case (0x1a<<1): /* TODO properly implement foreign i2c addressing like uda1355 */ handles_local[idx].buffer_size = MAX_I2C_BUFFER_SIZE; handles_local[idx].slave_address = slave_address; /* TODO: how do we deal with old bugs? */ #if (defined(TFA9887) || defined (TFA9887B) || defined(TFA98XX_FULL)) /* do a dummy read in order to generate * i2c clocks when accessing the device * for the first time */ rev = TFA_READ_REG(idx, REV); #endif /* this can be the very first read, so check error here */ rev = TFA_READ_REG(idx, REV); if (rev < 0) /* returns negative if error */ error = -rev; if (Tfa98xx_Error_Ok != error) { handles_local[idx].in_use = 0; pr_debug("\nError: Unable to read revid from slave:0x%02x \n", slave_address/2); return error; } handles_local[idx].rev = (unsigned short) rev; *pHandle = idx; error = Tfa98xx_Error_Ok; #ifdef __KERNEL__ /* don't spam userspace with information */ tfa98xx_trace_printk("slave:0x%02x revid:0x%04x\n", slave_address, rev); pr_debug("slave:0x%02x revid:0x%04x\n", slave_address, rev); #endif break; default: /* wrong slave address */ error = Tfa98xx_Error_Bad_Parameter; } tfa_set_query_info(idx); handles_local[idx].in_use = 0; return error; } enum Tfa98xx_Error tfa98xx_open(Tfa98xx_handle_t handle) { if (tfa98xx_handle_is_open(handle)) { return Tfa98xx_Error_InUse; } else { handles_local[handle].in_use = 1; return Tfa98xx_Error_Ok; } } /* * close */ enum Tfa98xx_Error tfa98xx_close(Tfa98xx_handle_t handle) { if (tfa98xx_handle_is_open(handle)) { handles_local[handle].in_use = 0; return Tfa98xx_Error_Ok; } else { return Tfa98xx_Error_NotOpen; } } /* * 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(Tfa98xx_handle_t dev, int filter_index, unsigned short *address, int channel) { enum Tfa98xx_DMEM dmem = -1; int idx; unsigned short bq_table[7][4] = { /* index: 10, 11, 12, 13 */ {346, 351, 356, 288}, /* 87 BRA_MAX_MRA4-2_7.00 */ {346, 351, 356, 288}, /* 90 BRA_MAX_MRA6_9.02 */ {467, 472, 477, 409}, /* 95 BRA_MAX_MRA7_10.02 */ {406, 411, 416, 348}, /* 97 BRA_MAX_MRA9_12.01 */ {467, 472, 477, 409}, /* 91 BRA_MAX_MRAA_13.02 */ {8832, 8837, 8842, 8847}, /* 88 part1 */ {8853, 8858, 8863, 8868} /* 88 part2 */ /* Since the 88 is stereo we have 2 parts. * Every index has 5 values except index 13 this one has 6 values */ }; if ((10 <= filter_index) && (filter_index <= 13)) { dmem = Tfa98xx_DMEM_YMEM; /* for all devices */ idx = filter_index-10; switch (handles_local[dev].rev & 0xff) { /* only compare lower byte */ case 0x12: if (tfa9887B_is87(dev)) *address = bq_table[0][idx]; else *address = bq_table[2][idx]; break; case 0x97: *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; default: /* unsupported case, possibly intermediate version */ return (-1); _ASSERT(0); } } return dmem; } /************************ query functions ********************************************************/ /* no device involved */ /** * return revision */ void tfa98xx_rev(int *major, int *minor, int *revision) { *major = TFA98XX_API_REV_MAJOR; *minor = TFA98XX_API_REV_MINOR; *revision = TFA98XX_API_REV_REVISION; } /** * Return the maximum nr of devices (SC39786) */ int tfa98xx_max_devices(void) /* TODO get from cnt (now only called from contOpen) */ { return MAX_HANDLES; } /* return the device revision id */ unsigned short tfa98xx_get_device_revision(Tfa98xx_handle_t handle) { /* local function. Caller must make sure handle is valid */ return handles_local[handle].rev; } /** * return the device digital audio interface (DAI) type bitmap */ enum Tfa98xx_DAI tfa98xx_get_device_dai(Tfa98xx_handle_t handle) { /* local function. Caller must make sure handle is valid */ return handles_local[handle].daimap; } /** * get the feature bits from the DSP * - the older tfa9887 does not support this getfeature and * also no tcoef support so we use this as the info for returning * this feature */ enum Tfa98xx_Error tfa98xx_dsp_support_tcoef(Tfa98xx_handle_t dev_idx, int *pb_support_tCoef) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; /* if already set return that state , always assume supported */ if (handles_local[dev_idx].support_tcoef != supportNotSet) { *pb_support_tCoef = (handles_local[dev_idx].support_tcoef == supportYes); } handles_local[dev_idx].support_tcoef = *pb_support_tCoef ? supportYes : supportNo; return error; } /** * tfa98xx_supported_speakers * returns the number of the supported speaker count */ enum Tfa98xx_Error tfa98xx_supported_speakers(Tfa98xx_handle_t handle, int *spkr_count) { if (tfa98xx_handle_is_open(handle)) { *spkr_count = handles_local[handle].spkr_count; } else return Tfa98xx_Error_NotOpen; return Tfa98xx_Error_Ok; } /** * tfa98xx_supported_dai * returns the bitmap of the supported Digital Audio Interfaces */ enum Tfa98xx_Error tfa98xx_supported_dai(Tfa98xx_handle_t handle, enum Tfa98xx_DAI *daimap) { if (tfa98xx_handle_is_open(handle)) { *daimap = handles_local[handle].daimap; } else return Tfa98xx_Error_NotOpen; return Tfa98xx_Error_Ok; } /* * tfa98xx_supported_saam * returns the supportedspeaker as microphone feature */ enum Tfa98xx_Error tfa98xx_supported_saam(Tfa98xx_handle_t handle, enum Tfa98xx_saam *saam) { int features; enum Tfa98xx_Error error; if (handles_local[handle].support_saam == supportNotSet) { error = tfa98xx_dsp_get_hw_feature_bits(handle, &features); if (error != Tfa98xx_Error_Ok) return error; handles_local[handle].support_saam = (features & 0x8000) ? supportYes : supportNo; /* SAAM is bit15 */ } *saam = handles_local[handle].support_saam == supportYes ? Tfa98xx_saam : Tfa98xx_saam_none ; return Tfa98xx_Error_Ok; } /* * tfa98xx_compare_features * Obtains features_from_MTP and features_from_cnt */ enum Tfa98xx_Error tfa98xx_compare_features(Tfa98xx_handle_t handle, int features_from_MTP[3], int features_from_cnt[3]) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; uint32_t value; uint16_t mtpbf; unsigned char bytes[3 * 2]; /* int sw_feature_bits[2]; *//* cached feature bits data */ /* int hw_feature_bits; *//* cached feature bits data */ /* Nothing to test without clock: */ int status; tfa98xx_dsp_system_stable(handle, &status); if (!status) return Tfa98xx_Error_NoClock; /* Only test when we have a clock. */ /* Set proper MTP location per device: */ if (tfa98xx_dev_family(handle) == 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(handle, mtpbf) & 0xffff; features_from_MTP[0] = handles_local[handle].hw_feature_bits = value; /* Read SW features: */ error = tfa_dsp_cmd_id_write_read(handle, MODULE_FRAMEWORK, FW_PAR_ID_GET_FEATURE_INFO, sizeof(bytes), bytes); if (error != Tfa98xx_Error_Ok) return error; /* old ROM code may respond with Tfa98xx_Error_RpcParamId */ tfa98xx_convert_bytes2data(sizeof(bytes), bytes, &features_from_MTP[1]); /* check if feature bits from MTP match feature bits from cnt file: */ get_hw_features_from_cnt(handle, &features_from_cnt[0]); get_sw_features_from_cnt(handle, &features_from_cnt[1]); return error; } /********************************* device specific ops ************************************************/ /* the wrapper for DspReset, in case of full */ enum Tfa98xx_Error tfa98xx_dsp_reset(Tfa98xx_handle_t dev_idx, int state) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; if (tfa98xx_handle_is_open(dev_idx)) { if (handles_local[dev_idx].dev_ops.tfa_dsp_reset) error = (*handles_local[dev_idx].dev_ops.tfa_dsp_reset)(dev_idx, state); else /* generic function */ TFA_SET_BF_VOLATILE(dev_idx, RST, (uint16_t)state); } return error; } /* tfa98xx_dsp_system_stable * return: *ready = 1 when clocks are stable to allow DSP subsystem access */ /* This is the clean, default static */ static enum Tfa98xx_Error _tfa98xx_dsp_system_stable(Tfa98xx_handle_t handle, int *ready) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; unsigned short status; int value; /* check the contents of the STATUS register */ value = TFA_READ_REG(handle, AREFS); if (value < 0) { error = -value; *ready = 0; _ASSERT(error); /* an error here can be fatal */ return error; } status = (unsigned short)value; /* check AREFS and CLKS: not ready if either is clear */ *ready = !((TFA_GET_BF_VALUE(handle, AREFS, status) == 0) || (TFA_GET_BF_VALUE(handle, CLKS, status) == 0)); return error; } /* deferred calibration */ void tfa98xx_apply_deferred_calibration(Tfa98xx_handle_t handle) { struct tfa98xx_controls *controls = &(handles_local[handle].dev_ops.controls); enum Tfa98xx_Error err = Tfa98xx_Error_Ok; unsigned short value; if (controls->otc.deferrable && controls->otc.triggered) { pr_debug("Deferred writing otc = %d\n", controls->otc.wr_value); err = tfa98xx_set_mtp(handle, (uint16_t)controls->otc.wr_value << TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS, 1 << TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS); if (err != Tfa98xx_Error_Ok) { pr_err("Unable to apply deferred MTP OTC write. Error=%d\n", err); } else { controls->otc.triggered = false; controls->otc.rd_valid = true; err = tfa98xx_get_mtp(handle, &value); if (err == Tfa98xx_Error_Ok) controls->otc.rd_value = (value & TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK) >> TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS; else controls->otc.rd_value = controls->otc.wr_value; } } if (controls->mtpex.deferrable && controls->mtpex.triggered) { pr_debug("Deferred writing mtpex = %d\n", controls->mtpex.wr_value); err = tfa98xx_set_mtp(handle, (uint16_t)controls->mtpex.wr_value << TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS, 1 << TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS); if (err != Tfa98xx_Error_Ok) { pr_err("Unable to apply deferred MTPEX write. Rrror=%d\n", err); } else { controls->mtpex.triggered = false; controls->mtpex.rd_valid = true; err = tfa98xx_get_mtp(handle, &value); if (err == Tfa98xx_Error_Ok) controls->otc.rd_value = (value & TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK) >> TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS; else controls->mtpex.rd_value = controls->mtpex.wr_value; } } if (controls->calib.triggered) { err = tfa_calibrate(handle); if (err) { pr_info("Deferred calibration failed: %d\n", err); } else { pr_debug("Deferred calibration ok\n"); controls->calib.triggered = false; } } } /* the ops wrapper for tfa98xx_dsp_SystemStable */ enum Tfa98xx_Error tfa98xx_dsp_system_stable(Tfa98xx_handle_t dev_idx, int *ready) { enum Tfa98xx_Error error; if (!tfa98xx_handle_is_open(dev_idx)) return Tfa98xx_Error_NotOpen; if (handles_local[dev_idx].dev_ops.tfa_dsp_system_stable) error = (*handles_local[dev_idx].dev_ops.tfa_dsp_system_stable)(dev_idx, ready); else /* generic function */ error = _tfa98xx_dsp_system_stable(dev_idx, ready); return error; } /* the ops wrapper for tfa98xx_dsp_SystemStable */ int tfa98xx_cf_enabled(Tfa98xx_handle_t dev_idx) { if (!tfa98xx_handle_is_open(dev_idx)) return Tfa98xx_Error_NotOpen; return TFA_GET_BF(dev_idx, CFE); } /* * bring the device into a state similar to reset */ enum Tfa98xx_Error tfa98xx_init(Tfa98xx_handle_t handle) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; uint16_t value = 0; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; /* reset all i2C registers to default * Write the register directly to avoid the read in the bitfield function. * The I2CR bit may overwrite the full register because it is reset anyway. * This will save a reg read transaction. */ TFA_SET_BF_VALUE(handle, I2CR, 1, &value); TFA_WRITE_REG(handle, I2CR, value); if (tfa98xx_dev_family(handle) == 2) { /* restore MANSCONF and MANCOLD to POR state */ TFA_SET_BF_VOLATILE(handle, MANSCONF, 0); TFA_SET_BF_VOLATILE(handle, MANCOLD, 1); } else { /* Mark TFA1 family chips OTC and MTPEX calibration accesses * as deferrable, since these registers cannot be accesed * while the I2S link is not up and running */ handles_local[handle].dev_ops.controls.otc.deferrable = true; handles_local[handle].dev_ops.controls.mtpex.deferrable = true; } /* Put DSP in reset */ tfa98xx_dsp_reset(handle, 1); /* in pair of tfaRunStartDSP() */ /* some other registers must be set for optimal amplifier behaviour * This is implemented in a file specific for the type number */ if (handles_local[handle].dev_ops.tfa_init) error = (*handles_local[handle].dev_ops.tfa_init)(handle); /* TODO warning here? */ return error; } enum Tfa98xx_Error tfa98xx_dsp_write_tables(Tfa98xx_handle_t handle, int sample_rate) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; if (handles_local[handle].dev_ops.tfa_dsp_write_tables) error = (*handles_local[handle].dev_ops.tfa_dsp_write_tables)(handle, sample_rate); return error; } /********************* new tfa2 *********************************************************************/ /* newly added messaging for tfa2 tfa1? */ enum Tfa98xx_Error tfa98xx_dsp_get_memory(Tfa98xx_handle_t handle, int memoryType, int offset, int length, unsigned char bytes[]) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; char msg[12]; msg[0] = 8; msg[1] = MODULE_FRAMEWORK + 128; msg[2] = FW_PAR_ID_GET_MEMORY; msg[3] = 0; msg[4] = 0; msg[5] = (char)memoryType; msg[6] = 0; msg[7] = (offset>>8) & 0xff; msg[8] = offset & 0xff; msg[9] = 0; msg[10] = (length>>8) & 0xff; msg[11] = length & 0xff; /* send msg */ error = tfa_dsp_msg(handle, sizeof(msg), (char *)msg); if (error != Tfa98xx_Error_Ok) return error; /* read the data from the device (length * 3 = words) */ error = tfa_dsp_msg_read(handle, length * 3, bytes); return error; } enum Tfa98xx_Error tfa98xx_dsp_set_memory(Tfa98xx_handle_t handle, int memoryType, int offset, int length, int value) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; char msg[15]; msg[0] = 8; msg[1] = MODULE_FRAMEWORK + 128; msg[2] = FW_PAR_ID_SET_MEMORY; msg[3] = 0; msg[4] = 0; msg[5] = (char)memoryType; msg[6] = 0; msg[7] = (offset>>8) & 0xff; msg[8] = offset & 0xff; msg[9] = 0; msg[10] = (length>>8) & 0xff; msg[11] = length & 0xff; msg[12] = (value>>16) & 0xff; msg[13] = (value>>8) & 0xff; msg[14] = value & 0xff; /* send msg */ error = tfa_dsp_msg(handle, sizeof(msg), (char *)msg); return error; } /****************************** calibration support **************************/ /* * get/set the mtp with user controllable values * * check if the relevant clocks are available */ enum Tfa98xx_Error tfa98xx_get_mtp(Tfa98xx_handle_t handle, uint16_t *value) { int status; int result; /* not possible if PLL in powerdown */ if (TFA_GET_BF(handle, PWDN)) { pr_debug("PLL in powerdown\n"); return Tfa98xx_Error_NoClock; } /* tfa1 needs audio PLL */ if (tfa98xx_dev_family(handle) == 1) { tfa98xx_dsp_system_stable(handle, &status); if (status == 0) { pr_debug("PLL not running\n"); return Tfa98xx_Error_NoClock; } } result = TFA_READ_REG(handle, 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(Tfa98xx_handle_t handle, int lock) { /* unhide lock registers */ tfa98xx_write_register16(handle, (tfa98xx_dev_family(handle) == 1) ? 0x40 : 0x0F, 0x5A6B); /* lock/unlock key2 MTPK */ TFA_WRITE_REG(handle, MTPKEY2, lock ? 0 : 0x5A); /* unhide lock registers */ tfa98xx_write_register16(handle, (tfa98xx_dev_family(handle) == 1) ? 0x40 : 0x0F, 0); } enum Tfa98xx_Error tfa98xx_set_mtp(Tfa98xx_handle_t handle, uint16_t value, uint16_t mask) { unsigned short mtp_old, mtp_new; int loop, status; enum Tfa98xx_Error error; error = tfa98xx_get_mtp(handle, &mtp_old); if (error != Tfa98xx_Error_Ok) return error; mtp_new = (value & mask) | (mtp_old & ~mask); if (mtp_old == mtp_new) /* no change */ return Tfa98xx_Error_Ok; /* assure that the clock is up, else we can't write MTP */ error = tfa98xx_dsp_system_stable(handle, &status); if (error) return error; if (status == 0) return Tfa98xx_Error_NoClock; tfa98xx_key2(handle , 0); /* unlock */ TFA_WRITE_REG(handle, MTP0, mtp_new); /* write to i2c shadow reg */ /* CIMTP=1 start copying all the data from i2c regs_mtp to mtp*/ TFA_SET_BF(handle, CIMTP, 1); /* no check for MTPBUSY here, i2c delay assumed to be enough */ tfa98xx_key2(handle , 1); /* lock */ /* wait until MTP write is done */ for (loop = 0; loop < 100 /*x10ms*/; loop++) { msleep_interruptible(10); /* wait 10ms to avoid busload */ if (TFA_GET_BF(handle, MTPB) == 0) return Tfa98xx_Error_Ok; } return Tfa98xx_Error_StateTimedOut; } /* * clear mtpex * set ACS * start tfa */ int tfa_calibrate(Tfa98xx_handle_t handle) { enum Tfa98xx_Error error; /* clear mtpex */ error = tfa98xx_set_mtp(handle, 0, 0x2); if (error) return error ; /* set ACS/coldboot state */ error = tfaRunColdboot(handle, 1); /* start tfa by playing */ return error; } static short twos(short x) { return (x < 0) ? x+512 : x; } void tfa98xx_set_exttemp(Tfa98xx_handle_t handle, 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(handle, TROS, 1); TFA_SET_BF(handle, EXTTS, twos(ext_temp)); } else { pr_debug("Clearing ext temp settings\n"); TFA_SET_BF(handle, TROS, 0); } } short tfa98xx_get_exttemp(Tfa98xx_handle_t handle) { short ext_temp = (short)TFA_GET_BF(handle, EXTTS); return twos(ext_temp); } /************************** tfa simple bitfield interfacing ***************************************/ /* convenience functions */ enum Tfa98xx_Error tfa98xx_set_volume_level(Tfa98xx_handle_t handle, unsigned short vol) { if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; if (vol > 255) /* restricted to 8 bits */ vol = 255; /* 0x00 -> 0.0 dB * 0x01 -> -0.5 dB * ... * 0xFE -> -127dB * 0xFF -> muted */ /* volume value is in the top 8 bits of the register */ return -TFA_SET_BF(handle, VOL, (uint16_t)vol); } static enum Tfa98xx_Error tfa98xx_set_mute_tfa2(Tfa98xx_handle_t handle, enum Tfa98xx_Mute mute) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; switch (mute) { case Tfa98xx_Mute_Off: TFA_SET_BF(handle, CFSMR, 0); TFA_SET_BF(handle, CFSML, 0); break; case Tfa98xx_Mute_Amplifier: case Tfa98xx_Mute_Digital: TFA_SET_BF(handle, CFSMR, 1); TFA_SET_BF(handle, CFSML, 1); break; default: return Tfa98xx_Error_Bad_Parameter; } return error; } static enum Tfa98xx_Error tfa98xx_set_mute_tfa1(Tfa98xx_handle_t handle, enum Tfa98xx_Mute mute) { enum Tfa98xx_Error error; unsigned short audioctrl_value; unsigned short sysctrl_value; int value; value = TFA_READ_REG(handle, CFSM); /* audio control register */ if (value < 0) return -value; audioctrl_value = (unsigned short)value; value = TFA_READ_REG(handle, 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(handle, CFSM, 0, &audioctrl_value); TFA_SET_BF_VALUE(handle, AMPE, 1, &sysctrl_value); TFA_SET_BF_VALUE(handle, DCA, 1, &sysctrl_value); break; case Tfa98xx_Mute_Digital: /* expect the amplifier to run */ /* set the cf_mute bit */ TFA_SET_BF_VALUE(handle, CFSM, 1, &audioctrl_value); /* set the enbl_amplifier bit */ TFA_SET_BF_VALUE(handle, AMPE, 1, &sysctrl_value); /* clear active mode */ TFA_SET_BF_VALUE(handle, DCA, 0, &sysctrl_value); break; case Tfa98xx_Mute_Amplifier: /* clear the cf_mute bit */ TFA_SET_BF_VALUE(handle, CFSM, 0, &audioctrl_value); /* clear the enbl_amplifier bit and active mode */ TFA_SET_BF_VALUE(handle, AMPE, 0, &sysctrl_value); TFA_SET_BF_VALUE(handle, DCA, 0, &sysctrl_value); break; default: return Tfa98xx_Error_Bad_Parameter; } error = -TFA_WRITE_REG(handle, CFSM, audioctrl_value); if (error) return error; error = -TFA_WRITE_REG(handle, AMPE, sysctrl_value); return error; } enum Tfa98xx_Error tfa98xx_set_mute(Tfa98xx_handle_t handle, enum Tfa98xx_Mute mute) { if (!tfa98xx_handle_is_open(handle)) { pr_err("device not opened\n"); return Tfa98xx_Error_NotOpen; } if (tfa98xx_dev_family(handle) == 1) return tfa98xx_set_mute_tfa1(handle, mute); else return tfa98xx_set_mute_tfa2(handle, mute); } /****************** patching **********************************************************/ static enum Tfa98xx_Error tfa98xx_process_patch_file(Tfa98xx_handle_t handle, 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 > handles_local[handle].buffer_size) { /* too big, must fit buffer */ return Tfa98xx_Error_Bad_Parameter; } error = tfa98xx_write_raw(handle, 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(Tfa98xx_handle_t handle, 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 = handles_local[handle].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(handle, &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 = tfa98xx_dsp_read_mem(handle, 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 != handles_local[handle].rev) { pr_err("patch file device type check failed: expected 0x%02x, actual 0x%02x\n", handles_local[handle].rev, revid); return Tfa98xx_Error_Not_Supported; } } } return error; } #define PATCH_HEADER_LENGTH 6 enum Tfa98xx_Error tfa_dsp_patch(Tfa98xx_handle_t handle, int patchLength, const unsigned char *patchBytes) { enum Tfa98xx_Error error; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; if (patchLength < PATCH_HEADER_LENGTH) return Tfa98xx_Error_Bad_Parameter; error = tfa98xx_check_ic_rom_version(handle, patchBytes); if (Tfa98xx_Error_Ok != error) { return error; } error = tfa98xx_process_patch_file(handle, patchLength - PATCH_HEADER_LENGTH, patchBytes + PATCH_HEADER_LENGTH); return error; } /****************** end patching **********************************************************/ TFA_INTERNAL enum Tfa98xx_Error tfa98xx_wait_result(Tfa98xx_handle_t handle, int wait_retry_count) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; int cf_status; /* the contents of the CF_STATUS register */ int tries = 0; do { cf_status = TFA_GET_BF(handle, ACK); if (cf_status < 0) error = -cf_status; tries++; /* i2c_cmd_ack */ /* don't wait forever, DSP is pretty quick to respond (< 1ms) */ } while ((error == Tfa98xx_Error_Ok) && ((cf_status & CF_STATUS_I2C_CMD_ACK) == 0) && (tries < wait_retry_count)); if (tries >= wait_retry_count) { /* something wrong with communication with DSP */ error = Tfa98xx_Error_DSP_not_running; } return error; } /* * * support functions for data conversion */ /** convert memory bytes to signed 24 bit integers input: bytes contains "num_bytes" byte elements output: data contains "num_bytes/3" int24 elements */ void tfa98xx_convert_bytes2data(int num_bytes, const unsigned char bytes[], int data[]) { int i; /* index for data */ int k; /* index for bytes */ int d; int num_data = num_bytes / 3; _ASSERT((num_bytes % 3) == 0); for (i = 0, k = 0; i < num_data; ++i, k += 3) { d = (bytes[k] << 16) | (bytes[k + 1] << 8) | (bytes[k + 2]); _ASSERT(d >= 0); _ASSERT(d < (1 << 24)); /* max 24 bits in use */ if (bytes[k] & 0x80) /* sign bit was set */ d = -((1 << 24) - d); data[i] = d; } } /** convert signed 24 bit integers to 32bit aligned bytes input: data contains "num_bytes/3" int24 elements output: bytes contains "num_bytes" 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(Tfa98xx_handle_t handle, int length, const char *buffer) { int offset = 0; int chunk_size = ROUND_DOWN(handles_local[handle].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(handle, DMEM); if (value < 0) { error = -value; return error; } cfctl = (uint16_t)value; /* assume no I2C errors from here */ TFA_SET_BF_VALUE(handle, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM, &cfctl); /* set cf ctl to DMEM */ TFA_SET_BF_VALUE(handle, AIF, 0, &cfctl); /* set to autoincrement */ TFA_WRITE_REG(handle, DMEM, cfctl); /* xmem[1] is start of message * direct write to register to save cycles avoiding read-modify-write */ TFA_WRITE_REG(handle, 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(handle, 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(handle, REQCMD, 0x01, &cfctl); /* bit 0 */ TFA_SET_BF_VALUE(handle, CFINT, 1, &cfctl); error = -TFA_WRITE_REG(handle, CFINT, cfctl); } return error; } enum Tfa98xx_Error tfa_dsp_msg_write_id(Tfa98xx_handle_t handle, int length, const char *buffer, uint8_t cmdid[3]) { int offset = 0; int chunk_size = ROUND_DOWN(handles_local[handle].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(handle, DMEM); if (value < 0) { error = -value; return error; } cfctl = (uint16_t)value; /* assume no I2C errors from here */ TFA_SET_BF_VALUE(handle, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM, &cfctl); /* set cf ctl to DMEM */ TFA_SET_BF_VALUE(handle, AIF, 0, &cfctl); /* set to autoincrement */ TFA_WRITE_REG(handle, DMEM, cfctl); /* xmem[1] is start of message * direct write to register to save cycles avoiding read-modify-write */ TFA_WRITE_REG(handle, MADD, 1); /* write cmd-id */ error = tfa98xx_write_data(handle, 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(handle, 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(handle, REQCMD, 0x01, &cfctl); /* bit 0 */ TFA_SET_BF_VALUE(handle, CFINT, 1, &cfctl); error = -TFA_WRITE_REG(handle, 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(Tfa98xx_handle_t handle, int *pRpcStatus) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; error = tfa98xx_wait_result(handle, 2); /* 2 is only one try */ if (error == Tfa98xx_Error_DSP_not_running) { *pRpcStatus = -1; return Tfa98xx_Error_Ok; } else if (error != Tfa98xx_Error_Ok) return error; error = tfa98xx_check_rpc_status(handle, pRpcStatus); return error; } const char *tfa98xx_get_i2c_status_id_string(int status) { const char *p_id_str; char latest_errorstr[64]; switch (status) { case Tfa98xx_DSP_Not_Running: p_id_str = "No response from DSP"; break; case Tfa98xx_I2C_Req_Done: p_id_str = "Ok"; break; case Tfa98xx_I2C_Req_Busy: p_id_str = "Request is being processed"; break; case Tfa98xx_I2C_Req_Invalid_M_ID: p_id_str = "Provided M-ID does not fit in valid rang [0..2]"; break; case Tfa98xx_I2C_Req_Invalid_P_ID: p_id_str = "Provided P-ID is not valid in the given M-ID context"; break; case Tfa98xx_I2C_Req_Invalid_CC: p_id_str = "Invalid channel configuration bits (SC|DS|DP|DC) combination"; break; case Tfa98xx_I2C_Req_Invalid_Seq: p_id_str = "Invalid sequence of commands, in case the DSP expects some commands in a specific order"; break; case Tfa98xx_I2C_Req_Invalid_Param: p_id_str = "Generic error"; break; case Tfa98xx_I2C_Req_Buffer_Overflow: p_id_str = "I2C buffer has overflowed: host has sent too many parameters, memory integrity is not guaranteed"; break; default: snprintf(latest_errorstr, sizeof(latest_errorstr)-1, "Unspecified error (%d)", (int)status); p_id_str = latest_errorstr; break; } return p_id_str; } enum Tfa98xx_Error tfa_dsp_msg_read(Tfa98xx_handle_t handle, int length, unsigned char *bytes) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; int burst_size; /* number of words per burst size */ int bytes_per_word = 3; int num_bytes; int offset = 0; unsigned short start_offset = 2; /* msg starts @xmem[2] ,[1]=cmd */ if (length > TFA2_MAX_PARAM_SIZE) return Tfa98xx_Error_Bad_Parameter; TFA_SET_BF(handle, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM); error = -TFA_WRITE_REG(handle, MADD, start_offset); if (error != Tfa98xx_Error_Ok) return error; num_bytes = length; /* input param */ while (num_bytes > 0) { burst_size = ROUND_DOWN(handles_local[handle].buffer_size, bytes_per_word); if (num_bytes < burst_size) burst_size = num_bytes; error = tfa98xx_read_data(handle, FAM_TFA98XX_CF_MEM, burst_size, bytes + offset); if (error != Tfa98xx_Error_Ok) return error; num_bytes -= burst_size; offset += burst_size; } return Tfa98xx_Error_Ok; } /* * write/read raw msg functions : * the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes. * The functions will return immediately and do not not wait for DSP reponse. */ #define MAX_WORDS (300) enum Tfa98xx_Error tfa_dsp_msg(Tfa98xx_handle_t handle, int length, const char *buf) { enum Tfa98xx_Error error; int tries, rpc_status = Tfa98xx_I2C_Req_Done; /* write the message and notify the DSP */ error = tfa_dsp_msg_write(handle, length, buf); if (error != Tfa98xx_Error_Ok) return error; /* get the result from the DSP (polling) */ for (tries = TFA98XX_WAITRESULT_NTRIES; tries > 0; tries--) { error = tfa_dsp_msg_status(handle, &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 (tfa98xx_runtime_verbose) PRINT("Number of tries: %d \n", TFA98XX_WAITRESULT_NTRIES-tries); */ if (rpc_status != Tfa98xx_I2C_Req_Done) { /* DSP RPC call returned an error */ error = (enum Tfa98xx_Error) (rpc_status + Tfa98xx_Error_RpcBase); pr_debug("DSP msg status: %d (%s)\n", rpc_status, tfa98xx_get_i2c_status_id_string(rpc_status)); } return error; } /** * write/read raw msg functions: * the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes. * The functions will return immediately and do not not wait for DSP reponse. * An ID is added to modify the command-ID */ enum Tfa98xx_Error tfa_dsp_msg_id(Tfa98xx_handle_t handle, 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(handle, 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(handle, &rpc_status); if (error == Tfa98xx_Error_Ok && rpc_status == Tfa98xx_I2C_Req_Done) break; } /* if (tfa98xx_runtime_verbose) PRINT("Number of tries: %d \n", TFA98XX_WAITRESULT_NTRIES-tries); */ if (rpc_status != Tfa98xx_I2C_Req_Done) { /* DSP RPC call returned an error */ error = (enum Tfa98xx_Error) (rpc_status + Tfa98xx_Error_RpcBase); pr_debug("DSP msg status: %d (%s)\n", rpc_status, tfa98xx_get_i2c_status_id_string(rpc_status)); } return error; } /* read the return code for the RPC call */ TFA_INTERNAL enum Tfa98xx_Error tfa98xx_check_rpc_status(Tfa98xx_handle_t handle, int *pRpcStatus) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; /* the value to sent to the * CF_CONTROLS register: cf_req=00000000, * cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */ unsigned short cf_ctrl = 0x0002; /* memory address to be accessed (0: Status, 1: ID, 2: parameters) */ unsigned short cf_mad = 0x0000; unsigned char mem[3]; /* for the status read from DSP memory */ if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; if (pRpcStatus == 0) return Tfa98xx_Error_Bad_Parameter; /* 1) write DMEM=XMEM to the DSP XMEM */ #define OPTIMIZED_RPC #ifdef OPTIMIZED_RPC /* TODO this only save 2 cycles so remove? */ { /* 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(handle, FAM_TFA98XX_CF_CONTROLS, sizeof(buffer), buffer); } #else /* (not) OPTIMIZED_RPC */ if (error == Tfa98xx_Error_Ok) { error = tfa98xx_write_register16(handle, FAM_TFA98XX_CF_CONTROLS, cf_ctrl); } if (error == Tfa98xx_Error_Ok) { /* write the address in XMEM where to read */ error = -TFA_WRITE_REG(handle, MADD, cf_mad); } #endif /* OPTIMIZED_RPC */ if (error == Tfa98xx_Error_Ok) { /* read 1 word (24 bit) from XMEM */ error = tfa98xx_read_data(handle, FAM_TFA98XX_CF_MEM, sizeof(mem), mem); } if (error == Tfa98xx_Error_Ok) *pRpcStatus = (mem[0] << 16) | (mem[1] << 8) | mem[2]; return error; } /***************************** xmem only **********************************/ enum Tfa98xx_Error tfa98xx_dsp_read_mem(Tfa98xx_handle_t handle, unsigned int start_offset, int num_words, int *pValues) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; unsigned char *bytes; int burst_size; /* number of words per burst size */ const int bytes_per_word = 3; int dmem; int num_bytes; int *p; bytes = (unsigned char *)kmalloc(num_words*bytes_per_word, 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; TFA_SET_BF(handle, DMEM, (uint16_t)dmem); /* Remove offset from adress */ start_offset = start_offset & 0xffff; error = -TFA_WRITE_REG(handle, MADD, (unsigned short)start_offset); if (error != Tfa98xx_Error_Ok) goto tfa98xx_dsp_read_mem_exit; num_bytes = num_words * bytes_per_word; p = pValues; for (; num_bytes > 0;) { burst_size = ROUND_DOWN(handles_local[handle].buffer_size, bytes_per_word); if (num_bytes < burst_size) burst_size = num_bytes; _ASSERT(burst_size <= sizeof(bytes)); error = tfa98xx_read_data(handle, 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: kfree(bytes); return error; } enum Tfa98xx_Error tfa98xx_dsp_write_mem_word(Tfa98xx_handle_t handle, unsigned short address, int value, int memtype) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; unsigned char bytes[3]; TFA_SET_BF(handle, DMEM, (uint16_t)memtype); error = -TFA_WRITE_REG(handle, MADD, address); if (error != Tfa98xx_Error_Ok) return error; tfa98xx_convert_data2bytes(1, &value, bytes); error = tfa98xx_write_data(handle, FAM_TFA98XX_CF_MEM, 3, bytes); if (error != Tfa98xx_Error_Ok) return error; return Tfa98xx_Error_Ok; } enum Tfa98xx_Error tfa_cont_write_filterbank(int device, nxpTfaFilter_t *filter) { unsigned char biquad_index; enum Tfa98xx_Error error = Tfa98xx_Error_Ok; for (biquad_index = 0; biquad_index < 10; biquad_index++) { if (filter[biquad_index].enabled) { error = tfa_dsp_cmd_id_write(device, MODULE_BIQUADFILTERBANK, biquad_index+1, /* start @1 */ sizeof(filter[biquad_index].biquad.bytes), filter[biquad_index].biquad.bytes); } else { error = Tfa98xx_DspBiquad_Disable(device, biquad_index+1); } if (error) return error; } return error; } enum Tfa98xx_Error Tfa98xx_DspBiquad_Disable(Tfa98xx_handle_t handle, int biquad_index) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; int coeff_buffer[BIQUAD_COEFF_SIZE]; unsigned char bytes[3 + BIQUAD_COEFF_SIZE * 3]; if (biquad_index > TFA98XX_BIQUAD_NUM) return Tfa98xx_Error_Bad_Parameter; if (biquad_index < 1) return Tfa98xx_Error_Bad_Parameter; /* make opcode */ /* 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 bits data */ tfa98xx_convert_data2bytes(BIQUAD_COEFF_SIZE, coeff_buffer, &bytes[3]); bytes[0] = 0; bytes[1] = MODULE_BIQUADFILTERBANK+128; bytes[2] = (unsigned char)biquad_index; error = tfa_dsp_msg(handle, 3 + BIQUAD_COEFF_SIZE * 3, (char *)bytes); return error; } /* wrapper for dsp_msg that adds opcode */ enum Tfa98xx_Error tfa_dsp_cmd_id_write(Tfa98xx_handle_t handle, unsigned char module_id, unsigned char param_id, int num_bytes, const unsigned char data[]) { enum Tfa98xx_Error error; unsigned char *buffer; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; buffer = kmalloc(3 + num_bytes, GFP_KERNEL); if (buffer == NULL) return Tfa98xx_Error_Fail; buffer[0] = handles_local[handle].spkr_select; buffer[1] = module_id + 128; buffer[2] = param_id; memcpy(&buffer[3], data, num_bytes); error = tfa_dsp_msg(handle, 3 + num_bytes, (char *)buffer); kfree(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(Tfa98xx_handle_t handle, unsigned char module_id, unsigned char param_id, int num_bytes, unsigned char data[]) { enum Tfa98xx_Error error; unsigned char buffer[3]; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; buffer[0] = handles_local[handle].spkr_select; buffer[1] = module_id + 128; buffer[2] = param_id; error = tfa_dsp_msg(handle, sizeof(unsigned char[3]), (char *)buffer); if (error != Tfa98xx_Error_Ok) return error; /* read the data from the dsp */ error = tfa_dsp_msg_read(handle, num_bytes, data); return error; } /* wrapper for dsp_msg that adds opcode and 3 bytes required for coefs */ enum Tfa98xx_Error tfa_dsp_cmd_id_coefs(Tfa98xx_handle_t handle, unsigned char module_id, unsigned char param_id, int num_bytes, unsigned char data[]) { enum Tfa98xx_Error error; unsigned char buffer[6]; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; buffer[0] = handles_local[handle].spkr_select; buffer[1] = module_id + 128; buffer[2] = param_id; buffer[3] = 0; buffer[4] = 0; buffer[5] = 0; error = tfa_dsp_msg(handle, sizeof(unsigned char[6]), (char *)buffer); if (error != Tfa98xx_Error_Ok) return error; /* read the data from the dsp */ error = tfa_dsp_msg_read(handle, num_bytes, data); return error; } /* wrapper for dsp_msg that adds opcode and 3 bytes required for MBDrcDynamics */ enum Tfa98xx_Error tfa_dsp_cmd_id_MBDrc_dynamics(Tfa98xx_handle_t handle, unsigned char module_id, unsigned char param_id, int index_subband, int num_bytes, unsigned char data[]) { enum Tfa98xx_Error error; unsigned char buffer[6]; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; buffer[0] = handles_local[handle].spkr_select; buffer[1] = module_id + 128; buffer[2] = param_id; buffer[3] = 0; buffer[4] = 0; buffer[5] = (unsigned char)index_subband; error = tfa_dsp_msg(handle, sizeof(unsigned char[6]), (char *)buffer); if (error != Tfa98xx_Error_Ok) return error; /* read the data from the dsp */ error = tfa_dsp_msg_read(handle, num_bytes, data); return error; } enum Tfa98xx_Error tfa98xx_dsp_write_preset(Tfa98xx_handle_t handle, int length, const unsigned char *p_preset_bytes) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; if (p_preset_bytes != 0) { /* by design: keep the data opaque and no * interpreting/calculation */ error = tfa_dsp_cmd_id_write(handle, 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(Tfa98xx_handle_t handle, int *features) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; uint32_t value; uint16_t mtpbf; /* return the cache data if it's valid */ if (handles_local[handle].hw_feature_bits != -1) { *features = handles_local[handle].hw_feature_bits; } else { /* for tfa1 check if we have clock */ if (tfa98xx_dev_family(handle) == 1) { int status; tfa98xx_dsp_system_stable(handle, &status); if (!status) { get_hw_features_from_cnt(handle, 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(handle, mtpbf) & 0xffff; *features = handles_local[handle].hw_feature_bits = value; } return error; } enum Tfa98xx_Error tfa98xx_dsp_get_sw_feature_bits(Tfa98xx_handle_t handle, int features[2]) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; unsigned char bytes[3 * 2]; /* return the cache data if it's valid */ if (handles_local[handle].sw_feature_bits[0] != -1) { features[0] = handles_local[handle].sw_feature_bits[0]; features[1] = handles_local[handle].sw_feature_bits[1]; } else { /* for tfa1 check if we have clock */ if (tfa98xx_dev_family(handle) == 1) { int status; tfa98xx_dsp_system_stable(handle, &status); if (!status) { get_sw_features_from_cnt(handle, features); /* skip reading MTP: */ return (features[0] == -1) ? Tfa98xx_Error_Fail : Tfa98xx_Error_Ok; } } error = tfa_dsp_cmd_id_write_read(handle, MODULE_FRAMEWORK, FW_PAR_ID_GET_FEATURE_INFO, sizeof(bytes), bytes); if (error != Tfa98xx_Error_Ok) { /* old ROM code may respond with Tfa98xx_Error_RpcParamId */ return error; } tfa98xx_convert_bytes2data(sizeof(bytes), bytes, features); } return error; } enum Tfa98xx_Error tfa98xx_dsp_get_state_info(Tfa98xx_handle_t handle, unsigned char bytes[], unsigned int *statesize) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; int bSupportFramework = 0; unsigned int stateSize = 9; err = tfa98xx_dsp_support_framework(handle, &bSupportFramework); if (err == Tfa98xx_Error_Ok) { if (bSupportFramework) { err = tfa_dsp_cmd_id_write_read(handle, MODULE_FRAMEWORK, FW_PARAM_GET_STATE, 3 * stateSize, bytes); } else { /* old ROM code, ask SpeakerBoost and only do first portion */ stateSize = 8; err = tfa_dsp_cmd_id_write_read(handle, MODULE_SPEAKERBOOST, SB_PARAM_GET_STATE, 3 * stateSize, bytes); } } *statesize = stateSize; return err; } enum Tfa98xx_Error tfa98xx_dsp_support_drc(Tfa98xx_handle_t handle, int *pbSupportDrc) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; *pbSupportDrc = 0; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; if (handles_local[handle].supportDrc != supportNotSet) { *pbSupportDrc = (handles_local[handle].supportDrc == supportYes); } else { int featureBits[2]; error = tfa98xx_dsp_get_sw_feature_bits(handle, featureBits); if (error == Tfa98xx_Error_Ok) { /* easy case: new API available */ /* bit=0 means DRC enabled */ *pbSupportDrc = (featureBits[0] & FEATURE1_DRC) == 0; } else if (error == Tfa98xx_Error_RpcParamId) { /* older ROM code, doesn't support it */ *pbSupportDrc = 0; error = Tfa98xx_Error_Ok; } /* else some other error, return transparently */ /* pbSupportDrc only changed when error == Tfa98xx_Error_Ok */ if (error == Tfa98xx_Error_Ok) { handles_local[handle].supportDrc = *pbSupportDrc ? supportYes : supportNo; } } return error; } enum Tfa98xx_Error tfa98xx_dsp_support_framework(Tfa98xx_handle_t handle, int *pbSupportFramework) { int featureBits[2] = { 0, 0 }; enum Tfa98xx_Error error = Tfa98xx_Error_Ok; _ASSERT(pbSupportFramework != 0); if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; if (handles_local[handle].supportFramework != supportNotSet) { if (handles_local[handle].supportFramework == supportNo) *pbSupportFramework = 0; else *pbSupportFramework = 1; } else { error = tfa98xx_dsp_get_sw_feature_bits(handle, featureBits); if (error == Tfa98xx_Error_Ok) { *pbSupportFramework = 1; handles_local[handle].supportFramework = supportYes; } else { *pbSupportFramework = 0; handles_local[handle].supportFramework = supportNo; error = Tfa98xx_Error_Ok; } } /* *pbSupportFramework only changed when error == Tfa98xx_Error_Ok */ return error; } enum Tfa98xx_Error tfa98xx_dsp_write_speaker_parameters(Tfa98xx_handle_t handle, int length, const unsigned char *p_speaker_bytes) { enum Tfa98xx_Error error; if (p_speaker_bytes != 0) { /* by design: keep the data opaque and no * interpreting/calculation */ /* Use long WaitResult retry count */ error = tfa_dsp_cmd_id_write( handle, MODULE_SPEAKERBOOST, SB_PARAM_SET_LSMODEL, length, p_speaker_bytes); } else { error = Tfa98xx_Error_Bad_Parameter; } #if (defined(TFA9887B) || defined(TFA98XX_FULL)) { int bSupportDrc; if (error != Tfa98xx_Error_Ok) return error; error = tfa98xx_dsp_support_drc(handle, &bSupportDrc); if (error != Tfa98xx_Error_Ok) return error; if (bSupportDrc) { /* Need to set AgcGainInsert back to PRE, * as the SetConfig forces it to POST */ uint8_t bytes[3] = {0, 0, 0}; error = tfa_dsp_cmd_id_write(handle, MODULE_SPEAKERBOOST, SB_PARAM_SET_AGCINS, sizeof(bytes), bytes); } } #endif return error; } enum Tfa98xx_Error tfa98xx_dsp_write_config(Tfa98xx_handle_t handle, int length, const unsigned char *p_config_bytes) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; error = tfa_dsp_cmd_id_write(handle, MODULE_SPEAKERBOOST, SB_PARAM_SET_CONFIG, length, p_config_bytes); #if (defined(TFA9887B) || defined(TFA98XX_FULL)) { int bSupportDrc; if (error != Tfa98xx_Error_Ok) return error; error = tfa98xx_dsp_support_drc(handle, &bSupportDrc); if (error != Tfa98xx_Error_Ok) return error; if (bSupportDrc) { /* Need to set AgcGainInsert back to PRE, * as the SetConfig forces it to POST */ uint8_t bytes[3] = {0, 0, 0}; error = tfa_dsp_cmd_id_write(handle, MODULE_SPEAKERBOOST, SB_PARAM_SET_AGCINS, sizeof(bytes), bytes); } } #endif return error; } #if (defined(TFA9887B) || defined(TFA98XX_FULL)) /* load all the parameters for the DRC settings from a file */ enum Tfa98xx_Error tfa98xx_dsp_write_drc(Tfa98xx_handle_t handle, int length, const unsigned char *p_drc_bytes) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; if (p_drc_bytes != 0) { error = tfa_dsp_cmd_id_write(handle, MODULE_SPEAKERBOOST, SB_PARAM_SET_DRC, length, p_drc_bytes); } else { error = Tfa98xx_Error_Bad_Parameter; } return error; } #endif enum Tfa98xx_Error tfa98xx_powerdown(Tfa98xx_handle_t handle, int powerdown) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; TFA_SET_BF(handle, PWDN, (uint16_t)powerdown); return error; } enum Tfa98xx_Error tfa98xx_select_mode(Tfa98xx_handle_t handle, enum Tfa98xx_Mode mode) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; if (!tfa98xx_handle_is_open(handle)) return Tfa98xx_Error_NotOpen; if (error == Tfa98xx_Error_Ok) { switch (mode) { default: error = Tfa98xx_Error_Bad_Parameter; } } return error; } int tfa_set_bf(Tfa98xx_handle_t dev_idx, 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 = tfa98xx_read_register16(dev_idx, address, ®value); if (err) { pr_err("Error getting bf :%d \n", -err); return -err; } oldvalue = regvalue; msk = ((1<<(len+1))-1)<> 4) & 0x0f; uint8_t address = (bf >> 8) & 0xff; err = tfa98xx_read_register16(dev_idx, address, ®value); if (err) { pr_err("Error getting bf :%d \n", -err); return -err; } msk = ((1<<(len+1))-1)<> 4) & 0x0f; uint8_t address = (bf >> 8) & 0xff; err = tfa98xx_read_register16(dev_idx, address, ®value); if (err) { pr_err("Error getting bf :%d \n", -err); return -err; } msk = ((1<<(len+1))-1)<>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)<> 4) & 0x0f; msk = ((1<<(len+1))-1)<> pos; return value; } int tfa_write_reg(Tfa98xx_handle_t dev_idx, 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 = tfa98xx_write_register16(dev_idx, address, reg_value); if (err) return -err; return 0; } int tfa_read_reg(Tfa98xx_handle_t dev_idx, const uint16_t bf) { enum Tfa98xx_Error err; uint16_t regvalue; /* bitfield enum - 8..15 : address */ uint8_t address = (bf >> 8) & 0xff; err = tfa98xx_read_register16(dev_idx, address, ®value); if (err) return -err; return regvalue; } /* * powerup the coolflux subsystem and wait for it */ enum Tfa98xx_Error tfa_cf_powerup(Tfa98xx_handle_t handle) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; int tries, status; /* power on the sub system */ TFA_SET_BF_VOLATILE(handle, PWDN, 0); /* wait until everything is stable, in case clock has been off */ if (tfa98xx_runtime_verbose) pr_info("Waiting for DSP system stable...\n"); for (tries = CFSTABLE_TRIES; tries > 0; tries--) { err = tfa98xx_dsp_system_stable(handle, &status); _ASSERT(err == Tfa98xx_Error_Ok); if (status) break; else msleep_interruptible(10); /* wait 10ms to avoid busload */ } if (tries == 0) {/* timedout */ pr_err("DSP subsystem start timed out\n"); return Tfa98xx_Error_StateTimedOut; } return err; } /* * Enable/Disable the I2S output for TFA1 devices * without TDM interface */ static enum Tfa98xx_Error tfa98xx_aec_output(Tfa98xx_handle_t handle, int enable) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; if ((tfa98xx_get_device_dai(handle) & Tfa98xx_DAI_TDM) == Tfa98xx_DAI_TDM) return err; if (tfa98xx_dev_family(handle) == 1) err = -tfa_set_bf(handle, 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 */ enum Tfa98xx_Error show_current_state(Tfa98xx_handle_t handle) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; int manstate = -1; if (tfa98xx_dev_family(handle) == 2) { manstate = TFA_GET_BF(handle, MANSTATE); if (manstate < 0) return -manstate; } pr_debug("Current HW manager state: "); switch (manstate) { case 0: pr_debug("power_down_state \n"); break; case 1: pr_debug("wait_for_source_settings_state \n"); break; case 2: pr_debug("connnect_pll_input_state \n"); break; case 3: pr_debug("disconnect_pll_input_state \n"); break; case 4: pr_debug("enable_pll_state \n"); break; case 5: pr_debug("enable_cgu_state \n"); break; case 6: pr_debug("init_cf_state \n"); break; case 7: pr_debug("enable_amplifier_state \n"); break; case 8: pr_debug("alarm_state \n"); break; case 9: pr_debug("operating_state \n"); break; case 10: pr_debug("mute_audio_state \n"); break; case 11: pr_debug("disable_cgu_pll_state \n"); break; default: pr_debug("Unable to find current state \n"); break; } return err; } /* * set OTC */ static enum Tfa98xx_Error tfa_set_calibrate_once(Tfa98xx_handle_t handle) { enum Tfa98xx_Error error; error = tfa98xx_set_mtp(handle, 1<length; i++) { if (dev->list[i].type == dscNoInit) { noinit = 1; break; } } if (!noinit) { /* load the optimal TFA98XX in HW settings */ err = tfa98xx_init(handle); PRINT_ASSERT(err); } else { pr_debug("\nWarning: No init keyword found in the cnt file. Init is skipped! \n"); } /* I2S settings to define the audio input properties * these must be set before the subsys is up * this will run the list until a non-register item is encountered */ err = tfaContWriteRegsDev(handle); /* write device register settings */ PRINT_ASSERT(err); /* also write register the settings from the default profile NOTE we may still have ACS=1 so we can switch sample rate here */ err = tfaContWriteRegsProf(handle, profile); PRINT_ASSERT(err); if (tfa98xx_dev_family(handle) == 2) { /* Factory trimming for the Boost converter */ tfa_factory_trimmer(handle); } /* leave power off state */ err = tfa98xx_powerdown(handle, 0); PRINT_ASSERT(err); if (tfa98xx_dev_family(handle) == 2) { /* signal that the clock settings are done * - PLL can start */ TFA_SET_BF_VOLATILE(handle, MANSCONF, 1); } /* wait until the PLL is ready * note that the DSP CPU is not running (RST=1) */ if (tfa98xx_runtime_verbose) { if (TFA_GET_BF(handle, NOCLK)) pr_debug("Using internal clock\n"); pr_debug("Waiting for DSP system stable...\n"); } for (tries = 1; tries < CFSTABLE_TRIES; tries++) { err = tfa98xx_dsp_system_stable(handle, &status); _ASSERT(err == Tfa98xx_Error_Ok); if (status) break; else msleep_interruptible(10); /* wait 10ms to avoid busload */ } if (tries == CFSTABLE_TRIES) { if (tfa98xx_runtime_verbose) pr_debug("Timed out\n"); return Tfa98xx_Error_StateTimedOut; } else if (tfa98xx_runtime_verbose) pr_debug(" OK (tries=%d)\n", tries); if (tfa98xx_runtime_verbose && tfa98xx_dev_family(handle) == 2) err = show_current_state(handle); return err; } /* * run the startup/init sequence and set ACS bit */ enum Tfa98xx_Error tfaRunColdStartup(Tfa98xx_handle_t handle, int profile) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; err = tfaRunStartup(handle, profile); PRINT_ASSERT(err); if (err) return err; /* force cold boot */ err = tfaRunColdboot(handle, 1); /* set ACS */ PRINT_ASSERT(err); if (err) return err; /* start */ err = tfaRunStartDSP(handle); PRINT_ASSERT(err); return err; } /* * */ enum Tfa98xx_Error tfaRunMute(Tfa98xx_handle_t handle) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; int status; int tries = 0; /* signal the TFA98XX to mute */ if (tfa98xx_dev_family(handle) == 1) { err = tfa98xx_set_mute(handle, Tfa98xx_Mute_Amplifier); if (err == Tfa98xx_Error_Ok) { /* now wait for the amplifier to turn off */ do { status = TFA_GET_BF(handle, SWS); if (status != 0) msleep_interruptible(10); /* wait 10ms to avoid busload */ else break; tries++; } while (tries < AMPOFFWAIT_TRIES); if (tfa98xx_runtime_verbose) pr_debug("-------------------- muted --------------------\n"); /*The amplifier is always switching*/ if (tries == AMPOFFWAIT_TRIES) return Tfa98xx_Error_Other; } } return err; } /* * */ enum Tfa98xx_Error tfaRunUnmute(Tfa98xx_handle_t handle) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; /* signal the TFA98XX to mute */ err = tfa98xx_set_mute(handle, Tfa98xx_Mute_Off); if (tfa98xx_runtime_verbose) pr_debug("-------------------unmuted ------------------\n"); return err; } /* * wait for calibrateDone */ enum Tfa98xx_Error tfaRunWaitCalibration(Tfa98xx_handle_t handle, int *calibrateDone) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; int tries = 0, mtp_busy = 1, tries_mtp_busy = 0; *calibrateDone = 0; /* in case of calibrate once wait for MTPEX */ if (TFA_GET_BF(handle, MTPOTC)) { /* Check if MTP_busy is clear! */ while (tries_mtp_busy < MTPBWAIT_TRIES) { mtp_busy = TFA_GET_BF(handle, MTPB); if (mtp_busy == 1) msleep_interruptible(10); /* wait 10ms to avoid busload */ else break; tries_mtp_busy++; } if (tries_mtp_busy < MTPBWAIT_TRIES) { /* Because of the msleep TFA98XX_API_WAITRESULT_NTRIES is way to long! * Setting this to 25 will take it atleast 25*50ms = 1.25 sec */ while ((*calibrateDone == 0) && (tries < 25)) { *calibrateDone = TFA_GET_BF(handle, MTPEX); if (*calibrateDone == 1) break; msleep_interruptible(50); /* wait 50ms to avoid busload */ tries++; } if (tries >= 25) { tries = TFA98XX_API_WAITRESULT_NTRIES; } } } /* poll xmem for calibrate always * calibrateDone = 0 means "calibrating", * calibrateDone = -1 (or 0xFFFFFF) means "fails" * calibrateDone = 1 means calibration done */ while ((*calibrateDone != 1) && (tries < TFA98XX_API_WAITRESULT_NTRIES)) { err = tfa98xx_dsp_read_mem(handle, TFA_FW_XMEM_CALIBRATION_DONE, 1, calibrateDone); tries++; } if (*calibrateDone != 1) { pr_err("Calibration failed! \n"); err = Tfa98xx_Error_Bad_Parameter; } else if (tries == TFA98XX_API_WAITRESULT_NTRIES) { pr_debug("Calibration has timedout! \n"); err = Tfa98xx_Error_StateTimedOut; } else if (tries_mtp_busy == 1000) { pr_err("Calibrate Failed: MTP_busy stays high! \n"); err = Tfa98xx_Error_StateTimedOut; } /* Check which speaker calibration failed. Only for 88C */ if ((err != Tfa98xx_Error_Ok) && ((handles_local[handle].rev & 0x0FFF) == 0xc88)) { individual_calibration_results(handle); } #ifdef CONFIG_DEBUG_FS tfa98xx_deferred_calibration_status(handle, *calibrateDone); #endif return err; } enum tfa_error tfa_start(int next_profile, int *vstep) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; int dev, devcount = tfa98xx_cnt_max_device(); int cal_profile = -1, istap_prof = 0, active_profile = -1; if (devcount < 1) { pr_err("No or wrong container file loaded\n"); return tfa_error_bad_param; } for (dev = 0; dev < devcount; dev++) { err = tfaContOpen(dev); if (err != Tfa98xx_Error_Ok) goto error_exit; /* Get currentprofile */ active_profile = tfa_get_swprof(dev); if (active_profile == 0xff) active_profile = -1; /* Search if there is a calibration profile * Only if the user did not give a specific profile and coldstart */ if (active_profile == -1 && next_profile < 1) { cal_profile = tfaContGetCalProfile(dev); if (cal_profile >= 0) next_profile = cal_profile; } /* Check if next profile is a tap profile */ istap_prof = tfaContIsTapProfile(dev, next_profile); /* tfaRun_SpeakerBoost implies un-mute */ if (tfa98xx_runtime_verbose) { pr_debug("active_profile:%s, next_profile:%s\n", tfaContProfileName(dev, active_profile), tfaContProfileName(dev, next_profile)); pr_debug("Starting device [%s]\n", tfaContDeviceName(dev)); if (tfa98xx_dev_family(dev) == 2) { err = show_current_state(dev); } } /* enable I2S output on TFA1 devices without TDM */ err = tfa98xx_aec_output(dev, 1); if (err != Tfa98xx_Error_Ok) goto error_exit; /* Check if we need coldstart or ACS is set */ err = tfaRunSpeakerBoost(dev, 0, next_profile); if (err != Tfa98xx_Error_Ok) goto error_exit; active_profile = tfa_get_swprof(dev); /* After loading calibration profile we need to load acoustic shock profile */ if (cal_profile >= 0) { next_profile = 0; pr_debug("Loading %s profile! \n", tfaContProfileName(dev, next_profile)); } } for (dev = 0; dev < devcount; dev++) { /* check if the profile and steps are the one we want */ /* was it not done already */ if ((next_profile != active_profile && active_profile > -1/*0*/) /* HTC_AUD */ || (istap_prof == 1)) { err = tfaContWriteProfile(dev, next_profile, vstep[dev]); if (err != Tfa98xx_Error_Ok) goto error_exit; } /* If the profile contains the .standby suffix go to powerdown * else we should be in operating state */ if (strnstr(tfaContProfileName(dev, next_profile), ".standby", strlen(tfaContProfileName(dev, next_profile))) != NULL) { err = tfa98xx_powerdown(dev, 1); } else if (TFA_GET_BF(dev, PWDN) != 0) { err = tfa98xx_powerdown(dev, 0); } if (err != Tfa98xx_Error_Ok) goto error_exit; if (tfa98xx_runtime_verbose && tfa98xx_dev_family(dev) == 2) err = show_current_state(dev); /* Always search and apply filters after a startup */ err = tfa_set_filters(dev, next_profile); if (err != Tfa98xx_Error_Ok) goto error_exit; #ifdef __KERNEL__ /* To write something in state 6 we need to be sure that SBSL is also set in IOMEM! * More information can be found in the document about the powerswitch */ if (tfa98xx_dev_family(dev) == 2) { if ((TFA_GET_BF(dev, RST) == 1) && (TFA_GET_BF(dev, SBSL) == 0) && (TFA_GET_BF(dev, MANSTATE) == 6)) { TFA_SET_BF_VOLATILE(dev, SBSL, 0); TFA_SET_BF(dev, RST, 0); } } #endif if (vstep[dev] != tfaContGetCurrentVstep(dev) && vstep[dev] != -1) { err = tfaContWriteFilesVstep(dev, next_profile, vstep[dev]); if (err != Tfa98xx_Error_Ok) goto error_exit; } tfa_set_swprof(dev, (unsigned short)next_profile); tfa_set_swvstep(dev, (unsigned short)tfaContGetCurrentVstep(dev)); } error_exit: if (tfa98xx_runtime_verbose && tfa98xx_dev_family(dev) == 2) show_current_state(dev); for (dev = 0; dev < devcount; dev++) { tfaRunUnmute(dev); /* unmute */ tfaContClose(dev); /* close all of them */ } return err; } enum tfa_error tfa_stop(void) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; int dev, devcount = tfa98xx_cnt_max_device(); if (devcount == 0) { pr_err("No or wrong container file loaded\n"); return tfa_error_bad_param; } for (dev = 0; dev < devcount; dev++) { err = tfaContOpen(dev); if (err != Tfa98xx_Error_Ok) goto error_exit; if (tfa98xx_runtime_verbose) pr_debug("Stopping device [%s]\n", tfaContDeviceName(dev)); /* mute */ tfaRunMute(dev); /* powerdown CF */ err = tfa98xx_powerdown(dev, 1); if (err != Tfa98xx_Error_Ok) goto error_exit; /* disable I2S output on TFA1 devices without TDM */ err = tfa98xx_aec_output(dev, 0); if (err != Tfa98xx_Error_Ok) goto error_exit; } error_exit: for (dev = 0; dev < devcount; dev++) tfaContClose(dev); /* close all of them */ return err; } /* * int registers and coldboot dsp */ int tfa98xx_reset(Tfa98xx_handle_t handle) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; TFA_SET_BF_VOLATILE(handle, I2CR, 1); /* restore MANSCONF and MANCOLD to POR state */ TFA_SET_BF_VOLATILE(handle, MANSCONF, 0); TFA_SET_BF_VOLATILE(handle, MANCOLD, 1); /* powerup CF to access CF io */ tfa98xx_powerdown(handle, 0); /* for clock */ err = tfa_cf_powerup(handle); PRINT_ASSERT(err); /* force cold boot */ err = tfaRunColdboot(handle, 1); /* set ACS */ PRINT_ASSERT(err); /* reset all i2C registers to default */ err = -TFA_SET_BF(handle, I2CR, 1); PRINT_ASSERT(err); return err; } enum tfa_error tfa_reset(void) { enum Tfa98xx_Error err = Tfa98xx_Error_Ok; int dev, devcount = tfa98xx_cnt_max_device(); for (dev = 0; dev < devcount; dev++) { err = tfaContOpen(dev); if (err != Tfa98xx_Error_Ok) break; if (tfa98xx_runtime_verbose) pr_debug("resetting device [%s]\n", tfaContDeviceName(dev)); err = tfa98xx_reset(dev); if (err != Tfa98xx_Error_Ok) break; } for (dev = 0; dev < devcount; dev++) { tfaContClose(dev); } return err; } /* * Write all the bytes specified by num_bytes and data */ enum Tfa98xx_Error tfa98xx_write_data(Tfa98xx_handle_t handle, 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 *)kmalloc(bytes2write, 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(handle, bytes2write, write_data); kfree (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(Tfa98xx_handle_t handle) { enum Tfa98xx_Error error = Tfa98xx_Error_Ok; int spkr_count, nr_bytes, i; unsigned char bytes[6] = {0}; int data[2]; error = tfa98xx_supported_speakers(handle, &spkr_count); if (error == Tfa98xx_Error_Ok) { /* If calibrate=once then get values from MTP */ if (TFA_GET_BF(handle, MTPOTC) && ((handles_local[handle].rev & 0xff) == 0x88)) { if (tfa98xx_runtime_verbose) pr_debug("Getting calibration values from MTP\n"); for (i = 0; i < spkr_count; i++) { handles_local[handle].mohm[i] = tfa_read_reg(handle, (uint16_t)TFA_MK_BF((0xF4+i), 0, 16)); } } else { /* Get values from speakerboost */ if (tfa98xx_runtime_verbose) pr_debug("Getting calibration values from Speakerboost\n"); nr_bytes = spkr_count * 3; error = tfa_dsp_cmd_id_write_read(handle, MODULE_SPEAKERBOOST, SB_PARAM_GET_RE0, nr_bytes, bytes); if (error == Tfa98xx_Error_Ok) { tfa98xx_convert_bytes2data(nr_bytes, bytes, data); for (i = 0; i < spkr_count; i++) { handles_local[handle].mohm[i] = (data[i]*1000)/TFA_FW_ReZ_SCALE; } } else { for (i = 0; i < spkr_count; i++) handles_local[handle].mohm[i] = -1; } } } return error; } /* start count from 1, 0 is invalid */ int tfa_get_swprof(Tfa98xx_handle_t handle) { /* get from register if not set yet */ if (handles_local[handle].profile < 0) /* get current profile, consider invalid if 0 */ handles_local[handle].profile = TFA_GET_BF(handle, SWPROFIL)-1; return handles_local[handle].profile; } int tfa_set_swprof(Tfa98xx_handle_t handle, unsigned short new_value) { int mtpk, active_value = tfa_get_swprof(handle); handles_local[handle].profile = new_value; if (handles_local[handle].tfa_family > 1) { TFA_SET_BF_VOLATILE(handle, SWPROFIL, new_value+1); } else { /* it's in MTP shadow, so unlock if not done already */ mtpk = TFA_GET_BF(handle, MTPK); /* get current key */ TFA_SET_BF_VOLATILE(handle, MTPK, 0x5a); TFA_SET_BF_VOLATILE(handle, SWPROFIL, new_value+1); /* set current profile */ TFA_SET_BF_VOLATILE(handle, MTPK, (uint16_t)mtpk); /* restore key */ } return active_value; } /* same value for all channels * start count from 1, 0 is invalid */ int tfa_get_swvstep(Tfa98xx_handle_t handle) { int value; if (handles_local[handle].vstep[0] > 0) return handles_local[handle].vstep[0]-1; value = TFA_GET_BF(handle, SWVSTEP); /* get current vstep[0] */ handles_local[handle].vstep[0] = value; handles_local[handle].vstep[1] = value; return value-1; /* invalid if 0 */ } int tfa_set_swvstep(Tfa98xx_handle_t handle, unsigned short new_value) { int mtpk, active_value = tfa_get_swvstep(handle); handles_local[handle].vstep[0] = new_value; handles_local[handle].vstep[1] = new_value; if (handles_local[handle].tfa_family > 1) { TFA_SET_BF_VOLATILE(handle, SWVSTEP, new_value+1); } else { /* it's in MTP shadow, so unlock if not done already */ mtpk = TFA_GET_BF(handle, MTPK); /* get current key */ TFA_SET_BF_VOLATILE(handle, MTPK, 0x5a); TFA_SET_BF_VOLATILE(handle, SWVSTEP, new_value+1); /* set current vstep[0] */ TFA_SET_BF_VOLATILE(handle, MTPK, (uint16_t)mtpk); /* restore key */ } return active_value; }