/* * Copyright 2015 NXP Semiconductors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 or later * as published by the Free Software Foundation. */ #include "config.h" #include "tfa98xx_tfafieldnames.h" #include "tfa_internal.h" #include "tfa.h" #include #include "tfa_service.h" #include "tfa_container.h" #include "tfa_dsp_fw.h" /* TODO: remove genregs usage? */ #include "tfa98xx_genregs_N1C.h" #if defined(TFADSP_DSP_MSG_APR_PACKET_STRATEGY) #include #endif /* 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 int tfa98xx_log_start_cnt; int tfa98xx_log_tfa_family; #ifndef MIN #define MIN(A, B) ((A < B) ? A : B) #endif #if defined(TFADSP_DSP_MSG_APR_PACKET_STRATEGY) /* in case of CONFIG_MSM_QDSP6_APRV2_GLINK/APRV3_GLINK, */ /* with smaller APR_MAX_BUF (512) */ #define APR_RESIDUAL_SIZE 60 #define MAX_APR_MSG_SIZE (APR_MAX_BUF-APR_RESIDUAL_SIZE) /* 452 */ /* #define STANDARD_PACKET_SIZE (MAX_APR_MSG_SIZE-4) // 448 */ /* (packet_id:2, packet_size:2) */ #endif /* retry values */ #define CFSTABLE_TRIES 10 #define PWDNWAIT_TRIES 50 #define AMPOFFWAIT_TRIES 50 #define MTPBWAIT_TRIES 50 #define MTPEX_WAIT_NTRIES 25 #define REDUCED_REGISTER_SETTING #define WRITE_CALIBRATION_DATA_TO_MTP #define CHECK_CALIBRATION_DATA_RANGE #if defined(WRITE_CALIBRATION_DATA_TO_MTP) #define MAX_WAIT_COUNT_UNTIL_CALIBRATION_DONE 20 static enum tfa98xx_error tfa_tfadsp_wait_calibrate_done(tfa98xx_handle_t handle); #endif static int tfa98xx_runtime_verbose; static int tfa98xx_trace_level; static int tfa98xx_dsp_verbose; /* 4 possible I2C addresses */ TFA_INTERNAL struct tfa98xx_handle_private handles_local[MAX_HANDLES]; /* calibration done executed */ #define TFA_MTPEX_POS TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS /**/ /* * static functions */ #if defined(TFADSP_32BITS) /* Sign extend to 32-bit from 24-bit */ static void tfa_msg24to32( int32_t *out32buf, const uint8_t *in24buf, int length) { int i = 0; int cmd_index = 0; int32_t tmp; while (length > 0) { tmp = ((uint8_t)in24buf[i] << 16) + ((uint8_t)in24buf[i+1] << 8) + (uint8_t)in24buf[i+2]; /* Sign extend to 32-bit from 24-bit */ out32buf[cmd_index] = ((int32_t)tmp << 8) >> 8; cmd_index++; i = i+3; length = length-3; } } /* 32bits data --> 24 bits: discard the MSB byte */ static void tfa_msg32to24( uint8_t *out24buf, const uint8_t *in32buf, int length) { int i, j; int len_out; len_out = (length / 4) * 3; for (i = 0, j = 0; i < len_out; i += 3, j += 4) { /* reverse bytes order */ out24buf[i+2] = in32buf[j]; out24buf[i+1] = in32buf[j+1]; out24buf[i] = in32buf[j+2]; } } #endif /* (TFADSP_32BITS) */ 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]; } /* return sign extended tap pattern */ int tfa_get_tap_pattern(tfa98xx_handle_t handle) { int value = tfa_get_bf(handle, TFA9912_BF_CFTAPPAT); int bitshift; uint8_t field_len = 1 + (TFA9912_BF_CFTAPPAT & 0x0f); /* length of bitfield */ bitshift = 8 * sizeof(int) - field_len; /* signextend */ value = (value << bitshift) >> bitshift; return value; } /* * interrupt bit function to clear */ int tfa_irq_clear(tfa98xx_handle_t handle, enum tfa9912_irq bit) { unsigned char reg; /* make bitfield enum */ if (bit == tfa9912_irq_all) { /* operate on all bits */ for (reg = TFA98XX_INTERRUPT_IN_REG1; reg < TFA98XX_INTERRUPT_IN_REG1 + 3; reg++) reg_write(handle, reg, 0xffff); /* all bits */ } else { if (bit < tfa9912_irq_max) { reg = (unsigned char) (TFA98XX_INTERRUPT_IN_REG1 + (bit >> 4)); reg_write(handle, 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(tfa98xx_handle_t handle, enum tfa9912_irq bit) { uint16_t value; int reg, mask; if (bit < tfa9912_irq_max) { /* only this bit */ reg = TFA98XX_INTERRUPT_OUT_REG1 + (bit >> 4); mask = 1 << (bit & 0x0f); reg_read(handle, (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(tfa98xx_handle_t handle, enum tfa9912_irq bit, int state) { uint16_t value, new_value; int reg = 0, mask; /* */ if (bit == tfa9912_irq_all) { /* operate on all bits */ for (reg = TFA98XX_INTERRUPT_ENABLE_REG1; reg <= TFA98XX_INTERRUPT_ENABLE_REG1+tfa9912_irq_max / 16; reg++) { reg_write(handle, (unsigned char)reg, state ? 0xffff : 0); /* all bits */ handles_local[handle].interrupt_enable [reg - TFA98XX_INTERRUPT_ENABLE_REG1] = state ? 0xffff : 0; /* all bits */ } } else { if (bit < tfa9912_irq_max) { /* only this bit */ reg = TFA98XX_INTERRUPT_ENABLE_REG1 + (bit >> 4); mask = 1 << (bit & 0x0f); reg_read(handle, (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(handle, (unsigned char)reg, new_value); /* only this bit */ handles_local[handle].interrupt_enable [reg-TFA98XX_INTERRUPT_ENABLE_REG1] = new_value; } } else { return TFA_ERROR; } } return 0; } /* * mask interrupts by disabling them */ int tfa_irq_mask(tfa98xx_handle_t handle) { int reg; /* operate on all bits */ for (reg = TFA98XX_INTERRUPT_ENABLE_REG1; reg <= TFA98XX_INTERRUPT_ENABLE_REG1 + tfa9912_irq_max / 16; reg++) reg_write(handle, (unsigned char)reg, 0); return 0; } /* * unmask interrupts by enabling them again */ int tfa_irq_unmask(tfa98xx_handle_t handle) { int reg; /* operate on all bits */ for (reg = TFA98XX_INTERRUPT_ENABLE_REG1; reg <= TFA98XX_INTERRUPT_ENABLE_REG1 + tfa9912_irq_max / 16; reg++) reg_write(handle, (unsigned char)reg, handles_local[handle].interrupt_enable [reg-TFA98XX_INTERRUPT_ENABLE_REG1]); return 0; } /* * interrupt bit function that sets the polarity */ int tfa_irq_set_pol(tfa98xx_handle_t handle, enum tfa9912_irq bit, int state) { uint16_t value, new_value; int reg = 0, mask; if (bit == tfa9912_irq_all) { /* operate on all bits */ for (reg = TFA98XX_STATUS_POLARITY_REG1; reg <= TFA98XX_STATUS_POLARITY_REG1 + tfa9912_irq_max / 16; reg++) { reg_write(handle, (unsigned char)reg, state ? 0xffff : 0); /* all bits */ } } else if (bit < tfa9912_irq_max) { /* only this bit */ reg = TFA98XX_STATUS_POLARITY_REG1 + (bit >> 4); mask = 1 << (bit & 0x0f); reg_read(handle, (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(handle, (unsigned char)reg, new_value); /* only this bit */ } } else return TFA_ERROR; return 0; } /* * update dirty cached regs to i2c registers */ void tfa_irq_update(tfa98xx_handle_t handle) { static int maxregs; static uint16_t regs_cache_irq_ena[(tfa9912_irq_max / 16) + 1]; int i, reg; if (!maxregs) { maxregs = tfa9912_irq_max/16; for (i = 0; i < maxregs; i++) { reg = TFA98XX_INTERRUPT_ENABLE_REG1 + i; reg_read(handle, (unsigned char)reg, ®s_cache_irq_ena[i]); } } /* write only regs that changed */ for (i = 0; i < maxregs; i++) { if (regs_cache_irq_ena[i] != handles_local[handle].interrupt_enable[i]) { reg = TFA98XX_INTERRUPT_ENABLE_REG1 + i; reg_write(handle, (unsigned char)reg, regs_cache_irq_ena[i]); } } } /* * 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; handles_local[dev_idx].default_boost_trip_level = -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 = SUPPORT_YES; handles_local[dev_idx].support_drc = SUPPORT_NOT_SET; handles_local[dev_idx].support_saam = SUPPORT_NOT_SET; /* handles_local[dev_idx].ext_dsp = 0; */ handles_local[dev_idx].is_cold = 1; handles_local[dev_idx].temp = 0xffff; handles_local[dev_idx].saam_use_case = 0; /* not in use */ handles_local[dev_idx].stream_state = 0; /* not in use */ /* TODO use the getfeatures() for retrieving the features [artf103523] * handles_local[dev_idx].support_drc = SUPPORT_NOT_SET; */ pr_info("%s: device type (0x%02x)\n", __func__, handles_local[dev_idx].rev); switch (handles_local[dev_idx].rev & 0xff) { case 0x72: /* tfa9872 */ handles_local[dev_idx].support_drc = SUPPORT_YES; handles_local[dev_idx].tfa_family = 2; handles_local[dev_idx].spkr_count = 1; handles_local[dev_idx].daimap = TFA98XX_DAI_TDM; tfa9872_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ break; 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].support_drc = SUPPORT_NO; 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 0x96: /* tfa9896 */ handles_local[dev_idx].support_drc = SUPPORT_NO; handles_local[dev_idx].spkr_count = 1; handles_local[dev_idx].daimap = TFA98XX_DAI_TDM; tfa9896_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].support_drc = SUPPORT_NO; handles_local[dev_idx].support_framework = SUPPORT_NO; tfa9890_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ break; case 0x12: /* tfa9895 */ handles_local[dev_idx].spkr_count = 1; handles_local[dev_idx].daimap = TFA98XX_DAI_I2S; tfa9895_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ break; case 0x13: /* tfa9912 */ handles_local[dev_idx].tfa_family = 2; handles_local[dev_idx].spkr_count = 1; handles_local[dev_idx].daimap = TFA98XX_DAI_TDM; tfa9912_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; } } #if defined(TFADSP_DSP_BUFFER_POOL) enum tfa98xx_error tfa_buffer_pool(int index, int size, int control) { int dev, devcount = tfa98xx_cnt_max_device(); switch (control) { case POOL_ALLOC: /* allocate */ for (dev = 0; dev < devcount; dev++) { handles_local[dev].buf_pool[index].pool = kmalloc(size, GFP_KERNEL); if (handles_local[dev] .buf_pool[index].pool == NULL) { handles_local[dev] .buf_pool[index].size = 0; handles_local[dev] .buf_pool[index].in_use = 1; pr_err("%s: dev %d - buffer_pool[%d] - kmalloc error %d bytes\n", __func__, dev, index, size); return TFA98XX_ERROR_FAIL; } pr_info("%s: dev %d - buffer_pool[%d] - kmalloc allocated %d bytes\n", __func__, dev, index, size); handles_local[dev].buf_pool[index].size = size; handles_local[dev].buf_pool[index].in_use = 0; } break; case POOL_FREE: /* deallocate */ for (dev = 0; dev < devcount; dev++) { if (handles_local[dev] .buf_pool[index].pool != NULL) kfree(handles_local[dev] .buf_pool[index].pool); pr_info("%s: dev %d - buffer_pool[%d] - kfree\n", __func__, dev, index); handles_local[dev].buf_pool[index].pool = NULL; handles_local[dev].buf_pool[index].size = 0; handles_local[dev].buf_pool[index].in_use = 0; } break; default: pr_info("%s: wrong control\n", __func__); break; } return TFA98XX_ERROR_OK; } int tfa98xx_buffer_pool_access(tfa98xx_handle_t handle, int r_index, size_t g_size, int control) { int index; switch (control) { case POOL_GET: /* get */ for (index = 0; index < POOL_MAX_INDEX; index++) { if (handles_local[handle] .buf_pool[index].in_use) continue; if (handles_local[handle] .buf_pool[index].size < g_size) continue; handles_local[handle] .buf_pool[index].in_use = 1; pr_debug("%s: dev %d - get buffer_pool[%d]\n", __func__, handle, index); return index; } pr_info("%s: dev %d - failed to get buffer_pool\n", __func__, handle); break; case POOL_RETURN: /* return */ if (handles_local[handle] .buf_pool[r_index].in_use == 0) { pr_info("%s: dev %d - buffer_pool[%d] is not in use\n", __func__, handle, r_index); break; } pr_debug("%s: dev %d - return buffer_pool[%d]\n", __func__, handle, r_index); memset(handles_local[handle].buf_pool[r_index].pool, 0, handles_local[handle].buf_pool[r_index].size); handles_local[handle].buf_pool[r_index].in_use = 0; return 0; default: pr_info("%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: 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 first_letter; /* 4=Left, 2=Right, 1=none, 0=default */ if (configuration == NULL) handles_local[dev_idx].spkr_select = 0; else { first_letter = (char)tolower((unsigned char)configuration[0]); switch (first_letter) { case 'b': /* SC / both -> apply primary 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 to 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 = tfa_cont_get_slave (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 *p_handle) { enum tfa98xx_error error = TFA98XX_ERROR_OK; int rev; int idx; _ASSERT(p_handle != NULL); *p_handle = -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_IN_USE; 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 implement foreign i2c addressing like uda1355 */ handles_local[idx].buffer_size = NXP_I2C_BufferSize(); handles_local[idx].slave_address = slave_address; /* 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 (error != TFA98XX_ERROR_OK) { handles_local[idx].in_use = 0; pr_debug("Error: Unable to read revid from slave:0x%02x\n", slave_address/2); return error; } handles_local[idx].rev = (unsigned short) rev; *p_handle = 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: pr_info("Unknown slave adress!\n"); /* 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_IN_USE; 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; } return TFA98XX_ERROR_NOT_OPEN; } /* * 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 ((filter_index >= 10) && (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: *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: default: /* unsupported case, possibly intermediate version */ return TFA98XX_DMEM_ERR; _ASSERT(0); } } return dmem; } /************************ query functions *******************************/ /* no device involved */ /** * return revision */ 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__); } /** * Return the maximum nr of devices (SC39786) * TODO get from cnt: now only called from contOpen */ int tfa98xx_max_devices(void) { 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_bitmap tfa98xx_get_device_dai(tfa98xx_handle_t handle) { /* local function. Caller must make sure handle is valid */ return handles_local[handle].daimap; } /** * 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_NOT_OPEN; if ((handles_local[handle].rev & 0xff) == 0x72) { if (tfa98xx_cnt_max_device() > 1) *spkr_count = 2; else *spkr_count = 1; } 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_bitmap *daimap) { if (tfa98xx_handle_is_open(handle)) *daimap = handles_local[handle].daimap; else return TFA98XX_ERROR_NOT_OPEN; 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 == SUPPORT_NOT_SET) { error = tfa98xx_dsp_get_hw_feature_bits(handle, &features); if (error != TFA98XX_ERROR_OK) return error; handles_local[handle].support_saam = (features & 0x8000) ? SUPPORT_YES : SUPPORT_NO; /* SAAM is bit15 */ } *saam = handles_local[handle].support_saam == SUPPORT_YES ? TFA98XX_SAAM : TFA98XX_SAAM_NONE; return TFA98XX_ERROR_OK; } /* * tfa98xx_set_saam_use_case * sets use case of saam: 0: not in use, 1: RaM / SaM only, 2: bidirectional */ enum tfa98xx_error tfa98xx_set_saam_use_case(int samstream) { int dev, devcount = tfa98xx_cnt_max_device(); pr_info("%s: set saam_use_case=%d\n", __func__, samstream); if (devcount < 1) { pr_err("No or wrong container file loaded\n"); return TFA98XX_ERROR_BAD_PARAMETER; } for (dev = 0; dev < devcount; dev++) handles_local[dev].saam_use_case = samstream; 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(int stream_state) { int dev, devcount = tfa98xx_cnt_max_device(); pr_debug("%s: set stream_state=0x%04x\n", __func__, stream_state); if (devcount < 1) { pr_err("No or wrong container file loaded\n"); return TFA98XX_ERROR_BAD_PARAMETER; } for (dev = 0; dev < devcount; dev++) handles_local[dev].stream_state = stream_state; 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 */ /* 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_RPC_PARAM_ID */ 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->mtpex.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); controls->calib.wr_value = true; 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_NOT_OPEN; 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_NOT_OPEN; 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_NOT_OPEN; /* 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(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 tfa_run_start_dsp() */ /* 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_NOT_OPEN; 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 memory_type, 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)memory_type; 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 = dsp_msg(handle, sizeof(msg), (char *)msg); if (error != TFA98XX_ERROR_OK) return error; /* read the data from the device (length * 3 = words) */ error = dsp_msg_read(handle, length * 3, bytes); return error; } enum tfa98xx_error tfa98xx_dsp_set_memory(tfa98xx_handle_t handle, int memory_type, 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)memory_type; 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 = 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 result; /* not possible if PLL in powerdown */ if (TFA_GET_BF(handle, PWDN)) { pr_debug("PLL in powerdown\n"); return TFA98XX_ERROR_NO_CLOCK; } 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 */ reg_write(handle, (tfa98xx_dev_family(handle) == 1) ? 0x40 : 0x0F, 0x5A6B); /* lock/unlock key2 MTPK */ TFA_WRITE_REG(handle, MTPKEY2, lock ? 0 : 0x5A); /* unhide lock registers */ reg_write(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; 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 */ { if (tfa98xx_runtime_verbose) pr_info("No change in MTP. Value not written!\n"); return TFA98XX_ERROR_OK; } 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_STATE_TIMED_OUT; } /* * clear mtpex * set ACS * start tfa */ enum tfa98xx_error tfa_calibrate(tfa98xx_handle_t handle) { enum tfa98xx_error error; /* clear mtpex */ error = tfa98xx_set_mtp(handle, 0, TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK); if (error) return error; /* set RST=1 to trigger recalibration */ TFA_SET_BF(handle, RST, 1); /* set ACS/coldboot state */ handles_local[handle].is_cold = 1; error = tfa_run_coldboot(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_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(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, INTSMUTE, 0); 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, INTSMUTE, 1); 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 with handle %d is not opened\n", (int)handle); return TFA98XX_ERROR_NOT_OPEN; } 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; 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) { /* read register to check the correct ROM version */ if (error == TFA98XX_ERROR_OK) { error = mem_read(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 patch_length, const unsigned char *patch_bytes) { enum tfa98xx_error error; if (!tfa98xx_handle_is_open(handle)) return TFA98XX_ERROR_NOT_OPEN; if (patch_length < PATCH_HEADER_LENGTH) return TFA98XX_ERROR_BAD_PARAMETER; error = tfa98xx_check_ic_rom_version(handle, patch_bytes); if (error != TFA98XX_ERROR_OK) return error; error = tfa98xx_process_patch_file (handle, patch_length - PATCH_HEADER_LENGTH, patch_bytes + 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 *p_rpc_status) { 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) { *p_rpc_status = -1; return TFA98XX_ERROR_OK; } else if (error != TFA98XX_ERROR_OK) return error; error = tfa98xx_check_rpc_status(handle, 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 config bits (SC|DS|DP|DC) combination"; break; case TFA98XX_I2C_REQ_INVALID_SEQ: p_id_str = "Invalid cmd sequence: DSP expected 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 overflowed: host sent too many parameters"; break; default: p_id_str = "Unspecified error"; } 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 error; } void tfa_dsp_msg_register(int dev_idx, dsp_msg_t func) { handles_local[dev_idx].dev_ops.dsp_msg = func; } void tfa_dsp_msg_read_register(int dev_idx, dsp_msg_read_t func) { handles_local[dev_idx].dev_ops.dsp_msg_read = func; } void tfa_dsp_reg_register(int dev_idx, reg_read_t read_func, reg_write_t write_func) { handles_local[dev_idx].dev_ops.reg_read = read_func; handles_local[dev_idx].dev_ops.reg_write = write_func; } void tfa_dsp_mem_register(int dev_idx, mem_read_t read_func, mem_write_t write_func) { handles_local[dev_idx].dev_ops.mem_read = read_func; handles_local[dev_idx].dev_ops.mem_write = write_func; } enum tfa98xx_error dsp_msg(tfa98xx_handle_t handle, int length, const char *buf) { enum tfa98xx_error error = 0; static int lastmessage; uint8_t *blob = NULL; #if defined(TFADSP_DSP_BUFFER_POOL) int blob_p_index = -1; #endif int i; const char *tfadsp_buf; int tfadsp_buf_size; #if defined(TFADSP_DEBUG) int n; #endif #if defined(TFADSP_32BITS) int32_t *buf32 = NULL; #if defined(TFADSP_DSP_BUFFER_POOL) int buf32_p_index = -1; #endif int buf32_len; #endif if (handles_local[handle].saam_use_case == 1 || (handles_local[handle].stream_state & BIT_PSTREAM) == 0) { pr_debug("%s: skip in SaaM (RaM/SaM only)\n", __func__); return error; } pr_debug("%s: length = %d\n", __func__, length); tfadsp_buf = buf; tfadsp_buf_size = length; #if defined(TFADSP_32BITS) /*need bits conversion: 24 bits --> 32 bits */ buf32_len = (length/3)*4; #if defined(TFADSP_DSP_BUFFER_POOL) buf32_p_index = tfa98xx_buffer_pool_access (handle, -1, buf32_len, POOL_GET); if (buf32_p_index != -1) { pr_debug("%s: allocated from buffer_pool[%d] for %d bytes\n", __func__, buf32_p_index, buf32_len); buf32 = (int32_t *)(handles_local[handle] .buf_pool[buf32_p_index].pool); } else { buf32 = kmalloc(buf32_len, GFP_KERNEL); if (buf32 == NULL) goto dsp_msg_error_exit; } #else buf32 = kmalloc(buf32_len, GFP_KERNEL); if (buf32 == NULL) goto dsp_msg_error_exit; #endif /* TFADSP_DSP_BUFFER_POOL */ /*convert 24 bits -> 32 bits */ tfa_msg24to32(buf32, buf, length); tfadsp_buf = (char *) buf32; tfadsp_buf_size = buf32_len; #endif /* (TFADSP_32BITS) */ if (handles_local[handle].dev_ops.dsp_msg) { /* Only create multi-msg when the dsp is cold */ if (handles_local[handle].ext_dsp == 1) { /* Creating the multi-msg */ lastmessage = tfa_tib_dsp_msgblob (handle, tfadsp_buf_size, tfadsp_buf); if (lastmessage == TFA_ERROR) goto dsp_msg_error_exit; /* When the lastmessage is done * we can send the multi-msg to the target */ if (lastmessage == 1) { /* Get the full multi-msg data */ #if defined(TFADSP_DSP_BUFFER_POOL) blob_p_index = tfa98xx_buffer_pool_access (handle, -1, 64*1024, POOL_GET); if (blob_p_index != -1) { pr_debug("%s: allocated from buffer_pool[%d] - blob\n", __func__, blob_p_index); blob = (uint8_t *) (handles_local[handle] .buf_pool [blob_p_index].pool); } else { blob = kmalloc(64*1024, GFP_KERNEL); /* max length is 64k */ if (blob == NULL) goto dsp_msg_error_exit; } tfadsp_buf_size = tfa_tib_dsp_msgblob (handle, -1, (const char *)blob); #else blob = kmalloc(64*1024, GFP_KERNEL); /* max length is 64k */ if (blob == NULL) goto dsp_msg_error_exit; tfadsp_buf_size = tfa_tib_dsp_msgblob (-1, tfadsp_buf_size, (const char *)blob); #endif /* TFADSP_DSP_BUFFER_POOL */ if (tfa98xx_runtime_verbose) pr_debug("Last message for the multi-message received. Multi-message length=%d\n", length); #if defined(TFADSP_DSP_MSG_APR_PACKET_STRATEGY) error = dsp_msg_packet(handle, blob, tfadsp_buf_size); #else /* Send to the target selected */ if ((handles_local[handle] .stream_state & BIT_PSTREAM) == 1) { error = (*handles_local[handle] .dev_ops.dsp_msg) (handle, tfadsp_buf_size, (const char *) /*tfadsp_buf*/blob); } else { pr_info("%s: skip dsp_msg when pstream is not active\n", __func__); } #endif /* TFADSP_DSP_MSG_APR_PACKET_STRATEGY */ #if defined(TFADSP_DSP_BUFFER_POOL) if (blob_p_index != -1) { tfa98xx_buffer_pool_access (handle, blob_p_index, 0, POOL_RETURN); blob_p_index = -1; } else { kfree(blob); blob = NULL; } #else kfree(blob); /* Free the kmalloc blob */ blob = NULL; #endif /* TFADSP_DSP_BUFFER_POOL */ lastmessage = 0; /* reset to be able to re-start */ } } else { if ((handles_local[handle] .stream_state & BIT_PSTREAM) == 1) { error = (*handles_local[handle] .dev_ops.dsp_msg) (handle, tfadsp_buf_size, tfadsp_buf); } else { pr_info("%s: skip dsp_msg when pstream is not active\n", __func__); } } } else { if ((handles_local[handle].stream_state & BIT_PSTREAM) == 1) { error = tfa_dsp_msg (handle, tfadsp_buf_size, tfadsp_buf); /* Use default */ } else { pr_info("%s: skip dsp_msg when pstream is not active\n", __func__); } } #if defined(TFADSP_DEBUG) #if defined(TFADSP_32BITS) /*show buffer: 32 bits*/ pr_debug("%s: buf32_len = %d\n", __func__, tfadsp_buf_size); n = (tfadsp_buf_size/4 >= 3) ? 3 : tfadsp_buf_size/4; for (i = 0; i < n; i++) pr_debug("%s: buf32[%d] = 0x%08x\n", __func__, i, tfadsp_buf[i]); #else /*show buffer: 24 bits*/ pr_debug("%s: buf24_len = %d\n", __func__, tfadsp_buf_size); n = (tfadsp_buf_size >= 9) ? 9 : tfadsp_buf_size; for (i = 0; i < n; i++) pr_debug("%s: buf24[%d] = 0x%02x\n", __func__, i, tfadsp_buf[i]); #endif #endif 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); pr_info("%s: (rpc base %d) error = %d\n", __func__, TFA98XX_ERROR_BUFFER_RPC_BASE, error); } #if defined(TFADSP_32BITS) #if defined(TFADSP_DSP_BUFFER_POOL) if (buf32_p_index != -1) { tfa98xx_buffer_pool_access (handle, buf32_p_index, 0, POOL_RETURN); buf32_p_index = -1; } else { if (buf32 != NULL) kfree(buf32); } #else if (buf32 != NULL) kfree(buf32); #endif /* TFADSP_DSP_BUFFER_POOL */ #endif /* (TFADSP_32BITS) */ if (tfa98xx_dsp_verbose) { pr_debug("DSP w [%d]: ", length); for (i = 0; i < length; i++) pr_debug("0x%02x ", (uint8_t)buf[i]); pr_debug("\n"); } return error; dsp_msg_error_exit: pr_err("%s: exiting due to error\n", __func__); if (lastmessage == TFA_ERROR) pr_err("%s: error in merging messages\n", __func__); else pr_err("%s: can not allocate memory\n", __func__); #if defined(TFADSP_32BITS) #if defined(TFADSP_DSP_BUFFER_POOL) if (buf32_p_index != -1) { tfa98xx_buffer_pool_access (handle, buf32_p_index, 0, POOL_RETURN); } else { kfree(buf32); } #else kfree(buf32); #endif /* TFADSP_DSP_BUFFER_POOL */ #endif /* (TFADSP_32BITS) */ #if defined(TFADSP_DSP_BUFFER_POOL) if (blob_p_index != -1) { tfa98xx_buffer_pool_access (handle, blob_p_index, 0, POOL_RETURN); } else { kfree(blob); } #else kfree(blob); /* Free the kmalloc blob */ #endif /* TFADSP_DSP_BUFFER_POOL */ return TFA98XX_ERROR_FAIL; } #if defined(TFADSP_DSP_MSG_APR_PACKET_STRATEGY) enum tfa98xx_error dsp_msg_packet(tfa98xx_handle_t handle, uint8_t *blob, int tfadsp_buf_size) { enum tfa98xx_error error = 0; uint8_t *apr_buff = NULL, *apr_buff_last = NULL; #if defined(TFADSP_DSP_BUFFER_POOL) int apr_buff_p_index = -1; #endif int remaining_blob_size, tfadsp_buf_offset; short packet_id, packet_size; tfadsp_buf_offset = 0; remaining_blob_size = tfadsp_buf_size; packet_size = MAX_APR_MSG_SIZE - 4; #if defined(TFADSP_DSP_BUFFER_POOL) apr_buff_p_index = tfa98xx_buffer_pool_access(handle, -1, MAX_APR_MSG_SIZE, POOL_GET); if (apr_buff_p_index != -1) { pr_debug("%s: allocated from buffer_pool[%d] - apr_buff\n", __func__, apr_buff_p_index); apr_buff = (uint8_t *) (handles_local[handle].buf_pool[apr_buff_p_index].pool); } else { apr_buff = kmalloc(MAX_APR_MSG_SIZE, GFP_KERNEL); if (apr_buff == NULL) goto dsp_msg_packet_error_exit; } #else apr_buff = kmalloc(MAX_APR_MSG_SIZE, GFP_KERNEL); if (apr_buff == NULL) goto dsp_msg_packet_error_exit; #endif /* TFADSP_DSP_BUFFER_POOL */ for (packet_id = 0; packet_id < (int)(tfadsp_buf_size / (MAX_APR_MSG_SIZE - 4)); packet_id++) { pr_debug("packet[%d]: size (%d)\n", packet_id, packet_size); apr_buff[0] = (uint8_t)(((packet_id + 1) >> 8) & 0xFF); apr_buff[1] = (uint8_t)((packet_id + 1) & 0xFF); apr_buff[2] = (uint8_t)((packet_size >> 8) & 0xFF); apr_buff[3] = (uint8_t) (packet_size & 0xFF); memcpy(apr_buff + 4, blob + tfadsp_buf_offset, packet_size); if ((handles_local[handle].stream_state & BIT_PSTREAM) == 1) { error = (*handles_local[handle].dev_ops.dsp_msg) (handle, packet_size + 4, (const char *)apr_buff); } else { pr_info("%s: skip dsp_msg when pstream is not active\n", __func__); } tfadsp_buf_offset += packet_size; remaining_blob_size -= packet_size; } #if defined(TFADSP_DSP_BUFFER_POOL) if (apr_buff_p_index != -1) { tfa98xx_buffer_pool_access(handle, apr_buff_p_index, 0, POOL_RETURN); apr_buff_p_index = -1; } else { kfree(apr_buff); apr_buff = NULL; } #else kfree(apr_buff); apr_buff = NULL; #endif /* TFADSP_DSP_BUFFER_POOL */ if (remaining_blob_size > 0) { packet_size = remaining_blob_size; #if defined(TFADSP_DSP_BUFFER_POOL) apr_buff_p_index = tfa98xx_buffer_pool_access (handle, -1, packet_size + 4, POOL_GET); if (apr_buff_p_index != -1) { pr_debug("%s: allocated from buffer_pool [%d] - apr_buff_last\n", __func__, apr_buff_p_index); apr_buff_last = (uint8_t *)(handles_local[handle] .buf_pool[apr_buff_p_index].pool); } else { apr_buff_last = kmalloc(packet_size + 4, GFP_KERNEL); if (apr_buff_last == NULL) goto dsp_msg_packet_error_exit; } #else apr_buff_last = kmalloc(packet_size + 4, GFP_KERNEL); if (apr_buff_last == NULL) goto dsp_msg_packet_error_exit; #endif /* TFADSP_DSP_BUFFER_POOL */ pr_debug("packet[%d]: size (%d) - last\n", packet_id, remaining_blob_size); apr_buff_last[0] = 0xFF; apr_buff_last[1] = 0xFF; apr_buff_last[2] = (uint8_t)((packet_size >> 8) & 0xFF); apr_buff_last[3] = (uint8_t)(packet_size & 0xFF); memcpy(apr_buff_last + 4, blob + tfadsp_buf_offset, remaining_blob_size); if ((handles_local[handle].stream_state & BIT_PSTREAM) == 1) { error = (*handles_local[handle].dev_ops.dsp_msg) (handle, packet_size + 4, (const char *)apr_buff_last); } else { pr_info("%s: skip dsp_msg when pstream is not active\n", __func__); } tfadsp_buf_offset += packet_size; remaining_blob_size = 0; #if defined(TFADSP_DSP_BUFFER_POOL) if (apr_buff_p_index != -1) { tfa98xx_buffer_pool_access(handle, apr_buff_p_index, 0, POOL_RETURN); apr_buff_p_index = -1; } else { kfree(apr_buff_last); apr_buff_last = NULL; } #else kfree(apr_buff_last); apr_buff_last = NULL; #endif /* TFADSP_DSP_BUFFER_POOL */ } return error; dsp_msg_packet_error_exit: pr_err("%s: can not allocate memory\n", __func__); #if defined(TFADSP_DSP_BUFFER_POOL) if (apr_buff_p_index != -1) { tfa98xx_buffer_pool_access (handle, apr_buff_p_index, 0, POOL_RETURN); } else { kfree(apr_buff); kfree(apr_buff_last); } #else kfree(apr_buff); kfree(apr_buff_last); #endif /* TFADSP_DSP_BUFFER_POOL */ return TFA98XX_ERROR_FAIL; } #endif /* TFADSP_DSP_MSG_APR_PACKET_STRATEGY */ enum tfa98xx_error dsp_msg_read(tfa98xx_handle_t handle, int length, unsigned char *bytes) { enum tfa98xx_error error = TFA98XX_ERROR_OK; unsigned char *tfadsp_buf; int tfadsp_buf_size; #if defined(TFADSP_32BITS) int32_t *buf32; #if defined(TFADSP_DSP_BUFFER_POOL) int buf32_p_index = -1; #endif int buf32_len; #endif int i; #if defined(TFADSP_DEBUG) int n; #endif if (handles_local[handle].saam_use_case == 1) { pr_info("%s: skip dsp_msg_read in SaaM (RaM / SaM only)\n", __func__); return error; } pr_debug("%s: length = %d\n", __func__, length); tfadsp_buf = bytes; tfadsp_buf_size = length; #if defined(TFADSP_32BITS) buf32_len = (length/3)*4; #if defined(TFADSP_DSP_BUFFER_POOL) buf32_p_index = tfa98xx_buffer_pool_access (handle, -1, buf32_len, POOL_GET); if (buf32_p_index != -1) { pr_debug("%s: allocated from buffer_pool[%d] for %d bytes\n", __func__, buf32_p_index, buf32_len); buf32 = (int32_t *)(handles_local[handle] .buf_pool[buf32_p_index].pool); } else { buf32 = kmalloc(buf32_len, GFP_KERNEL); if (buf32 == NULL) goto dsp_msg_read_error_exit; } #else buf32 = kmalloc(buf32_len, GFP_KERNEL); if (buf32 == NULL) goto dsp_msg_read_error_exit; #endif /* TFADSP_DSP_BUFFER_POOL */ tfadsp_buf = (unsigned char *) buf32; tfadsp_buf_size = buf32_len; #endif /* TFADSP_32BITS */ if ((handles_local[handle].stream_state & BIT_PSTREAM) == 1) { if (handles_local[handle].dev_ops.dsp_msg_read) { /*dsp read msg*/ error = (*handles_local[handle].dev_ops.dsp_msg_read)( handle, tfadsp_buf_size, tfadsp_buf); 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); pr_info("%s: (rpc base %d) error = %d\n", __func__, TFA98XX_ERROR_BUFFER_RPC_BASE, error); } } else { error = tfa_dsp_msg_read (handle, tfadsp_buf_size, tfadsp_buf); /* Use default */ } } else { pr_debug("%s: skip dsp_msg_read when pstream is not active\n", __func__); } #if defined(TFADSP_32BITS) for (i = 0; i < 6; i++) pr_debug("%s: buf32bits[%d] = 0x%08x\n", __func__, i, tfadsp_buf[i]); /*convert 32 bits -> 24 bits */ tfa_msg32to24(bytes, (uint8_t *)tfadsp_buf, tfadsp_buf_size); for (i = 0; i < 6; i++) pr_debug("%s: buf24bits[%d] = 0x%08x\n", __func__, i, bytes[i]); #if defined(TFADSP_DEBUG) pr_debug("%s: buf32_len = %d\n", __func__, tfadsp_buf_size); n = (tfadsp_buf_size/4 >= 3) ? 3 : tfadsp_buf_size/4; for (i = 0; i < n; i++) pr_debug("%s: buf32[%d] = 0x%08x\n", __func__, i, tfadsp_buf[i]); pr_debug("%s: buf24_len = %d\n", __func__, length); n = (length >= 9) ? 9 : length; for (i = 0; i < n; i++) pr_debug("%s: buf24[%d] = 0x%02x\n", __func__, i, bytes[i]); #endif #if defined(TFADSP_DSP_BUFFER_POOL) if (buf32_p_index != -1) { tfa98xx_buffer_pool_access (handle, buf32_p_index, 0, POOL_RETURN); } else { kfree(buf32); } #else if (buf32 != NULL) kfree(buf32); #endif /* TFADSP_DSP_BUFFER_POOL */ #endif /* (TFADSP_32BITS) */ if (tfa98xx_dsp_verbose) { pr_debug("DSP R [%d]: ", length); for (i = 0; i < length; i++) pr_debug("0x%02x ", (uint8_t)bytes[i]); pr_debug("\n"); } return error; dsp_msg_read_error_exit: pr_err("%s: can not allocate memory\n", __func__); return TFA98XX_ERROR_FAIL; } enum tfa98xx_error reg_read(tfa98xx_handle_t handle, unsigned char subaddress, unsigned short *value) { enum tfa98xx_error error; if (handles_local[handle].dev_ops.reg_read) { error = (*handles_local[handle] .dev_ops.reg_read)(handle, subaddress, value); if (error != TFA98XX_ERROR_OK) error = (enum tfa98xx_error) (error + TFA98XX_ERROR_BUFFER_RPC_BASE); /* Get actual error code from softDSP */ } else { error = tfa98xx_read_register16(handle, subaddress, value); /* Use default */ } return error; } enum tfa98xx_error reg_write(tfa98xx_handle_t handle, unsigned char subaddress, unsigned short value) { enum tfa98xx_error error; if (handles_local[handle].dev_ops.reg_write) { error = (*handles_local[handle] .dev_ops.reg_write)(handle, subaddress, value); if (error != TFA98XX_ERROR_OK) error = (enum tfa98xx_error) (error + TFA98XX_ERROR_BUFFER_RPC_BASE); /* Get actual error code from softDSP */ } else { error = tfa98xx_write_register16(handle, subaddress, value); /* Use default */ } return error; } enum tfa98xx_error mem_read(tfa98xx_handle_t handle, unsigned int start_offset, int num_words, int *p_values) { enum tfa98xx_error error; if (handles_local[handle].dev_ops.mem_read) { error = (*handles_local[handle].dev_ops.mem_read) (handle, start_offset, num_words, p_values); if (error != TFA98XX_ERROR_OK) error = (enum tfa98xx_error) (error + TFA98XX_ERROR_BUFFER_RPC_BASE); /* Get actual error code from softDSP */ } else { error = tfa98xx_dsp_read_mem (handle, start_offset, num_words, p_values); /* Use default */ } return error; } enum tfa98xx_error mem_write(tfa98xx_handle_t handle, unsigned short address, int value, int memtype) { enum tfa98xx_error error; if (handles_local[handle].dev_ops.mem_write) { error = (*handles_local[handle].dev_ops.mem_write) (handle, address, value, memtype); if (error != TFA98XX_ERROR_OK) error = (enum tfa98xx_error) (error + TFA98XX_ERROR_BUFFER_RPC_BASE); /* Get actual error code from softDSP */ } else { error = tfa98xx_dsp_write_mem_word (handle, address, value, memtype); /* Use default */ } return error; } /* * write/read raw msg functions : * the buffer is provided in little endian format, each word * occupying 3 bytes, length is in bytes. * The functions will return immediately and do not not wait for DSP reponse. */ #define MAX_WORDS (300) enum tfa98xx_error tfa_dsp_msg(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) * pr_debug("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_BUFFER_RPC_BASE); 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) * pr_debug("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_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(tfa98xx_handle_t handle, int *p_rpc_status) { enum tfa98xx_error error = TFA98XX_ERROR_OK; /* the value to sent to the * CF_CONTROLS register: cf_req=00000000, * cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */ unsigned short cf_ctrl = 0x0002; /* memory address to be accessed (0: Status, 1: ID, 2: parameters) */ unsigned short cf_mad = 0x0000; if (!tfa98xx_handle_is_open(handle)) return TFA98XX_ERROR_NOT_OPEN; if (p_rpc_status == 0) 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 (handle, FAM_TFA98XX_CF_CONTROLS, sizeof(buffer), buffer); } if (error == TFA98XX_ERROR_OK) { /* read 1 word (24 bit) from XMEM */ error = tfa98xx_dsp_read_mem(handle, 0, 1, p_rpc_status); } return error; } /***************************** xmem only **********************************/ enum tfa98xx_error tfa98xx_dsp_read_mem(tfa98xx_handle_t handle, 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 = 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; /* Remove offset from adress */ start_offset = start_offset & 0xffff; num_bytes = num_words * bytes_per_word; p = p_values; TFA_SET_BF(handle, DMEM, (uint16_t)dmem); error = -TFA_WRITE_REG(handle, MADD, (unsigned short)start_offset); if (error != TFA98XX_ERROR_OK) goto tfa98xx_dsp_read_mem_exit; 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); return error; } enum tfa98xx_error tfa_cont_write_filterbank(int device, 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 (device, MODULE_BIQUADFILTERBANK, biquad_index+1, /* start @1 */ sizeof(filter[biquad_index].biquad.bytes), filter[biquad_index].biquad.bytes); } else { error = tfa98xx_dsp_biquad_disable (device, biquad_index+1); } if (error) return error; } return error; } enum tfa98xx_error tfa98xx_dsp_biquad_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 = 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 defined(TFADSP_DSP_BUFFER_POOL) int buffer_p_index = -1; #endif if (!tfa98xx_handle_is_open(handle)) return TFA98XX_ERROR_NOT_OPEN; #if defined(TFADSP_DSP_BUFFER_POOL) buffer_p_index = tfa98xx_buffer_pool_access (handle, -1, 3 + num_bytes, POOL_GET); if (buffer_p_index != -1) { pr_debug("%s: allocated from buffer_pool[%d] for %d bytes\n", __func__, buffer_p_index, 3 + num_bytes); buffer = (unsigned char *) (handles_local[handle].buf_pool[buffer_p_index].pool); } else { buffer = kmalloc(3 + num_bytes, GFP_KERNEL); if (buffer == NULL) goto dsp_cmd_id_write_error_exit; } #else buffer = kmalloc(3 + num_bytes, GFP_KERNEL); if (buffer == NULL) goto dsp_cmd_id_write_error_exit; #endif /* TFADSP_DSP_BUFFER_POOL */ buffer[0] = handles_local[handle].spkr_select; buffer[1] = module_id + 128; buffer[2] = param_id; memcpy(&buffer[3], data, num_bytes); error = dsp_msg(handle, 3 + num_bytes, (char *)buffer); #if defined(TFADSP_DSP_BUFFER_POOL) if (buffer_p_index != -1) { tfa98xx_buffer_pool_access (handle, buffer_p_index, 0, POOL_RETURN); } else { kfree(buffer); } #else kfree(buffer); #endif /* TFADSP_DSP_BUFFER_POOL */ return error; dsp_cmd_id_write_error_exit: pr_err("%s: can not allocate memory\n", __func__); return TFA98XX_ERROR_FAIL; } EXPORT_SYMBOL(tfa_dsp_cmd_id_write); /* 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_NOT_OPEN; buffer[0] = handles_local[handle].spkr_select; buffer[1] = module_id + 128; buffer[2] = param_id; if (((handles_local[handle].rev & 0xff) == 0x72) && (tfa98xx_cnt_max_device() == 1) && (param_id == SB_PARAM_GET_RE25C || param_id == SB_PARAM_GET_LSMODEL || param_id == SB_PARAM_GET_ALGO_PARAMS)) { /* Modifying the ID for GetRe25C */ buffer[0] = 4; } error = dsp_msg(handle, sizeof(unsigned char[3]), (char *)buffer); if (error != TFA98XX_ERROR_OK) return error; /* read the data from the dsp */ error = dsp_msg_read(handle, num_bytes, data); return error; } EXPORT_SYMBOL(tfa_dsp_cmd_id_write_read); /* 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_NOT_OPEN; 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 = dsp_msg(handle, sizeof(unsigned char[6]), (char *)buffer); if (error != TFA98XX_ERROR_OK) return error; /* read the data from the dsp */ error = dsp_msg_read(handle, num_bytes, data); return error; } /* wrapper for dsp_msg that adds opcode; 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_NOT_OPEN; 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 = dsp_msg(handle, sizeof(unsigned char[6]), (char *)buffer); if (error != TFA98XX_ERROR_OK) return error; /* read the data from the dsp */ error = 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 { 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 { 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_RPC_PARAM_ID */ 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 b_support_framework = 0; unsigned int state_size = 9; err = tfa98xx_dsp_support_framework(handle, &b_support_framework); if (err == TFA98XX_ERROR_OK) { if (b_support_framework) { err = tfa_dsp_cmd_id_write_read(handle, 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 (handle, MODULE_SPEAKERBOOST, SB_PARAM_GET_STATE, 3 * state_size, bytes); } } *statesize = state_size; return err; } enum tfa98xx_error tfa98xx_dsp_support_drc(tfa98xx_handle_t handle, int *pb_support_drc) { enum tfa98xx_error error = TFA98XX_ERROR_OK; *pb_support_drc = 0; if (!tfa98xx_handle_is_open(handle)) return TFA98XX_ERROR_NOT_OPEN; if (handles_local[handle].support_drc != SUPPORT_NOT_SET) { *pb_support_drc = (handles_local[handle].support_drc == SUPPORT_YES); } else { int feature_bits[2]; error = tfa98xx_dsp_get_sw_feature_bits(handle, 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) { handles_local[handle].support_drc = *pb_support_drc ? SUPPORT_YES : SUPPORT_NO; } } return error; } enum tfa98xx_error tfa98xx_dsp_support_framework(tfa98xx_handle_t handle, int *pb_support_framework) { int feature_bits[2] = {0, 0}; enum tfa98xx_error error = TFA98XX_ERROR_OK; _ASSERT(pb_support_framework != 0); if (!tfa98xx_handle_is_open(handle)) return TFA98XX_ERROR_NOT_OPEN; if (handles_local[handle].support_framework != SUPPORT_NOT_SET) { if (handles_local[handle].support_framework == SUPPORT_NO) *pb_support_framework = 0; else *pb_support_framework = 1; } else { error = tfa98xx_dsp_get_sw_feature_bits(handle, feature_bits); if (error == TFA98XX_ERROR_OK) { *pb_support_framework = 1; handles_local[handle].support_framework = SUPPORT_YES; } else { *pb_support_framework = 0; handles_local[handle].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(tfa98xx_handle_t handle, int length, const unsigned char *p_speaker_bytes) { enum tfa98xx_error error; int b_support_drc; 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 (error != TFA98XX_ERROR_OK) return error; error = tfa98xx_dsp_support_drc(handle, &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(handle, MODULE_SPEAKERBOOST, SB_PARAM_SET_AGCINS, sizeof(bytes), bytes); } 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; int b_support_drc; error = tfa_dsp_cmd_id_write(handle, MODULE_SPEAKERBOOST, SB_PARAM_SET_CONFIG, length, p_config_bytes); if (error != TFA98XX_ERROR_OK) return error; error = tfa98xx_dsp_support_drc(handle, &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(handle, MODULE_SPEAKERBOOST, SB_PARAM_SET_AGCINS, sizeof(bytes), bytes); } return error; } /* 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; } 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_NOT_OPEN; 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_NOT_OPEN; 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 = reg_read(dev_idx, 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(dev_idx, address, regvalue); if (err) { pr_err("Error setting bf :%d\n", -err); return -err; } } return 0; } int tfa_set_bf_volatile(tfa98xx_handle_t dev_idx, 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(dev_idx, 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(dev_idx, address, regvalue); if (err) { pr_err("Error setting bf :%d\n", -err); return -err; } return 0; } int tfa_get_bf(tfa98xx_handle_t dev_idx, 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(dev_idx, 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(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 = reg_write(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 = reg_read(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; /* power on the sub system */ TFA_SET_BF_VOLATILE(handle, PWDN, 0); 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: dev = %d", handle); 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; } /* * start the speakerboost algorithm * this implies a full system startup when the system was not already started * */ enum tfa98xx_error tfa_run_speaker_boost(tfa98xx_handle_t handle, int force, int profile) { enum tfa98xx_error err = TFA98XX_ERROR_OK; int value; int istap_prof = 0; if (force) { handles_local[handle].is_cold = 1; err = tfa_run_coldstartup(handle, profile); if (err) return err; /* DSP is running now */ } /* Returns 1 when device is "cold" and 0 when device is warm */ value = tfa_is_cold(handle); if (value < 0) err = value; else handles_local[handle].is_cold = value; pr_info("%s: %s boot, ext_dsp = %d, profile = %d\n", __func__, value ? "cold" : "warm", handles_local[handle].ext_dsp, profile); #ifdef __KERNEL__ /* TODO try to combine this with the pr_debug below */ tfa98xx_trace_printk("%s %sstart\n", tfa_cont_device_name(handle), value ? "cold" : "warm"); #endif /* Check if next profile is a tap profile */ istap_prof = tfa_cont_is_tap_profile(handle, profile); /* cold start and not tap profile */ if ((value == 1) && (!istap_prof)) { /* Run startup and write all files */ pr_info("%s: cold start, speaker startup\n", __func__); err = tfa_run_speaker_startup(handle, force, profile); if (err) { pr_info("%s: speaker startup error = %d\n", __func__, err); return err; } /* Save the current profile and set the vstep to 0 */ /* This needs to be overwriten even in CF bypass */ tfa_set_swprof(handle, (unsigned short)profile); tfa_set_swvstep(handle, 0); /* Dont run this for softDSP */ if (!handles_local[handle].ext_dsp) { /* Startup with CF in bypass then return here */ if (tfa_cf_enabled(handle) == 0) return err; #ifdef __KERNEL__ /* TODO check if this can move to the tfa98xx.c */ /* Necessary here for early calibrate (MTPEX + ACS) */ tfa98xx_apply_deferred_calibration(handle); #endif /* calibrate */ err = tfa_run_speaker_calibration(handle, profile); } else { /* Only for Tiberius */ /* always send the SetRe25 message * to indicate all messages are send */ err = tfa_set_calibration_values(handle); if (err) pr_info("%s: set calibration values error = %d\n", __func__, err); } } else if (istap_prof) { /* Dont run this for softDSP */ if (!handles_local[handle].ext_dsp) { /* Save the current profile and set the vstep to 0 */ /* This needs to be overwriten in tap profile */ tfa_set_swprof(handle, (unsigned short)profile); tfa_set_swvstep(handle, 0); } } return err; } enum tfa98xx_error tfa_run_speaker_startup(tfa98xx_handle_t handle, int force, int profile) { enum tfa98xx_error err = TFA98XX_ERROR_OK; pr_debug("coldstart%s :", force ? " (forced)" : ""); if (!force) { /* in case of force CF already runnning */ err = tfa_run_startup(handle, 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(handle) == 0) return err; if (!handles_local[handle].ext_dsp) { err = tfa_run_start_dsp(handle); if (err) return err; } } /* SET auto_copy_mtp_to_iic (bit 5 of A3) to 1. * Workaround for 72 and 88 (see PLMA5290) */ err = reg_write(handle, 0xA3, 0x20); if (err) return err; /* DSP is running now */ /* write all the files from the device list */ err = tfa_cont_write_files(handle); if (err) { pr_debug("[%s] tfa_cont_write_files error = %d\n", __func__, err); return err; } /* write all the files from the profile list (use volumstep 0) */ err = tfa_cont_write_files_prof(handle, 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(tfa98xx_handle_t handle, int profile) { enum tfa98xx_error err = TFA98XX_ERROR_OK; int calibrate_done, spkr_count = 0; (void)(profile); /* Remove unreferenced warning */ #ifdef __KERNEL__ /* Necessary otherwise we are thrown out of operating mode * in kernel (because of internal clock) */ if ((strnstr(tfa_cont_profile_name(handle, profile), ".cal", strlen(tfa_cont_profile_name(handle, profile))) == NULL) || (tfa98xx_dev_family(handle) == 1)) #endif TFA_SET_BF_VOLATILE(handle, SBSL, 1); /* return if there is no audio running */ if ((tfa98xx_dev_family(handle) == 2) && TFA_GET_BF(handle, NOCLK)) return TFA98XX_ERROR_NO_CLOCK; /* When MTPOTC is set (cal=once) unlock key2 */ if (TFA_GET_BF(handle, MTPOTC) == 1) tfa98xx_key2(handle, 0); /* await calibration, this should return ok */ err = tfa_run_wait_calibration(handle, &calibrate_done); if (err == TFA98XX_ERROR_OK) { err = tfa_dsp_get_calibration_impedance(handle); PRINT_ASSERT(err); } /* Give reason why calibration failed! */ if (err != TFA98XX_ERROR_OK) { if ((tfa98xx_dev_family(handle) == 2) && (TFA_GET_BF(handle, REFCKSEL) == 1)) pr_err("Unable to calibrate the device with the internal clock!\n"); } if (err == TFA98XX_ERROR_OK) { err = tfa98xx_supported_speakers(handle, &spkr_count); if (spkr_count == 1) pr_debug(" %d mOhms\n", handles_local[handle].mohm[0]); else pr_debug(" Prim:%d mOhms, Sec:%d mOhms\n", handles_local[handle].mohm[0], handles_local[handle].mohm[1]); } /* When MTPOTC is set (cal=once) re-lock key2 */ if (TFA_GET_BF(handle, MTPOTC) == 1) tfa98xx_key2(handle, 1); return err; } /* * Write calibration values for Tiberius */ enum tfa98xx_error tfa_set_calibration_values(tfa98xx_handle_t handle) { enum tfa98xx_error err = TFA98XX_ERROR_OK; unsigned char bytes[6] = {0}; unsigned short value = 0; int dsp_cal_value_left = 0, dsp_cal_value_right = 0; /* If calibration is set to once we load from MTP, else send zero's */ if (TFA_GET_BF(handle, MTPEX) == 1) { /* mono */ if (tfa98xx_cnt_max_device() == 1) { err = reg_read(handle, 0xF5, &value); dsp_cal_value_left = (value * 65536) / 1000; dsp_cal_value_right = dsp_cal_value_left; } else { /* stereo */ /* For now use quick hack * to assume handle 0 & 1 for stereo */ err = reg_read(0, 0xF5, &value); dsp_cal_value_left = (value * 65536) / 1000; err = reg_read(1, 0xF5, &value); dsp_cal_value_right = (value * 65536) / 1000; } /* We have to copy it for both channels. Even when MONO! */ bytes[0] = (uint8_t) ((dsp_cal_value_left >> 16) & 0xffff); bytes[1] = (uint8_t) ((dsp_cal_value_left >> 8) & 0xff); bytes[2] = (uint8_t) (dsp_cal_value_left & 0xff); bytes[3] = (uint8_t) ((dsp_cal_value_right >> 16) & 0xffff); bytes[4] = (uint8_t) ((dsp_cal_value_right >> 8) & 0xff); bytes[5] = (uint8_t) (dsp_cal_value_right & 0xff); } else { bytes[0] = 0; bytes[1] = 0; bytes[2] = 0; bytes[3] = 0; bytes[4] = 0; bytes[5] = 0; } err = tfa_dsp_cmd_id_write (handle, MODULE_SPEAKERBOOST, SB_PARAM_SET_RE25C, sizeof(bytes), bytes); #if defined(WRITE_CALIBRATION_DATA_TO_MTP) if (TFA_GET_BF(handle, MTPEX) == 0) err = tfa_tfadsp_wait_calibrate_done(handle); #endif /* print out readback for debugging */ if (TFA_GET_BF(handle, MTPEX) == 1) pr_info("%s: calibration data: %d\n", __func__, value); return err; } /* * Set the debug option */ void tfa_verbose(int level) { tfa98xx_trace_level = level; tfa98xx_runtime_verbose = (level != 0); /* any non-zero */ } int tfa_get_verbose(void) { return tfa98xx_trace_level; } void tfa_dsp_verbose(int level) { tfa98xx_dsp_verbose = level; } enum tfa98xx_error tfa_run_coldboot(tfa98xx_handle_t handle, 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(handle, ACS)) { /* set colstarted in CF_CONTROL to force ACS */ err = mem_write(handle, 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(tfa98xx_handle_t handle) { return tfa_cont_write_patch(handle); } /* * this will load the patch witch will implicitly start the DSP * if no patch is available the DPS is started immediately */ enum tfa98xx_error tfa_run_start_dsp(tfa98xx_handle_t handle) { enum tfa98xx_error err = TFA98XX_ERROR_OK; err = tfa_run_load_patch(handle); if (err) { /* patch load is fatal so return immediately*/ return err; } /* Clear count_boot, should be reset to 0 * before the DSP reset is released */ err = mem_write(handle, 512, 0, TFA98XX_DMEM_XMEM); PRINT_ASSERT(err); /* Reset DSP once for sure after initializing */ if (err == TFA98XX_ERROR_OK) { err = tfa98xx_dsp_reset(handle, 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(handle, TFA_GET_BF(handle, 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(tfa98xx_handle_t handle, int profile) { enum tfa98xx_error err = TFA98XX_ERROR_OK; struct tfa_device_list *dev = tfa_cont_device(handle); int i, noinit = 0; #if defined(REDUCED_REGISTER_SETTING) static int first_after_boot = 1; int is_cold_amp; #endif if (dev == NULL) return TFA98XX_ERROR_FAIL; #if defined(REDUCED_REGISTER_SETTING) is_cold_amp = tfa_is_cold_amp(handle); if (first_after_boot || (is_cold_amp == 1)) { #endif /* 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) { /* load the optimal TFA98XX in HW settings */ err = tfa98xx_init(handle); PRINT_ASSERT(err); } 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 */ err = tfa_cont_write_regs_dev(handle); /* 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__, first_after_boot, is_cold_amp); } #endif #if defined(REDUCED_REGISTER_SETTING) if ((first_after_boot || (is_cold_amp == 1)) || (profile != tfa_get_swprof(handle))) { #endif /* also write register the settings from the default profile * NOTE we may still have ACS=1 * so we can switch sample rate here */ err = tfa_cont_write_regs_prof(handle, profile); PRINT_ASSERT(err); #if defined(REDUCED_REGISTER_SETTING) } else { pr_info("%s: skip writing registers under profile (%d)\n", __func__, profile); } #endif if ((tfa98xx_dev_revision(handle) & 0xff) == 0x88) { /* 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); #if defined(REDUCED_REGISTER_SETTING) first_after_boot = 0; #endif } 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 tfa_run_coldstartup(tfa98xx_handle_t handle, int profile) { enum tfa98xx_error err = TFA98XX_ERROR_OK; err = tfa_run_startup(handle, profile); PRINT_ASSERT(err); if (err) return err; if ((handles_local[handle].rev & 0xFF) != 0x72) { /* force cold boot */ err = tfa_run_coldboot(handle, 1); /* set ACS */ PRINT_ASSERT(err); if (err) return err; } /* start */ err = tfa_run_start_dsp(handle); PRINT_ASSERT(err); return err; } /* * */ enum tfa98xx_error tfa_run_mute(tfa98xx_handle_t handle) { enum tfa98xx_error err = TFA98XX_ERROR_OK; int status; int tries = 0; /* signal the TFA98XX to mute */ err = tfa98xx_set_mute(handle, TFA98XX_MUTE_AMPLIFIER); if (tfa98xx_dev_family(handle) == 1) { 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); /*The amplifier is always switching*/ if (tries == AMPOFFWAIT_TRIES) { pr_err("%s: timeout in stopping amplifier switching\n", __func__); return TFA98XX_ERROR_OTHER; } } } if (tfa98xx_runtime_verbose) pr_debug("-------------------- muted --------------------\n"); return err; } /* * */ enum tfa98xx_error tfa_run_unmute(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 calibrate_done */ enum tfa98xx_error tfa_run_wait_calibration(tfa98xx_handle_t handle, int *calibrate_done) { enum tfa98xx_error err = TFA98XX_ERROR_OK; int tries = 0, mtp_busy = 1, tries_mtp_busy = 0; *calibrate_done = 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 * at least 25*50ms = 1.25 sec */ while ((*calibrate_done == 0) && (tries < MTPEX_WAIT_NTRIES)) { *calibrate_done = TFA_GET_BF(handle, MTPEX); if (*calibrate_done == 1) break; msleep_interruptible(50); /* wait 50ms to avoid busload */ tries++; } if (tries >= MTPEX_WAIT_NTRIES) tries = TFA98XX_API_WAITRESULT_NTRIES; } else { pr_err("MTP bussy after %d tries\n", MTPBWAIT_TRIES); } } /* poll xmem for calibrate always * 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(handle, TFA_FW_XMEM_CALIBRATION_DONE, 1, calibrate_done); tries++; } if (*calibrate_done != 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_STATE_TIMED_OUT; } else if (tries_mtp_busy == 1000) { pr_err("Calibrate Failed: MTP_busy stays high!\n"); err = TFA98XX_ERROR_STATE_TIMED_OUT; } /* 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, *calibrate_done); #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 profile = -1, cal_profile = -1, istap_prof = 0, active_profile = -1; #ifdef __KERNEL__ uint8_t kernel_sleep_timeout = 0; #endif tfa98xx_log_start_cnt++; tfa98xx_log_tfa_family = tfa98xx_dev_family(0); /* device 0 */ pr_debug("%s: tfa98xx_log_tfa_family=%d, ", __func__, tfa98xx_log_tfa_family); pr_debug("%s: tfa98xx_log_revision=0x%x, ", __func__, tfa98xx_log_revision); pr_debug("%s: tfa98xx_log_subrevision=0x%x, ", __func__, tfa98xx_log_subrevision); pr_debug("%s: tfa98xx_log_i2c_devicenum=/dev/i2c-%d, ", __func__, tfa98xx_log_i2c_devicenum); pr_debug("%s: tfa98xx_log_i2c_slaveaddress=0x%x, ", __func__, tfa98xx_log_i2c_slaveaddress); pr_info("%s: tfa98xx_log_start_cnt=%d\n", __func__, tfa98xx_log_start_cnt); if (devcount < 1) { pr_err("No or wrong container file loaded\n"); return tfa_error_bad_param; } for (dev = 0; dev < devcount; dev++) { err = tfa_cont_open(dev); if (err != TFA98XX_ERROR_OK) goto error_exit; if (TFA_GET_BF(dev, MTPEX) == 0) { int cal_prof_idx = -2; cal_prof_idx = tfa_cont_get_cal_profile(dev); if (cal_prof_idx >= 0) { pr_info("%s: set profile for calibration cal_prof_idx %d\n", __func__, cal_prof_idx); next_profile = cal_prof_idx; } break; } } for (dev = 0; dev < devcount; dev++) { /* We need to remember the next_profile otherwise * .cal profile overwrites this for the next dev */ profile = next_profile; /* Get currentprofile */ active_profile = tfa_get_swprof(dev); if (active_profile == 0xff) active_profile = -1; /* Check if next profile is a tap profile */ istap_prof = tfa_cont_is_tap_profile(dev, next_profile); /* tfaRun_SpeakerBoost implies un-mute */ if (tfa98xx_runtime_verbose) { pr_debug("active_profile:%s, next_profile:%s\n", tfa_cont_profile_name(dev, active_profile), tfa_cont_profile_name(dev, profile)); pr_debug("Starting device [%s]\n", tfa_cont_device_name(dev)); if (tfa98xx_dev_family(dev) == 2) err = show_current_state(dev); } /* Check if we need coldstart or ACS is set */ err = tfa_run_speaker_boost(dev, 0, profile); if (err != TFA98XX_ERROR_OK) goto error_exit; #if !defined(REDUCED_REGISTER_SETTING) if (profile == tfa_get_swprof(dev)) /* cold start already set profile */ pr_info("%s:[cold] device:%s profile:%s\n", __func__, tfa_cont_device_name(dev), tfa_cont_profile_name(dev, profile)); #else if (handles_local[dev].is_cold) /* separate cold flag * to adjust contents in container file */ pr_info("%s:[cold] device:%s profile:%s\n", __func__, tfa_cont_device_name(dev), tfa_cont_profile_name(dev, profile)); else pr_info("%s:[warm] device:%s profile:%s\n", __func__, tfa_cont_device_name(dev), tfa_cont_profile_name(dev, next_profile)); #endif } for (dev = 0; dev < devcount; dev++) { 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", tfa_cont_profile_name(dev, next_profile)); } /* check if the profile and steps are the one we want */ /* was it not done already */ if ((next_profile != active_profile && active_profile >= 0) || (istap_prof == 1)) { #if !defined(REDUCED_REGISTER_SETTING) pr_info("%s:[warm] device:%s profile:%s\n", __func__, tfa_cont_device_name(dev), tfa_cont_profile_name(dev, next_profile)); #endif err = tfa_cont_write_profile (dev, next_profile, vstep[dev]); if (err != TFA98XX_ERROR_OK) goto error_exit; if (handles_local[dev].is_cold == 0) { pr_debug("%s: flush buffer in blob, in warm start\n", __func__); err = tfa_tib_dsp_msgblob(dev, -2, NULL); } } /* If the profile contains the .standby suffix go * to powerdown else we should be in operating state */ if (strnstr(tfa_cont_profile_name(dev, next_profile), ".standby", strlen(tfa_cont_profile_name (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); #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 we are in state 0 or 1 we can skip * setting SBSL and RST, but also filters * and vstep (since there is no clk) * We can be in state 0 or 1 * because of standby profile or switching profile * with internal clock * No need to wait since we can only * get out of state 0 or 1 by setting a bit * (not by waiting) */ int manstate = TFA_GET_BF(dev, MANSTATE); if (manstate > 1) { /* Wait for the HW manager * to be in state 6 (initCF) * Absolute worstcase * the HW manager takes 28ms (norm = ~3) */ while ((manstate < 6) && (kernel_sleep_timeout < 28)) { kernel_sleep_timeout++; msleep_interruptible(1); manstate = TFA_GET_BF(dev, MANSTATE); } if ((manstate == 6) && (TFA_GET_BF(dev, RST) == 1) && (TFA_GET_BF(dev, SBSL) == 0)) { TFA_SET_BF_VOLATILE(dev, SBSL, 0); TFA_SET_BF(dev, RST, 0); } } } #endif /* Optimize Intelligent boost trip level * based on Re25 measurements */ err = tfa98xx_set_boost_trip_level(); tfa_set_swprof(dev, (unsigned short)next_profile); tfa_set_swvstep (dev, (unsigned short)tfa_cont_get_current_vstep(dev)); } for (dev = 0; dev < devcount; dev++) { if (tfa98xx_runtime_verbose && (tfa98xx_dev_family(dev) == 2)) show_current_state(dev); tfa_run_unmute(dev); /* unmute */ tfa_cont_close(dev); /* close all of them */ } #if defined(TFA_BLACKBOX_LOGGING) if (handles_local[0].log_set_cb) handles_local[0].log_set_cb(); #endif return err; error_exit: if (dev < devcount) if (tfa98xx_runtime_verbose && (tfa98xx_dev_family(dev) == 2)) show_current_state(dev); for (dev = 0; dev < devcount; dev++) tfa_cont_close(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 = tfa_cont_open(dev); if (err != TFA98XX_ERROR_OK) goto error_exit; if (tfa98xx_runtime_verbose) pr_debug("Stopping device [%s]\n", tfa_cont_device_name(dev)); /* mute */ tfa_run_mute(dev); /* powerdown CF */ err = tfa98xx_powerdown(dev, 1); if (err != TFA98XX_ERROR_OK) goto error_exit; /* Workaround for ticket PLMA5337 */ if ((handles_local[dev].rev & 0xff) == 0x72) TFA_SET_BF_VOLATILE(dev, AMPE, 0); /* 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++) tfa_cont_close(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; err = -TFA_SET_BF_VOLATILE(handle, I2CR, 1); if (err) return err; /* restore MANSCONF to POR state */ TFA_SET_BF_VOLATILE(handle, MANSCONF, 0); if ((handles_local[handle].rev & 0xff) != 0x72) { /* restore MANCOLD to POR state */ 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 = tfa_run_coldboot(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 = tfa_cont_open(dev); if (err != TFA98XX_ERROR_OK) break; if (tfa98xx_runtime_verbose) pr_debug("resetting device [%s]\n", tfa_cont_device_name(dev)); err = tfa98xx_reset(dev); if (err != TFA98XX_ERROR_OK) break; tfa_cont_close(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 = 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, dev; /* unsigned char bytes[6] = {0}; */ unsigned char bytes[TFACONT_MAXDEVS * 9] = {0}; /* 3 data */ unsigned int scaled_data[2]; /* int data[2]; */ int data[3]; /* 3 data */ error = tfa98xx_supported_speakers(handle, &spkr_count); if (error != TFA98XX_ERROR_OK) { pr_err("error in checking supported speakers\n"); return error; } if ((handles_local[handle].rev & 0xff) == 0x72) { if (TFA_GET_BF(handle, MTPOTC) && TFA_GET_BF(handle, MTPEX)) { pr_debug("Getting calibration values from MTP\n"); handles_local[handle].mohm[0] = tfa_read_reg(handle, (uint16_t)TFA_MK_BF((0xF5), 0, 16)); /* stereo */ if ((spkr_count > 1) && ((handle + 1) < tfa98xx_cnt_max_device())) { dev = handle + 1; if (!tfa98xx_handle_is_open (dev)) { /* If handle is not * open, try to open */ error = tfa_cont_open(dev); if (error != TFA98XX_ERROR_OK) return error; handles_local[handle].mohm[1] = tfa_read_reg(dev, (uint16_t) TFA_MK_BF((0xF5), 0, 16)); tfa_cont_close(dev); } } } else { pr_debug("Getting calibration values from Speakerboost\n"); /* nr_bytes = spkr_count * 3; */ nr_bytes = spkr_count * 9; /* 3 data */ pr_info("%s: read SB_PARAM_GET_RE25C\n", __func__); error = tfa_dsp_cmd_id_write_read (handle, MODULE_SPEAKERBOOST, SB_PARAM_GET_RE25C, nr_bytes, bytes); if (error == TFA98XX_ERROR_OK) { tfa98xx_convert_bytes2data (nr_bytes, bytes, data); /* signed data has` * a limit of 30 Ohm */ /* scaled_data[0] = data[0]; */ /* scaled_data[1] = data[1]; */ scaled_data[0] = data[1]; /* first data */ scaled_data[1] = data[2]; /* second data */ for (i = 0; i < spkr_count; i++) { handles_local[handle].mohm[i] = (scaled_data[i]*1000)/TFA_FW_ReZ_SCALE; } } else { for (i = 0; i < spkr_count; i++) handles_local[handle].mohm[i] = -1; } } } else if (TFA_GET_BF(handle, MTPOTC) && (((handles_local[handle].rev & 0xff) == 0x88) || (handles_local[handle].rev & 0xff) == 0x13)) { 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; */ nr_bytes = spkr_count * 9; /* 3 data */ error = tfa_dsp_cmd_id_write_read (handle, MODULE_SPEAKERBOOST, SB_PARAM_GET_RE25C, 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; } enum tfa98xx_error tfa_mtp_get_calibration(tfa98xx_handle_t handle, uint16_t *cal_data) { enum tfa98xx_error err = TFA98XX_ERROR_OK; *cal_data = (uint16_t)tfa_read_reg (handle, (uint16_t)TFA_MK_BF(0xF5, 0, 16)); return err; } enum tfa98xx_error tfa_mtp_set_calibration(tfa98xx_handle_t handle, uint16_t cal_data) { enum tfa98xx_error err = TFA98XX_ERROR_OK; int loop; tfa98xx_key2(handle, 0); /* unlock */ /* Write calibration values to MTP shadow register */ err = tfa_write_reg(handle, (uint16_t)TFA_MK_BF(0xF5, 0, 16), cal_data); if (err != TFA98XX_ERROR_OK) return err; /* CIMTP=1 start copying all the data from i2c regs_mtp to mtp*/ err = TFA_SET_BF(handle, CIMTP, 1); if (err != TFA98XX_ERROR_OK) return err; /* 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_STATE_TIMED_OUT; } /* start count from 1, 0 is invalid */ int tfa_get_swprof(tfa98xx_handle_t handle) { unsigned short rev = handles_local[handle].rev & 0xff; /* get from register if not set yet */ if (handles_local[handle].profile < 0) { switch (rev) { case 0x72:{ handles_local[handle].profile = tfa_get_bf(handle, TFA9872_BF_SWPROFIL) - 1; break; } case 0x88:{ handles_local[handle].profile = TFA_GET_BF(handle, SWPROFIL) - 1; break; } case 0x13:{ handles_local[handle].profile = tfa_get_bf(handle, TFA9912_BF_SWPROFIL) - 1; break; } default: break; } } 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); unsigned short rev = handles_local[handle].rev & 0xff; handles_local[handle].profile = new_value; switch (rev) { case 0x72: tfa_set_bf_volatile(handle, TFA9872_BF_SWPROFIL, new_value + 1); break; case 0x88: TFA_SET_BF_VOLATILE(handle, SWPROFIL, new_value + 1); break; case 0x13: tfa_set_bf_volatile(handle, TFA9912_BF_SWPROFIL, new_value + 1); break; default: /* for max1 devices */ /* 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 = 0; unsigned short rev = handles_local[handle].rev & 0xff; if (handles_local[handle].vstep[0] > 0) return handles_local[handle].vstep[0] - 1; switch (rev) { case 0x72: value = tfa_get_bf(handle, TFA9872_BF_SWVSTEP); break; case 0x88: value = TFA_GET_BF(handle, SWVSTEP); break; case 0x13: value = tfa_get_bf(handle, TFA9912_BF_SWVSTEP); break; } 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); unsigned short rev = handles_local[handle].rev & 0xff; handles_local[handle].vstep[0] = new_value; handles_local[handle].vstep[1] = new_value; switch (rev) { case 0x72: tfa_set_bf_volatile(handle, TFA9872_BF_SWVSTEP, new_value + 1); break; case 0x88: TFA_SET_BF_VOLATILE(handle, SWVSTEP, new_value + 1); break; case 0x13: tfa_set_bf_volatile(handle, TFA9912_BF_SWVSTEP, new_value + 1); break; default: /* for max1 devices */ /* 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; } int tfa_is_cold(tfa98xx_handle_t handle) { int value; if ((handles_local[handle].ext_dsp) || ((handles_local[handle].rev & 0xff) == 0x72)) { /* If device is already in operating state * then it has to be warm! (for profile switching) */ if (TFA_GET_BF(handle, MANSTATE) == 9) value = 0; /* warm */ else value = 1; /* cold */ } else { value = TFA_GET_BF(handle, ACS); } return value; } int tfa_is_cold_amp(tfa98xx_handle_t handle) { int value; if ((handles_local[handle].ext_dsp) || ((handles_local[handle].rev & 0xff) == 0x72)) /* * for non-dsp device reading MANSCONF * (src_set_configured) is a way * to check for cold boot status */ value = (TFA_GET_BF(handle, MANSCONF) == 0); else value = TFA_GET_BF(handle, ACS); return value; } int tfa_cf_enabled(tfa98xx_handle_t handle) { int value; /* For 72 there is no CF */ if ((handles_local[handle].rev & 0xff) == 0x72) value = (handles_local[handle].ext_dsp != 0); else value = TFA_GET_BF(handle, CFE); return value; } enum tfa98xx_error tfa98xx_set_boost_trip_level(void) { enum tfa98xx_error error = TFA98XX_ERROR_OK; int dev, devcount = tfa98xx_cnt_max_device(); unsigned short Re25C = 0, local_open = 0; for (dev = 0; dev < devcount; dev++) { if (!tfa98xx_handle_is_open(dev)) { /* If handle is not open, try to open */ error = tfa_cont_open(dev); if (error != TFA98XX_ERROR_OK) return error; local_open = 1; } /* If we dont have an MMAP (72 stand alone) * to access try reading the value from MTP */ if (Re25C == 0) { unsigned char mtp_reg_addr = 0xF5; /* mtp_reg_addr of tfa2 family as default */ /* set the MTP register address, * depending on the tfa device family */ if (handles_local[dev].tfa_family == 1) mtp_reg_addr = 0x83; else { if (handles_local[dev].tfa_family == 2) mtp_reg_addr = 0xF5; } Re25C = (unsigned short)tfa_read_reg(dev, (uint16_t)TFA_MK_BF ((mtp_reg_addr), 0, 16)); } if (handles_local[dev].dev_ops.tfa_set_boost_trip_level) error = (*handles_local[dev] .dev_ops.tfa_set_boost_trip_level)(dev, Re25C); /* If locally opened we also need to close! */ if (local_open == 1) tfa_cont_close(dev); } return error; } /* * tfa external interfacing and event handling */ /** * parse the event and call driver in case of tfadsp event */ static int tfa_dsp_handle_event(tfa98xx_handle_t handle, enum tfadsp_event_en tfadsp_event) { int retval = handles_local[handle].rev; /* return revid by default */ enum tfa_error err; switch (tfadsp_event) { case TFADSP_EXT_PWRUP: /*DSP starting*/ /* pr_info("set cold\n"); */ handles_local[handle].ext_dsp = 1; handles_local[handle].is_cold = 1; break; case TFADSP_CMD_READY: /*Ready to receive commands*/ /* confirm */ pr_info("TFADSP_CMD_READY: call tfa_start to send msgs\n"); pr_info("TFADSP_CMD_READY: set cold\n"); handles_local[handle].ext_dsp = 1; err = tfa_start (handles_local[handle].profile, handles_local[handle].vstep); if (err == tfa_error_ok) { handles_local[handle].ext_dsp = 2; /* set warm */ handles_local[handle].is_cold = 0; retval = 0; } else retval = -1*err; break; /* * case TFADSP_WARM: // config complete * enable amp * send Re0 * await pilot tone [audio power detect] * audio active * break; */ case TFADSP_EXT_PWRDOWN: /* DSP stopping */ pr_info("disable ext dsp\n"); handles_local[handle].ext_dsp = 0; /* unregister */ /* amp off */ break; case TFADSP_SOFT_MUTE_READY: /* [audio low power detect]: Muting completed */ break; case TFADSP_VOLUME_READY: /* Volume change completed */ break; case TFADSP_DAMAGED_SPEAKER: /* Damaged speaker was detected */ /* amp off */ break; default: pr_err("%s: unknown tfadsp event:0x%0x\n", __func__, tfadsp_event); retval = -1; break; } handles_local[handle].tfadsp_event = tfadsp_event; /* record active event */ return retval; } /* Since this is only valid for Tiberius we can set this for 2 handles, * regardless if we are mono or stereo. */ /* int tfa_ext_register(dsp_write_reg_t tfa_write_reg, * dsp_send_message_t tfa_send_message, * dsp_read_message_t tfa_read_message, * tfa_event_handler_t *tfa_event_handler) */ int tfa_ext_register(dsp_send_message_t tfa_send_message, dsp_read_message_t tfa_read_message, tfa_event_handler_t *tfa_event_handler) { /* handles_local[0].rev = 0x1b72; // added for smart studio. */ /* pr_info("%s: device type (0x%02x)\n", * __func__, handles_local[0].rev); * if (((handles_local[0].rev & 0xff) == 0x72) * || (handles_local[0].ext_dsp != 0)) */ { /* * if (tfa_write_reg != NULL) { * handles_local[0].dev_ops.reg_write = * (reg_write_t)tfa_write_reg; * handles_local[1].dev_ops.reg_write = * (reg_write_t)tfa_write_reg; * } */ /* assume that external is always device 0, * Don't do this for device 1! */ handles_local[0].ext_dsp = 1; /* set cold 1st msg */ handles_local[0].is_cold = 1; handles_local[0].profile = 0; handles_local[1].profile = 0; handles_local[0].dev_ops.dsp_msg = (dsp_msg_t)tfa_send_message; handles_local[1].dev_ops.dsp_msg = (dsp_msg_t)tfa_send_message; handles_local[0].dev_ops.dsp_msg_read = (dsp_msg_read_t)tfa_read_message; handles_local[1].dev_ops.dsp_msg_read = (dsp_msg_read_t)tfa_read_message; if (tfa_event_handler != NULL) *tfa_event_handler = tfa_dsp_handle_event; } return 0; } EXPORT_SYMBOL(tfa_ext_register); /* This is required to set the right status when remote is used */ void tfa_ext_set_ext_dsp(int value) { handles_local[0].ext_dsp = 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(tfa98xx_handle_t dev_idx, uint8_t *prev, uint8_t *next) { uint8_t bq, eq; int eq_offset; int new_cost, old_cost; uint32_t eq_biquad_mask[NR_EQ]; enum tfa98xx_error err = TFA98XX_ERROR_OK; struct dsp_msg_all_coeff *data1 = (struct dsp_msg_all_coeff *)prev; struct dsp_msg_all_coeff *data2 = (struct dsp_msg_all_coeff *)next; old_cost = DSP_MSG_OVERHEAD + 3 + sizeof(struct dsp_msg_all_coeff); new_cost = 0; eq_offset = 0; for (eq = 0; eq < NR_EQ; eq++) { uint8_t *eq1 = &data1->biquad[eq_offset][0][0]; uint8_t *eq2 = &data2->biquad[eq_offset][0][0]; eq_biquad_mask[eq] = 0; if (memcmp(eq1, eq2, BQ_SIZE*eq_biquads[eq]) != 0) { int nr_bq = 0; int bq_sz, eq_sz; for (bq = 0; bq < eq_biquads[eq]; bq++) { uint8_t *bq1 = &eq1[bq*BQ_SIZE]; uint8_t *bq2 = &eq2[bq*BQ_SIZE]; if (memcmp(bq1, bq2, BQ_SIZE) != 0) { eq_biquad_mask[eq] |= (1 << bq); nr_bq++; } } bq_sz = (2 * 3 + BQ_SIZE) * nr_bq; eq_sz = 2 * 3 + BQ_SIZE * eq_biquads[eq]; /* dsp message i2c transaction overhead */ bq_sz += DSP_MSG_OVERHEAD * nr_bq; eq_sz += DSP_MSG_OVERHEAD; if (bq_sz >= eq_sz) { eq_biquad_mask[eq] = 0xffffffff; new_cost += eq_sz; } else { new_cost += bq_sz; } } pr_debug("eq_biquad_mask[%d] = 0x%.8x\n", eq, eq_biquad_mask[eq]); eq_offset += eq_biquads[eq]; } pr_debug("cost for writing all coefficients = %d\n", old_cost); pr_debug("cost for writing changed coefficients = %d\n", new_cost); if (new_cost >= old_cost) { const int buffer_sz = 3 + sizeof(struct dsp_msg_all_coeff); uint8_t *buffer; buffer = kmalloc(buffer_sz, GFP_KERNEL); if (buffer == NULL) return TFA98XX_ERROR_FAIL; /* cmd id */ buffer[0] = 0x00; buffer[1] = 0x82; buffer[2] = 0x00; /* parameters */ memcpy(&buffer[3], data2, sizeof(struct dsp_msg_all_coeff)); err = dsp_msg(dev_idx, 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] = 0x82; msg[2] = 0x00; /* select eq and bq */ msg[3] = 0x00; msg[4] = eq + 1; msg[5] = 0x00; /* all biquads */ /* biquad parameters */ memcpy(&msg[6], eq2, BQ_SIZE * eq_biquads[eq]); err = dsp_msg(dev_idx, 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 = kmalloc(msg_sz, 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] = 0x82; msg[2] = 0x00; /* select eq and bq */ msg[3] = 0x00; msg[4] = eq + 1; msg[5] = bq + 1; /* biquad parameters */ memcpy(&msg[6], bq2, BQ_SIZE); err = dsp_msg(dev_idx, msg_sz, (const char *)msg); if (err) { kfree(msg); return err; } } } kfree(msg); } eq_offset += eq_biquads[eq]; } return err; } int get_manager_state(tfa98xx_handle_t handle) { int manstate = -1; /* First we need to find the current manager state */ if (tfa98xx_dev_family(handle) == 2) { manstate = TFA_GET_BF(handle, MANSTATE); } else { /* For max1 we dont have a manager state bitfield * so we need to determine the Manager state ourself */ if (TFA_GET_BF(handle, PWDN) == 1) { /* we are in powerdown state */ manstate = 0; } else if (TFA_GET_BF(handle, SBSL) == 0) { /* we are in initCF state */ manstate = 6; } else if (TFA_GET_BF(handle, AMPS) == 1) { /* we are in operating state */ manstate = 9; } else { /* we are in unknown state? */ if (tfa98xx_runtime_verbose) pr_debug("Error: Unknown state!\n"); return TFA_ERROR; } } return manstate; } int set_manager_state(tfa98xx_handle_t handle, int state_nr) /* TODO CHANGE INT TO ENUM! */ { int manstate, not_possible = 0; /* Get the current manager state */ manstate = get_manager_state(handle); if (manstate < 0) return TFA98XX_ERROR_BAD_PARAMETER; if (tfa98xx_runtime_verbose) pr_debug("Current HW manager state for device %d is %d\n", handle, manstate); if (state_nr == manstate) { if (tfa98xx_runtime_verbose) pr_debug("HW manager state for device %d is already correct\n", handle); return TFA98XX_ERROR_OK; } /* It is not possible to switch to every state: * For example: From powerdown to operating is not possible, * we cannot do this with setting bitfield alone */ if (tfa98xx_dev_family(handle) == 2) { if (manstate == 0 && (state_nr != 6 && state_nr != 1)) not_possible = 1; else if (manstate == 1 && (state_nr != 6 && state_nr != 0)) not_possible = 1; else if (manstate == 6 && (state_nr != 9 && state_nr != 0)) not_possible = 1; else if (manstate == 9 && state_nr != 0) not_possible = 1; else { /* [11/14/2016]: Needs more testing * to find the corner cases! */ switch (state_nr) { case 0: TFA_SET_BF(handle, PWDN, 1); break; case 1: TFA_SET_BF(handle, SBSL, 0); TFA_SET_BF(handle, MANSCONF, 0); TFA_SET_BF(handle, PWDN, 0); break; case 6: TFA_SET_BF(handle, SBSL, 0); TFA_SET_BF(handle, MANSCONF, 1); TFA_SET_BF(handle, PWDN, 0); break; case 9: TFA_SET_BF(handle, SBSL, 1); break; } } } else { if (manstate == 0 && state_nr != 6) not_possible = 1; else if (manstate == 6 && (state_nr != 9 && state_nr != 0)) not_possible = 1; else if (manstate == 9 && state_nr != 0) not_possible = 1; else { /* [11/14/2016]: Needs more testing * to find the corner cases! */ switch (state_nr) { case 0: TFA_SET_BF(handle, PWDN, 1); break; case 6: TFA_SET_BF(handle, SBSL, 0); TFA_SET_BF(handle, PWDN, 0); break; case 9: TFA_SET_BF(handle, SBSL, 1); break; } } } if (not_possible) { pr_debug("Error: It is not possible to switch to state %d from current state %d\n", state_nr, manstate); return TFA98XX_ERROR_BAD_PARAMETER; } msleep_interruptible(100); /* wait 100ms to get the correct state */ manstate = get_manager_state(handle); if (manstate < 0) return TFA98XX_ERROR_BAD_PARAMETER; if (tfa98xx_runtime_verbose) pr_debug("New HW manager state for device %d is %d\n", handle, manstate); return TFA98XX_ERROR_OK; } /*****************************************************************************/ enum tfa98xx_error tfa_tfadsp_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}; */ unsigned char bytes[TFACONT_MAXDEVS * 9] = {0}; /* 3 data */ unsigned int scaled_data[2]; int data[3]; /* 3 data */ if ((handles_local[handle].stream_state & BIT_PSTREAM) != 1) { pr_info("%s: no playback; getting calibration fail from tfadsp\n", __func__); return TFA98XX_ERROR_RPC_CALIB_FAILED; } error = tfa98xx_supported_speakers(handle, &spkr_count); if (error == TFA98XX_ERROR_OK) { pr_debug("%s: get calibration values from tfadsp\n", __func__); nr_bytes = spkr_count * 9; /* 3 data */ pr_info("%s: read SB_PARAM_GET_RE25C\n", __func__); error = tfa_dsp_cmd_id_write_read (handle, MODULE_SPEAKERBOOST, SB_PARAM_GET_RE25C, nr_bytes, bytes); if (error == TFA98XX_ERROR_OK) { tfa98xx_convert_bytes2data(nr_bytes, bytes, data); /* signed data has a limit of 30 Ohm */ scaled_data[0] = data[1]; /* first data */ scaled_data[1] = data[2]; /* second data */ for (i = 0; i < spkr_count; i++) { handles_local[handle].mohm[i] = (scaled_data[i] * 1000) / TFA_FW_ReZ_SCALE; } } else { for (i = 0; i < spkr_count; i++) handles_local[handle].mohm[i] = -1; } } return error; } enum tfa98xx_error tfa_tfadsp_calibrate(tfa98xx_handle_t handle) { enum tfa98xx_error err = TFA98XX_ERROR_OK; int cal_prof_idx = 0; if ((handles_local[handle].stream_state & BIT_PSTREAM) != 1) { pr_info("%s: no playback; speaker init calibration fail\n", __func__); return TFA98XX_ERROR_RPC_CALIB_FAILED; } pr_info("%s: begin\n", __func__); if (TFA_GET_BF(handle, MTPEX) == 1) { pr_debug("speaker is already calibrated; reset MTPEX first\n"); /* reset MTPEX */ err = tfa98xx_set_mtp(handle, 0 << TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS, TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK); if (err) { pr_err("%s: setting MPTEX failed, err (%d)\n", __func__, err); return TFA98XX_ERROR_RPC_CALIB_FAILED; } } cal_prof_idx = tfa_cont_get_cal_profile(handle); if (cal_prof_idx < 0) cal_prof_idx = 0; pr_info("%s: set profile for calibration cal_prof_idx %d\n", __func__, cal_prof_idx); err = tfa_cont_write_files(handle); PRINT_ASSERT(err); err = tfa_cont_write_regs_prof(handle, cal_prof_idx); PRINT_ASSERT(err); err = tfa_cont_write_files_prof(handle, cal_prof_idx, 0); PRINT_ASSERT(err); tfa_set_swprof(handle, (unsigned short)cal_prof_idx); pr_info("%s: set_calibration\n", __func__); err = tfa_set_calibration_values(handle); /* SetRe25C with {0, 0} */ PRINT_ASSERT(err); #if defined(WRITE_CALIBRATION_DATA_TO_MTP) if (TFA_GET_BF(handle, MTPEX) == 0) err = tfa_tfadsp_wait_calibrate_done(handle); #else err = tfa_tfadsp_wait_calibrate_done(handle); #endif pr_info("%s: end\n", __func__); return err; } static enum tfa98xx_error tfa_tfadsp_wait_calibrate_done(tfa98xx_handle_t handle) { enum tfa98xx_error err = TFA98XX_ERROR_OK; struct tfa98xx_controls *controls = &(handles_local[handle].dev_ops.controls); char buffer[12] = {0}; int calibration_done = 0; int otc_val = 1; uint16_t readback; int count; pr_info("%s: begin\n", __func__); controls->calib.wr_value = true; /* request was triggered from here */ controls->calib.rd_valid = false; /* result not available */ controls->calib.rd_value = false; /* result not valid (dafault) */ pr_info("%s: wait until calibration is done\n", __func__); for (count = 0; count < MAX_WAIT_COUNT_UNTIL_CALIBRATION_DONE; count++) { err = tfa_dsp_cmd_id_write_read (handle, MODULE_FRAMEWORK, FW_PAR_ID_GET_STATUS_CHANGE, 9 /* 3 data */, (unsigned char *)buffer); msleep_interruptible(100); pr_debug("%s: err (%d), buffer (0x%x), count (%d)\n", __func__, err, buffer[2], count); if (buffer[8] & 0x1) break; } if (count >= MAX_WAIT_COUNT_UNTIL_CALIBRATION_DONE) { pr_err("%s: calibration failed from time out\n", __func__); err = TFA98XX_ERROR_RPC_CALIB_BUSY; goto wait_calibrate_done_error_exit; } err = tfa_tfadsp_get_calibration_impedance(handle); if (err) { pr_err("%s: getting calibration data failed, err (%d)\n", __func__, err); err = TFA98XX_ERROR_RPC_CALIB_FAILED; goto wait_calibrate_done_error_exit; } pr_info("%s: calibration done\n", __func__); calibration_done = 1; pr_info("Prim:%d mOhms", handles_local[handle].mohm[0]); #if defined(CHECK_CALIBRATION_DATA_RANGE) err = tfa_calibration_range_check (handle, handles_local[handle].mohm[0]); if (err) { calibration_done = 0; pr_err("%s: calibration data is out of range\n", __func__); err = TFA98XX_ERROR_BAD_PARAMETER; goto wait_calibrate_done_error_exit; } #endif #if defined(WRITE_CALIBRATION_DATA_TO_MTP) if ((handles_local[handle].mohm[0] > 0) && calibration_done) { err = tfa_mtp_set_calibration (handle, handles_local[handle].mohm[0]); if (err) { pr_err("%s: writing calibration data failed to MTP, err (%d)\n", __func__, err); err = TFA98XX_ERROR_RPC_CALIB_FAILED; goto wait_calibrate_done_error_exit; } /* readback after writing onto MTP */ err = tfa_mtp_get_calibration(handle, &readback); if (err) { pr_err("%s: reading calibration data back failed from MTP, err (%d)\n", __func__, err); err = TFA98XX_ERROR_RPC_CALIB_FAILED; goto wait_calibrate_done_error_exit; } /* write again if readback is out of range */ pr_info("%s: calibaration data %d, readback from MTP\n", __func__, readback); #if defined(CHECK_CALIBRATION_DATA_RANGE) err = tfa_calibration_range_check(handle, readback); #else err = (readback == 0) ? TFA98XX_ERROR_BAD_PARAMETER : TFA98XX_ERROR_OK; #endif if (err) { err = tfa_mtp_set_calibration (handle, handles_local[handle].mohm[0]); if (err) { pr_err("%s: writing calibration data failed to MTP (2nd), err (%d)\n", __func__, err); err = TFA98XX_ERROR_RPC_CALIB_FAILED; goto wait_calibrate_done_error_exit; } } if (controls->otc.deferrable && controls->otc.triggered) { otc_val = controls->otc.wr_value; pr_debug("Deferred writing otc = %d\n", otc_val); } else { otc_val = 1; } /* set OTC <0:always 1:once> */ err = tfa98xx_set_mtp(handle, (otc_val & 1) << TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS, TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK); if (err) { pr_info("%s: setting OTC failed, err (%d)\n", __func__, err); err = TFA98XX_ERROR_RPC_CALIB_FAILED; goto wait_calibrate_done_error_exit; } else { controls->otc.triggered = false; controls->otc.wr_value = otc_val; controls->otc.rd_value = otc_val; } /* set MTPEX */ err = tfa98xx_set_mtp(handle, 1 << TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS, TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK); if (err) { pr_err("%s: setting MPTEX failed, err (%d)\n", __func__, err); err = TFA98XX_ERROR_RPC_CALIB_FAILED; goto wait_calibrate_done_error_exit; } /* readback after writing onto MTP and setting MTPEX */ err = tfa_mtp_get_calibration(handle, &readback); if (err) { pr_err("%s: reading calibration data back failed from MTP (2nd), err (%d)\n", __func__, err); err = TFA98XX_ERROR_RPC_CALIB_FAILED; goto wait_calibrate_done_error_exit; } /* panic if readback is out of range */ pr_info("%s: calibaration data %d readback from MTP (2nd)\n", __func__, readback); #if defined(CHECK_CALIBRATION_DATA_RANGE) err = tfa_calibration_range_check(handle, readback); #else err = (readback == 0) ? TFA98XX_ERROR_BAD_PARAMETER : TFA98XX_ERROR_OK; #endif if (err) { pr_err("%s: calibration data (%d) is out of range, readback from MTP\n", __func__, readback); panic("[AUDIO_BSP] SPK INIT CALIBRATION Fail."); } } #endif /* WRITE_CALIBRATION_DATA_TO_MTP */ pr_info("%s: end\n", __func__); wait_calibrate_done_error_exit: if (err) { controls->calib.wr_value = false; /* request was not in triggered state */ controls->calib.rd_valid = false; /* result not available */ controls->calib.rd_value = false; /* result not valid (dafault) */ } else { controls->calib.wr_value = false; /* request was not in triggered state */ controls->calib.rd_valid = true; /* result available */ controls->calib.rd_value = true; /* result valid */ } return err; } #if defined(CHECK_CALIBRATION_DATA_RANGE) #define LOWER_LIMIT_CAL_N3B 0 #define UPPER_LIMIT_CAL_N3B 32000 #define LOWER_LIMIT_CAL_N1A 0 #define UPPER_LIMIT_CAL_N1A 32000 enum tfa98xx_error tfa_calibration_range_check(tfa98xx_handle_t handle, int mohm) { enum tfa98xx_error err = TFA98XX_ERROR_OK; int lower_limit_cal = LOWER_LIMIT_CAL_N3B, upper_limit_cal = UPPER_LIMIT_CAL_N3B; if (((handles_local[handle].rev >> 8) & 0xff) == 0x1a) { lower_limit_cal = LOWER_LIMIT_CAL_N1A; upper_limit_cal = UPPER_LIMIT_CAL_N1A; } pr_info("%s: calibration range chceck [%d, %d] with %d\n", __func__, lower_limit_cal, upper_limit_cal, mohm); if (mohm < lower_limit_cal || mohm > upper_limit_cal) err = TFA98XX_ERROR_BAD_PARAMETER; return err; } #endif /* CHECK_CALIBRATION_DATA_RANGE */ /**************************************************************************/