/* ** ============================================================================= ** Copyright (c) 2016 Texas Instruments Inc. ** ** This program is free software; you can redistribute it and/or modify it under ** the terms of the GNU General Public License as published by the Free Software ** Foundation; version 2. ** ** This program is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ** ** File: ** tas2557-regmap.c ** ** Description: ** I2C driver with regmap for Texas Instruments TAS2557 High Performance 4W Smart Amplifier ** ** ============================================================================= */ #ifdef CONFIG_TAS2557_REGMAP #define DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tas2557.h" #include "tas2557-core.h" #ifdef CONFIG_TAS2557_CODEC #include "tas2557-codec.h" #endif #ifdef CONFIG_TAS2557_MISC #include "tas2557-misc.h" #endif #define ENABLE_TILOAD #ifdef ENABLE_TILOAD #include "tiload.h" #endif #define LOW_TEMPERATURE_GAIN 6 #define LOW_TEMPERATURE_COUNTER 12 static int tas2557_change_book_page( struct tas2557_priv *pTAS2557, unsigned char nBook, unsigned char nPage) { int nResult = 0; if ((pTAS2557->mnCurrentBook == nBook) && pTAS2557->mnCurrentPage == nPage) goto end; if (pTAS2557->mnCurrentBook != nBook) { nResult = regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_PAGE, 0); if (nResult < 0) { dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", __func__, __LINE__, nResult); goto end; } pTAS2557->mnCurrentPage = 0; nResult = regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_REG, nBook); if (nResult < 0) { dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", __func__, __LINE__, nResult); goto end; } pTAS2557->mnCurrentBook = nBook; if (nPage != 0) { nResult = regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_PAGE, nPage); if (nResult < 0) { dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", __func__, __LINE__, nResult); goto end; } pTAS2557->mnCurrentPage = nPage; } } else if (pTAS2557->mnCurrentPage != nPage) { nResult = regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_PAGE, nPage); if (nResult < 0) { dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", __func__, __LINE__, nResult); goto end; } pTAS2557->mnCurrentPage = nPage; } end: if (nResult < 0) pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; else pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; return nResult; } static int tas2557_dev_read( struct tas2557_priv *pTAS2557, unsigned int nRegister, unsigned int *pValue) { int nResult = 0; unsigned int Value = 0; mutex_lock(&pTAS2557->dev_lock); if (pTAS2557->mbTILoadActive) { if (!(nRegister & 0x80000000)) goto end; /* let only reads from TILoad pass. */ nRegister &= ~0x80000000; dev_dbg(pTAS2557->dev, "TiLoad R REG B[%d]P[%d]R[%d]\n", TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister), TAS2557_PAGE_REG(nRegister)); } nResult = tas2557_change_book_page(pTAS2557, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister)); if (nResult >= 0) { nResult = regmap_read(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), &Value); if (nResult < 0) { dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", __func__, __LINE__, nResult); pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; goto end; } else pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; *pValue = Value; } end: mutex_unlock(&pTAS2557->dev_lock); return nResult; } static int tas2557_dev_write( struct tas2557_priv *pTAS2557, unsigned int nRegister, unsigned int nValue) { int nResult = 0; mutex_lock(&pTAS2557->dev_lock); if ((nRegister == 0xAFFEAFFE) && (nValue == 0xBABEBABE)) { pTAS2557->mbTILoadActive = true; goto end; } if ((nRegister == 0xBABEBABE) && (nValue == 0xAFFEAFFE)) { pTAS2557->mbTILoadActive = false; goto end; } if (pTAS2557->mbTILoadActive) { if (!(nRegister & 0x80000000)) goto end;/* let only writes from TILoad pass. */ nRegister &= ~0x80000000; dev_dbg(pTAS2557->dev, "TiLoad W REG B[%d]P[%d]R[%d] =0x%x\n", TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister), TAS2557_PAGE_REG(nRegister), nValue); } nResult = tas2557_change_book_page(pTAS2557, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister)); if (nResult >= 0) { nResult = regmap_write(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), nValue); if (nResult < 0) { dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", __func__, __LINE__, nResult); pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; } else pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; } end: mutex_unlock(&pTAS2557->dev_lock); return nResult; } static int tas2557_dev_bulk_read( struct tas2557_priv *pTAS2557, unsigned int nRegister, u8 *pData, unsigned int nLength) { int nResult = 0; mutex_lock(&pTAS2557->dev_lock); if (pTAS2557->mbTILoadActive) { if (!(nRegister & 0x80000000)) goto end; /* let only writes from TILoad pass. */ nRegister &= ~0x80000000; dev_dbg(pTAS2557->dev, "TiLoad BR REG B[%d]P[%d]R[%d], count=%d\n", TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister), TAS2557_PAGE_REG(nRegister), nLength); } nResult = tas2557_change_book_page(pTAS2557, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister)); if (nResult >= 0) { nResult = regmap_bulk_read(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), pData, nLength); if (nResult < 0) { dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", __func__, __LINE__, nResult); pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; } else pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; } end: mutex_unlock(&pTAS2557->dev_lock); return nResult; } static int tas2557_dev_bulk_write( struct tas2557_priv *pTAS2557, unsigned int nRegister, u8 *pData, unsigned int nLength) { int nResult = 0; mutex_lock(&pTAS2557->dev_lock); if (pTAS2557->mbTILoadActive) { if (!(nRegister & 0x80000000)) goto end; /* let only writes from TILoad pass. */ nRegister &= ~0x80000000; dev_dbg(pTAS2557->dev, "TiLoad BW REG B[%d]P[%d]R[%d], count=%d\n", TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister), TAS2557_PAGE_REG(nRegister), nLength); } nResult = tas2557_change_book_page( pTAS2557, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister)); if (nResult >= 0) { nResult = regmap_bulk_write(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), pData, nLength); if (nResult < 0) { dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", __func__, __LINE__, nResult); pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; } else pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; } end: mutex_unlock(&pTAS2557->dev_lock); return nResult; } static int tas2557_dev_update_bits( struct tas2557_priv *pTAS2557, unsigned int nRegister, unsigned int nMask, unsigned int nValue) { int nResult = 0; mutex_lock(&pTAS2557->dev_lock); if (pTAS2557->mbTILoadActive) { if (!(nRegister & 0x80000000)) goto end; /* let only writes from TILoad pass. */ nRegister &= ~0x80000000; dev_dbg(pTAS2557->dev, "TiLoad SB REG B[%d]P[%d]R[%d], mask=0x%x, value=0x%x\n", TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister), TAS2557_PAGE_REG(nRegister), nMask, nValue); } nResult = tas2557_change_book_page( pTAS2557, TAS2557_BOOK_ID(nRegister), TAS2557_PAGE_ID(nRegister)); if (nResult >= 0) { nResult = regmap_update_bits(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), nMask, nValue); if (nResult < 0) { dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n", __func__, __LINE__, nResult); pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM; } else pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM; } end: mutex_unlock(&pTAS2557->dev_lock); return nResult; } void tas2557_clearIRQ(struct tas2557_priv *pTAS2557) { unsigned int nValue; int nResult = 0; nResult = pTAS2557->read(pTAS2557, TAS2557_FLAGS_1, &nValue); if (nResult >= 0) pTAS2557->read(pTAS2557, TAS2557_FLAGS_2, &nValue); } void tas2557_enableIRQ(struct tas2557_priv *pTAS2557, bool enable, bool startup_chk) { if (enable) { if (!pTAS2557->mbIRQEnable) { if (gpio_is_valid(pTAS2557->mnGpioINT)) { enable_irq(pTAS2557->mnIRQ); if (startup_chk) { /* check after 10 ms */ schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(10)); } pTAS2557->mbIRQEnable = true; } } } else { if (gpio_is_valid(pTAS2557->mnGpioINT)) disable_irq_nosync(pTAS2557->mnIRQ); pTAS2557->mbIRQEnable = false; } } static void tas2557_hw_reset(struct tas2557_priv *pTAS2557) { if (gpio_is_valid(pTAS2557->mnResetGPIO)) { gpio_direction_output(pTAS2557->mnResetGPIO, 0); msleep(5); gpio_direction_output(pTAS2557->mnResetGPIO, 1); msleep(2); } pTAS2557->mnCurrentBook = -1; pTAS2557->mnCurrentPage = -1; if (pTAS2557->mnErrCode) dev_info(pTAS2557->dev, "before reset, ErrCode=0x%x\n", pTAS2557->mnErrCode); pTAS2557->mnErrCode = 0; } static void irq_work_routine(struct work_struct *work) { int nResult = 0; unsigned int nDevInt1Status = 0, nDevInt2Status = 0; unsigned int nDevPowerUpFlag = 0; int nCounter = 2; struct tas2557_priv *pTAS2557 = container_of(work, struct tas2557_priv, irq_work.work); #ifdef CONFIG_TAS2557_CODEC mutex_lock(&pTAS2557->codec_lock); #endif #ifdef CONFIG_TAS2557_MISC mutex_lock(&pTAS2557->file_lock); #endif if(pTAS2557->mnErrCode & ERROR_FAILSAFE) goto program; if (pTAS2557->mbRuntimeSuspend) { dev_info(pTAS2557->dev, "%s, Runtime Suspended\n", __func__); goto end; } if (!pTAS2557->mbPowerUp) { dev_info(pTAS2557->dev, "%s, device not powered\n", __func__); goto end; } if ((!pTAS2557->mpFirmware->mnConfigurations) || (!pTAS2557->mpFirmware->mnPrograms)) { dev_info(pTAS2557->dev, "%s, firmware not loaded\n", __func__); goto end; } nResult = tas2557_dev_write(pTAS2557, TAS2557_GPIO4_PIN_REG, 0x00); if (nResult < 0) goto program; nResult = tas2557_dev_read(pTAS2557, TAS2557_FLAGS_1, &nDevInt1Status); if (nResult >= 0) nResult = tas2557_dev_read(pTAS2557, TAS2557_FLAGS_2, &nDevInt2Status); if (nResult < 0) goto program; if (((nDevInt1Status & 0xfc) != 0) || ((nDevInt2Status & 0x0c) != 0)) { /* in case of INT_OC, INT_UV, INT_OT, INT_BO, INT_CL, INT_CLK1, INT_CLK2 */ dev_err(pTAS2557->dev, "critical error: 0x%x, 0x%x\n", nDevInt1Status, nDevInt2Status); if (nDevInt1Status & 0x80) { pTAS2557->mnErrCode |= ERROR_OVER_CURRENT; dev_err(pTAS2557->dev, "DEVA SPK over current!\n"); } else pTAS2557->mnErrCode &= ~ERROR_OVER_CURRENT; if (nDevInt1Status & 0x40) { pTAS2557->mnErrCode |= ERROR_UNDER_VOLTAGE; dev_err(pTAS2557->dev, "DEVA SPK under voltage!\n"); } else pTAS2557->mnErrCode &= ~ERROR_UNDER_VOLTAGE; if (nDevInt1Status & 0x20) { pTAS2557->mnErrCode |= ERROR_CLK_HALT; dev_err(pTAS2557->dev, "DEVA clk halted!\n"); } else pTAS2557->mnErrCode &= ~ERROR_CLK_HALT; if (nDevInt1Status & 0x10) { pTAS2557->mnErrCode |= ERROR_DIE_OVERTEMP; dev_err(pTAS2557->dev, "DEVA die over temperature!\n"); } else pTAS2557->mnErrCode &= ~ERROR_DIE_OVERTEMP; if (nDevInt1Status & 0x08) { pTAS2557->mnErrCode |= ERROR_BROWNOUT; dev_err(pTAS2557->dev, "DEVA brownout!\n"); } else pTAS2557->mnErrCode &= ~ERROR_BROWNOUT; if (nDevInt1Status & 0x04) { pTAS2557->mnErrCode |= ERROR_CLK_LOST; dev_err(pTAS2557->dev, "DEVA clock lost!\n"); } else pTAS2557->mnErrCode &= ~ERROR_CLK_LOST; if (nDevInt2Status & 0x08) { pTAS2557->mnErrCode |= ERROR_CLK_DET1; dev_err(pTAS2557->dev, "DEVA clk detection 1!\n"); } else pTAS2557->mnErrCode &= ~ERROR_CLK_DET1; if (nDevInt2Status & 0x04) { pTAS2557->mnErrCode |= ERROR_CLK_DET2; dev_err(pTAS2557->dev, "DEVA clk detection 2!\n"); } else pTAS2557->mnErrCode &= ~ERROR_CLK_DET2; goto program; } else { dev_dbg(pTAS2557->dev, "IRQ Status: 0x%x, 0x%x\n", nDevInt1Status, nDevInt2Status); nCounter = 2; while (nCounter > 0) { nResult = tas2557_dev_read(pTAS2557, TAS2557_POWER_UP_FLAG_REG, &nDevPowerUpFlag); if (nResult < 0) goto program; if ((nDevPowerUpFlag & 0xc0) == 0xc0) break; nCounter--; if (nCounter > 0) { /* in case check pow status just after power on TAS2557 */ dev_dbg(pTAS2557->dev, "PowSts: 0x%x, check again after 10ms\n", nDevPowerUpFlag); msleep(10); } } if ((nDevPowerUpFlag & 0xc0) != 0xc0) { dev_err(pTAS2557->dev, "%s, Critical ERROR B[%d]_P[%d]_R[%d]= 0x%x\n", __func__, TAS2557_BOOK_ID(TAS2557_POWER_UP_FLAG_REG), TAS2557_PAGE_ID(TAS2557_POWER_UP_FLAG_REG), TAS2557_PAGE_REG(TAS2557_POWER_UP_FLAG_REG), nDevPowerUpFlag); pTAS2557->mnErrCode |= ERROR_CLASSD_PWR; goto program; } pTAS2557->mnErrCode &= ~ERROR_CLASSD_PWR; dev_dbg(pTAS2557->dev, "%s: INT1=0x%x, INT2=0x%x; PowerUpFlag=0x%x\n", __func__, nDevInt1Status, nDevInt2Status, nDevPowerUpFlag); goto end; } program: /* hardware reset and reload */ nResult = -1; tas2557_set_program(pTAS2557, pTAS2557->mnCurrentProgram, pTAS2557->mnCurrentConfiguration); end: if (nResult >= 0) { tas2557_dev_write(pTAS2557, TAS2557_GPIO4_PIN_REG, 0x07); tas2557_enableIRQ(pTAS2557, true, false); } #ifdef CONFIG_TAS2557_MISC mutex_unlock(&pTAS2557->file_lock); #endif #ifdef CONFIG_TAS2557_CODEC mutex_unlock(&pTAS2557->codec_lock); #endif } static irqreturn_t tas2557_irq_handler(int irq, void *dev_id) { struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)dev_id; tas2557_enableIRQ(pTAS2557, false, false); /* get IRQ status after 100 ms */ schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(100)); return IRQ_HANDLED; } static enum hrtimer_restart temperature_timer_func(struct hrtimer *timer) { struct tas2557_priv *pTAS2557 = container_of(timer, struct tas2557_priv, mtimer); if (pTAS2557->mbPowerUp) { schedule_work(&pTAS2557->mtimerwork); if (gpio_is_valid(pTAS2557->mnGpioINT)) { tas2557_enableIRQ(pTAS2557, false, false); schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(1)); } } return HRTIMER_NORESTART; } static void timer_work_routine(struct work_struct *work) { struct tas2557_priv *pTAS2557 = container_of(work, struct tas2557_priv, mtimerwork); int nResult, nActTemp; int nTemp = 0; struct TProgram *pProgram; static int nAvg; #ifdef CONFIG_TAS2557_CODEC mutex_lock(&pTAS2557->codec_lock); #endif #ifdef CONFIG_TAS2557_MISC mutex_lock(&pTAS2557->file_lock); #endif if (pTAS2557->mbRuntimeSuspend) { dev_info(pTAS2557->dev, "%s, Runtime Suspended\n", __func__); goto end; } if (!pTAS2557->mpFirmware->mnConfigurations) { dev_info(pTAS2557->dev, "%s, firmware not loaded\n", __func__); goto end; } pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]); if (!pTAS2557->mbPowerUp || (pProgram->mnAppMode != TAS2557_APP_TUNINGMODE)) { dev_info(pTAS2557->dev, "%s, pass, Pow=%d, program=%s\n", __func__, pTAS2557->mbPowerUp, pProgram->mpName); goto end; } nResult = tas2557_get_die_temperature(pTAS2557, &nTemp); if (nResult >= 0) { nActTemp = (int)(nTemp >> 23); dev_dbg(pTAS2557->dev, "Die=0x%x, degree=%d\n", nTemp, nActTemp); if (!pTAS2557->mnDieTvReadCounter) nAvg = 0; pTAS2557->mnDieTvReadCounter++; nAvg += nActTemp; if (!(pTAS2557->mnDieTvReadCounter % LOW_TEMPERATURE_COUNTER)) { nAvg /= LOW_TEMPERATURE_COUNTER; dev_dbg(pTAS2557->dev, "check : avg=%d\n", nAvg); if (nAvg < -6) { /* if Die temperature is below -6 degree C */ if (pTAS2557->mnDevCurrentGain != LOW_TEMPERATURE_GAIN) { nResult = tas2557_set_DAC_gain(pTAS2557, LOW_TEMPERATURE_GAIN); if (nResult < 0) goto end; pTAS2557->mnDevCurrentGain = LOW_TEMPERATURE_GAIN; dev_dbg(pTAS2557->dev, "LOW Temp: set gain to %d\n", LOW_TEMPERATURE_GAIN); } } else if (nAvg > 5) { /* if Die temperature is above 5 degree C */ if (pTAS2557->mnDevCurrentGain != pTAS2557->mnDevGain) { nResult = tas2557_set_DAC_gain(pTAS2557, pTAS2557->mnDevGain); if (nResult < 0) goto end; pTAS2557->mnDevCurrentGain = pTAS2557->mnDevGain; dev_dbg(pTAS2557->dev, "LOW Temp: set gain to original\n"); } } nAvg = 0; } if (pTAS2557->mbPowerUp) hrtimer_start(&pTAS2557->mtimer, ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); } end: #ifdef CONFIG_TAS2557_MISC mutex_unlock(&pTAS2557->file_lock); #endif #ifdef CONFIG_TAS2557_CODEC mutex_unlock(&pTAS2557->codec_lock); #endif } static int tas2557_runtime_suspend(struct tas2557_priv *pTAS2557) { dev_dbg(pTAS2557->dev, "%s\n", __func__); pTAS2557->mbRuntimeSuspend = true; if (hrtimer_active(&pTAS2557->mtimer)) { dev_dbg(pTAS2557->dev, "cancel die temp timer\n"); hrtimer_cancel(&pTAS2557->mtimer); } if (work_pending(&pTAS2557->mtimerwork)) { dev_dbg(pTAS2557->dev, "cancel timer work\n"); cancel_work_sync(&pTAS2557->mtimerwork); } if (gpio_is_valid(pTAS2557->mnGpioINT)) { if (delayed_work_pending(&pTAS2557->irq_work)) { dev_dbg(pTAS2557->dev, "cancel IRQ work\n"); cancel_delayed_work_sync(&pTAS2557->irq_work); } } return 0; } static int tas2557_runtime_resume(struct tas2557_priv *pTAS2557) { struct TProgram *pProgram; dev_dbg(pTAS2557->dev, "%s\n", __func__); if (!pTAS2557->mpFirmware->mpPrograms) { dev_dbg(pTAS2557->dev, "%s, firmware not loaded\n", __func__); goto end; } if (pTAS2557->mnCurrentProgram >= pTAS2557->mpFirmware->mnPrograms) { dev_err(pTAS2557->dev, "%s, firmware corrupted\n", __func__); goto end; } pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]); if (pTAS2557->mbPowerUp && (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE)) { if (!hrtimer_active(&pTAS2557->mtimer)) { dev_dbg(pTAS2557->dev, "%s, start Die Temp check timer\n", __func__); pTAS2557->mnDieTvReadCounter = 0; hrtimer_start(&pTAS2557->mtimer, ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); } } pTAS2557->mbRuntimeSuspend = false; end: return 0; } static bool tas2557_volatile(struct device *pDev, unsigned int nRegister) { return true; } static bool tas2557_writeable(struct device *pDev, unsigned int nRegister) { return true; } static const struct regmap_config tas2557_i2c_regmap = { .reg_bits = 8, .val_bits = 8, .writeable_reg = tas2557_writeable, .volatile_reg = tas2557_volatile, .cache_type = REGCACHE_NONE, .max_register = 128, }; /* tas2557_i2c_probe : * platform dependent * should implement hardware reset functionality */ static int tas2557_i2c_probe(struct i2c_client *pClient, const struct i2c_device_id *pID) { struct tas2557_priv *pTAS2557; int nResult = 0; unsigned int nValue = 0; const char *pFWName; dev_info(&pClient->dev, "%s enter\n", __func__); pTAS2557 = devm_kzalloc(&pClient->dev, sizeof(struct tas2557_priv), GFP_KERNEL); if (!pTAS2557) { nResult = -ENOMEM; goto err; } pTAS2557->dev = &pClient->dev; i2c_set_clientdata(pClient, pTAS2557); dev_set_drvdata(&pClient->dev, pTAS2557); pTAS2557->mpRegmap = devm_regmap_init_i2c(pClient, &tas2557_i2c_regmap); if (IS_ERR(pTAS2557->mpRegmap)) { nResult = PTR_ERR(pTAS2557->mpRegmap); dev_err(&pClient->dev, "Failed to allocate register map: %d\n", nResult); goto err; } if (pClient->dev.of_node) tas2557_parse_dt(&pClient->dev, pTAS2557); if (gpio_is_valid(pTAS2557->mnResetGPIO)) { nResult = gpio_request(pTAS2557->mnResetGPIO, "TAS2557-RESET"); if (nResult < 0) { dev_err(pTAS2557->dev, "%s: GPIO %d request error\n", __func__, pTAS2557->mnResetGPIO); goto err; } tas2557_hw_reset(pTAS2557); } pTAS2557->read = tas2557_dev_read; pTAS2557->write = tas2557_dev_write; pTAS2557->bulk_read = tas2557_dev_bulk_read; pTAS2557->bulk_write = tas2557_dev_bulk_write; pTAS2557->update_bits = tas2557_dev_update_bits; pTAS2557->enableIRQ = tas2557_enableIRQ; pTAS2557->clearIRQ = tas2557_clearIRQ; pTAS2557->set_config = tas2557_set_config; pTAS2557->set_calibration = tas2557_set_calibration; pTAS2557->hw_reset = tas2557_hw_reset; pTAS2557->runtime_suspend = tas2557_runtime_suspend; pTAS2557->runtime_resume = tas2557_runtime_resume; pTAS2557->mnRestart = 0; pTAS2557->mnEdge = 4; mutex_init(&pTAS2557->dev_lock); /* Reset the chip */ nResult = tas2557_dev_write(pTAS2557, TAS2557_SW_RESET_REG, 0x01); if (nResult < 0) { dev_err(&pClient->dev, "I2c fail, %d\n", nResult); goto err; } msleep(1); tas2557_dev_read(pTAS2557, TAS2557_REV_PGID_REG, &nValue); pTAS2557->mnPGID = nValue; if (pTAS2557->mnPGID == TAS2557_PG_VERSION_2P1) { dev_info(pTAS2557->dev, "PG2.1 Silicon found\n"); pFWName = TAS2557_FW_NAME; } else if (pTAS2557->mnPGID == TAS2557_PG_VERSION_1P0) { dev_info(pTAS2557->dev, "PG1.0 Silicon found\n"); pFWName = TAS2557_PG1P0_FW_NAME; } else { nResult = -ENOTSUPP; dev_info(pTAS2557->dev, "unsupport Silicon 0x%x\n", pTAS2557->mnPGID); goto err; } if (gpio_is_valid(pTAS2557->mnGpioINT)) { nResult = gpio_request(pTAS2557->mnGpioINT, "TAS2557-IRQ"); if (nResult < 0) { dev_err(pTAS2557->dev, "%s: GPIO %d request INT error\n", __func__, pTAS2557->mnGpioINT); goto err; } gpio_direction_input(pTAS2557->mnGpioINT); pTAS2557->mnIRQ = gpio_to_irq(pTAS2557->mnGpioINT); dev_dbg(pTAS2557->dev, "irq = %d\n", pTAS2557->mnIRQ); INIT_DELAYED_WORK(&pTAS2557->irq_work, irq_work_routine); nResult = request_threaded_irq(pTAS2557->mnIRQ, tas2557_irq_handler, NULL, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, pClient->name, pTAS2557); if (nResult < 0) { dev_err(pTAS2557->dev, "request_irq failed, %d\n", nResult); goto err; } disable_irq_nosync(pTAS2557->mnIRQ); } pTAS2557->mpFirmware = devm_kzalloc(&pClient->dev, sizeof(struct TFirmware), GFP_KERNEL); if (!pTAS2557->mpFirmware) { nResult = -ENOMEM; goto err; } pTAS2557->mpCalFirmware = devm_kzalloc(&pClient->dev, sizeof(struct TFirmware), GFP_KERNEL); if (!pTAS2557->mpCalFirmware) { nResult = -ENOMEM; goto err; } #ifdef CONFIG_TAS2557_CODEC mutex_init(&pTAS2557->codec_lock); tas2557_register_codec(pTAS2557); #endif #ifdef CONFIG_TAS2557_MISC mutex_init(&pTAS2557->file_lock); tas2557_register_misc(pTAS2557); #endif #ifdef ENABLE_TILOAD tiload_driver_init(pTAS2557); #endif hrtimer_init(&pTAS2557->mtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); pTAS2557->mtimer.function = temperature_timer_func; INIT_WORK(&pTAS2557->mtimerwork, timer_work_routine); nResult = request_firmware_nowait(THIS_MODULE, 1, pFWName, pTAS2557->dev, GFP_KERNEL, pTAS2557, tas2557_fw_ready); err: return nResult; } static int tas2557_i2c_remove(struct i2c_client *pClient) { struct tas2557_priv *pTAS2557 = i2c_get_clientdata(pClient); dev_info(pTAS2557->dev, "%s\n", __func__); #ifdef CONFIG_TAS2557_CODEC tas2557_deregister_codec(pTAS2557); mutex_destroy(&pTAS2557->codec_lock); #endif #ifdef CONFIG_TAS2557_MISC tas2557_deregister_misc(pTAS2557); mutex_destroy(&pTAS2557->file_lock); #endif mutex_destroy(&pTAS2557->dev_lock); return 0; } static const struct i2c_device_id tas2557_i2c_id[] = { {"tas2557", 0}, {} }; MODULE_DEVICE_TABLE(i2c, tas2557_i2c_id); #if defined(CONFIG_OF) static const struct of_device_id tas2557_of_match[] = { {.compatible = "ti,tas2557"}, {}, }; MODULE_DEVICE_TABLE(of, tas2557_of_match); #endif static struct i2c_driver tas2557_i2c_driver = { .driver = { .name = "tas2557", .owner = THIS_MODULE, #if defined(CONFIG_OF) .of_match_table = of_match_ptr(tas2557_of_match), #endif }, .probe = tas2557_i2c_probe, .remove = tas2557_i2c_remove, .id_table = tas2557_i2c_id, }; module_i2c_driver(tas2557_i2c_driver); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("TAS2557 I2C Smart Amplifier driver"); MODULE_LICENSE("GPL v2"); #endif