/* * ALSA SoC Texas Instruments TAS2562 High Performance 4W Smart Amplifier * * Copyright (C) 2016 Texas Instruments, Inc. * * Author: saiprasad * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * */ #ifdef CONFIG_TAS2562_REGMAP #define DEBUG 5 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tas2562.h" #include "tas2562-codec.h" #include "tas2562-misc.h" #ifdef CONFIG_TAS25XX_ALGO #include #endif /*CONFIG_TAS25XX_ALGO*/ static char p_icn_threshold[] = {0x00, 0x01, 0x2f, 0x2c}; static char p_icn_hysteresis[] = {0x00, 0x01, 0x5d, 0xc0}; static int tas2562_regmap_write(struct tas2562_priv *p_tas2562, unsigned int reg, unsigned int value) { int nResult = 0; int retry_count = TAS2562_I2C_RETRY_COUNT; if(p_tas2562->i2c_suspend) return ERROR_I2C_SUSPEND; while(retry_count--) { nResult = regmap_write(p_tas2562->regmap, reg, value); if (nResult >= 0) break; msleep(20); } if(retry_count == -1) return ERROR_I2C_FAILED; else return 0; } static int tas2562_regmap_bulk_write(struct tas2562_priv *p_tas2562, unsigned int reg, unsigned char *pData, unsigned int nLength) { int nResult = 0; int retry_count = TAS2562_I2C_RETRY_COUNT; if(p_tas2562->i2c_suspend) return ERROR_I2C_SUSPEND; while(retry_count --) { nResult = regmap_bulk_write(p_tas2562->regmap, reg, pData, nLength); if (nResult >= 0) break; msleep(20); } if(retry_count == -1) return ERROR_I2C_FAILED; else return 0; } static int tas2562_regmap_read(struct tas2562_priv *p_tas2562, unsigned int reg, unsigned int *value) { int nResult = 0; int retry_count = TAS2562_I2C_RETRY_COUNT; if(p_tas2562->i2c_suspend) return ERROR_I2C_SUSPEND; while(retry_count --) { nResult = regmap_read(p_tas2562->regmap, reg, value); if (nResult >= 0) break; msleep(20); } if(retry_count == -1) return ERROR_I2C_FAILED; else return 0; } static int tas2562_regmap_bulk_read(struct tas2562_priv *p_tas2562, unsigned int reg, unsigned char *pData, unsigned int nLength) { int nResult = 0; int retry_count = TAS2562_I2C_RETRY_COUNT; if(p_tas2562->i2c_suspend) return ERROR_I2C_SUSPEND; while(retry_count --) { nResult = regmap_bulk_read(p_tas2562->regmap, reg, pData, nLength); if (nResult >= 0) break; msleep(20); } if(retry_count == -1) return ERROR_I2C_FAILED; else return 0; } static int tas2562_regmap_update_bits(struct tas2562_priv *p_tas2562, unsigned int reg, unsigned int mask, unsigned int value) { int nResult = 0; int retry_count = TAS2562_I2C_RETRY_COUNT; if(p_tas2562->i2c_suspend) return ERROR_I2C_SUSPEND; while(retry_count--) { nResult = regmap_update_bits(p_tas2562->regmap, reg, mask, value); if (nResult >= 0) break; msleep(20); } if(retry_count == -1) return ERROR_I2C_FAILED; else return 0; } static int tas2562_change_book_page(struct tas2562_priv *p_tas2562, enum channel chn, int book, int page) { int n_result = 0; if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { p_tas2562->client->addr = p_tas2562->mn_l_addr; if (p_tas2562->mn_l_current_book != book) { n_result = tas2562_regmap_write(p_tas2562, TAS2562_BOOKCTL_PAGE, 0); if (n_result < 0) { dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); goto end; } p_tas2562->mn_l_current_page = 0; n_result = tas2562_regmap_write(p_tas2562, TAS2562_BOOKCTL_REG, book); if (n_result < 0) { dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); goto end; } p_tas2562->mn_l_current_book = book; } if (p_tas2562->mn_l_current_page != page) { n_result = tas2562_regmap_write(p_tas2562, TAS2562_BOOKCTL_PAGE, page); if (n_result < 0) { dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); goto end; } p_tas2562->mn_l_current_page = page; } } if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { p_tas2562->client->addr = p_tas2562->mn_r_addr; if (p_tas2562->mn_r_current_book != book) { n_result = tas2562_regmap_write(p_tas2562, TAS2562_BOOKCTL_PAGE, 0); if (n_result < 0) { dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); goto end; } p_tas2562->mn_r_current_page = 0; n_result = tas2562_regmap_write(p_tas2562, TAS2562_BOOKCTL_REG, book); if (n_result < 0) { dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); goto end; } p_tas2562->mn_r_current_book = book; } if (p_tas2562->mn_r_current_page != page) { n_result = tas2562_regmap_write(p_tas2562, TAS2562_BOOKCTL_PAGE, page); if (n_result < 0) { dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); goto end; } p_tas2562->mn_r_current_page = page; } } end: return n_result; } static int tas2562_dev_read(struct tas2562_priv *p_tas2562, enum channel chn, unsigned int reg, unsigned int *pValue) { int n_result = 0; mutex_lock(&p_tas2562->dev_lock); n_result = tas2562_change_book_page(p_tas2562, chn, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); if (n_result < 0) goto end; if ((chn == channel_left) || (p_tas2562->mn_channels == 1)) p_tas2562->client->addr = p_tas2562->mn_l_addr; else if (chn == channel_right) p_tas2562->client->addr = p_tas2562->mn_r_addr; else dev_err(p_tas2562->dev, "%s, wrong channel number\n", __func__); n_result = tas2562_regmap_read(p_tas2562, TAS2562_PAGE_REG(reg), pValue); if (n_result < 0) dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); else dev_dbg(p_tas2562->dev, "%s: chn:%x:BOOK:PAGE:REG %u:%u:%u,0x%x\n", __func__, p_tas2562->client->addr, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), *pValue); end: mutex_unlock(&p_tas2562->dev_lock); return n_result; } static int tas2562_dev_write(struct tas2562_priv *p_tas2562, enum channel chn, unsigned int reg, unsigned int value) { int n_result = 0; mutex_lock(&p_tas2562->dev_lock); n_result = tas2562_change_book_page(p_tas2562, chn, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); if (n_result < 0) goto end; if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { p_tas2562->client->addr = p_tas2562->mn_l_addr; n_result = tas2562_regmap_write(p_tas2562, TAS2562_PAGE_REG(reg), value); if (n_result < 0) dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); else dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n", __func__, p_tas2562->client->addr, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), value); } if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { p_tas2562->client->addr = p_tas2562->mn_r_addr; n_result = tas2562_regmap_write(p_tas2562, TAS2562_PAGE_REG(reg), value); if (n_result < 0) dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); else dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n", __func__, p_tas2562->client->addr, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), value); } end: mutex_unlock(&p_tas2562->dev_lock); return n_result; } static int tas2562_dev_bulk_write(struct tas2562_priv *p_tas2562, enum channel chn, unsigned int reg, unsigned char *p_data, unsigned int n_length) { int n_result = 0; mutex_lock(&p_tas2562->dev_lock); n_result = tas2562_change_book_page(p_tas2562, chn, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); if (n_result < 0) goto end; if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { p_tas2562->client->addr = p_tas2562->mn_l_addr; n_result = tas2562_regmap_bulk_write(p_tas2562, TAS2562_PAGE_REG(reg), p_data, n_length); if (n_result < 0) dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); else dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", __func__, p_tas2562->client->addr, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), n_length); } if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { p_tas2562->client->addr = p_tas2562->mn_r_addr; n_result = tas2562_regmap_bulk_write(p_tas2562, TAS2562_PAGE_REG(reg), p_data, n_length); if (n_result < 0) dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); else dev_dbg(p_tas2562->dev, "%s: %x:BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", __func__, p_tas2562->client->addr, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), n_length); } end: mutex_unlock(&p_tas2562->dev_lock); return n_result; } static int tas2562_dev_bulk_read(struct tas2562_priv *p_tas2562, enum channel chn, unsigned int reg, unsigned char *p_data, unsigned int n_length) { int n_result = 0; mutex_lock(&p_tas2562->dev_lock); if ((chn == channel_left) || (p_tas2562->mn_channels == 1)) p_tas2562->client->addr = p_tas2562->mn_l_addr; else if (chn == channel_right) p_tas2562->client->addr = p_tas2562->mn_r_addr; else dev_err(p_tas2562->dev, "%s, wrong channel number\n", __func__); n_result = tas2562_change_book_page(p_tas2562, chn, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); if (n_result < 0) goto end; n_result = tas2562_regmap_bulk_read(p_tas2562, TAS2562_PAGE_REG(reg), p_data, n_length); if (n_result < 0) dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); else dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", __func__, p_tas2562->client->addr, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), n_length); end: mutex_unlock(&p_tas2562->dev_lock); return n_result; } static int tas2562_dev_update_bits(struct tas2562_priv *p_tas2562, enum channel chn, unsigned int reg, unsigned int mask, unsigned int value) { int n_result = 0; mutex_lock(&p_tas2562->dev_lock); n_result = tas2562_change_book_page(p_tas2562, chn, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); if (n_result < 0) goto end; if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { p_tas2562->client->addr = p_tas2562->mn_l_addr; n_result = tas2562_regmap_update_bits(p_tas2562, TAS2562_PAGE_REG(reg), mask, value); if (n_result < 0) dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); else dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, mask: 0x%x, val: 0x%x\n", __func__, p_tas2562->client->addr, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), mask, value); } if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { p_tas2562->client->addr = p_tas2562->mn_r_addr; n_result = tas2562_regmap_update_bits(p_tas2562, TAS2562_PAGE_REG(reg), mask, value); if (n_result < 0) dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, n_result); else dev_dbg(p_tas2562->dev, "%s:chn%x:BOOK:PAGE:REG %u:%u:%u,mask: 0x%x, val: 0x%x\n", __func__, p_tas2562->client->addr, TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), TAS2562_PAGE_REG(reg), mask, value); } end: mutex_unlock(&p_tas2562->dev_lock); return n_result; } static bool tas2562_volatile(struct device *dev, unsigned int reg) { return true; } static bool tas2562_writeable(struct device *dev, unsigned int reg) { return true; } static const struct regmap_config tas2562_i2c_regmap = { .reg_bits = 8, .val_bits = 8, .writeable_reg = tas2562_writeable, .volatile_reg = tas2562_volatile, .cache_type = REGCACHE_NONE, .max_register = 1 * 128, }; static void tas2562_hw_reset(struct tas2562_priv *p_tas2562) { if (gpio_is_valid(p_tas2562->mn_reset_gpio)) { gpio_direction_output(p_tas2562->mn_reset_gpio, 0); if(p_tas2562->mn_channels != 1) { dev_dbg(p_tas2562->dev, "Reset gpio: not mono case, resetting second gpio"); if(gpio_is_valid(p_tas2562->mn_reset_gpio2)) gpio_direction_output(p_tas2562->mn_reset_gpio2, 0); } else { dev_dbg(p_tas2562->dev, "Reset gpio: mono case, not resetting second gpio"); } msleep(20); gpio_direction_output(p_tas2562->mn_reset_gpio, 1); if(p_tas2562->mn_channels != 1) { dev_dbg(p_tas2562->dev, "Reset gpio: not mono case, resetting second gpio"); if(gpio_is_valid(p_tas2562->mn_reset_gpio2)) gpio_direction_output(p_tas2562->mn_reset_gpio2, 1); } else { dev_dbg(p_tas2562->dev, "Reset gpio: mono case, not resetting second gpio"); } msleep(20); } dev_info(p_tas2562->dev, "reset gpio up !!\n"); p_tas2562->mn_l_current_book = -1; p_tas2562->mn_l_current_page = -1; p_tas2562->mn_r_current_book = -1; p_tas2562->mn_r_current_page = -1; } void tas2562_enable_irq(struct tas2562_priv *p_tas2562, bool enable) { static int irq1_enabled = 0; static int irq2_enabled = 0; struct irq_desc *desc = NULL; if (enable) { if (p_tas2562->mb_irq_eable) return; if (gpio_is_valid(p_tas2562->mn_irq_gpio) && irq1_enabled == 0) { desc = irq_to_desc(p_tas2562->mn_irq); if (desc && desc->depth > 0) { enable_irq(p_tas2562->mn_irq); } else { dev_info (p_tas2562->dev, "### irq already enabled"); } irq1_enabled = 1; } if (p_tas2562->mn_channels == 2) { if (gpio_is_valid(p_tas2562->mn_irq_gpio2) && irq2_enabled == 0) { enable_irq(p_tas2562->mn_irq2); irq2_enabled = 1; } } p_tas2562->mb_irq_eable = true; } else { if (gpio_is_valid(p_tas2562->mn_irq_gpio) && irq1_enabled == 1) { disable_irq_nosync(p_tas2562->mn_irq); irq1_enabled = 0; } if (p_tas2562->mn_channels == 2) { if (gpio_is_valid(p_tas2562->mn_irq_gpio2) && irq2_enabled == 1) { disable_irq_nosync(p_tas2562->mn_irq2); irq2_enabled = 0; } } p_tas2562->mb_irq_eable = false; } } static void irq_work_routine(struct work_struct *work) { struct tas2562_priv *p_tas2562 = container_of(work, struct tas2562_priv, irq_work.work); unsigned int nDevInt1Status = 0, nDevInt2Status = 0, nDevInt3Status = 0, nDevInt4Status = 0; int n_counter = 2; int n_result = 0; int irqreg; enum channel chn; dev_info(p_tas2562->dev, "%s\n", __func__); #ifdef CONFIG_TAS2562_CODEC mutex_lock(&p_tas2562->codec_lock); #endif tas2562_enable_irq(p_tas2562, false); if (p_tas2562->mb_runtime_suspend) { dev_info(p_tas2562->dev, "%s, Runtime Suspended\n", __func__); goto end; } if (p_tas2562->mn_power_state == TAS2562_POWER_SHUTDOWN) { dev_info(p_tas2562->dev, "%s, device not powered\n", __func__); goto end; } n_result = p_tas2562->write(p_tas2562, channel_both, TAS2562_INTERRUPTMASKREG0, TAS2562_INTERRUPTMASKREG0_DISABLE); n_result = p_tas2562->write(p_tas2562, channel_both, TAS2562_INTERRUPTMASKREG1, TAS2562_INTERRUPTMASKREG1_DISABLE); if (n_result < 0) goto reload; if ((p_tas2562->spk_l_control == 1) && (p_tas2562->spk_r_control == 1) && (p_tas2562->mn_channels == 2)) chn = channel_both; else if (p_tas2562->spk_l_control == 1) chn = channel_left; else if ((p_tas2562->spk_r_control == 1) && (p_tas2562->mn_channels == 2)) chn = channel_right; else chn = channel_left; if (chn & channel_left) n_result = p_tas2562->read(p_tas2562, channel_left, TAS2562_LATCHEDINTERRUPTREG0, &nDevInt1Status); if (n_result >= 0) n_result = p_tas2562->read(p_tas2562, channel_left, TAS2562_LATCHEDINTERRUPTREG1, &nDevInt2Status); else goto reload; if (chn & channel_right) n_result = p_tas2562->read(p_tas2562, channel_right, TAS2562_LATCHEDINTERRUPTREG0, &nDevInt3Status); if (n_result >= 0) n_result = p_tas2562->read(p_tas2562, channel_right, TAS2562_LATCHEDINTERRUPTREG1, &nDevInt4Status); else goto reload; dev_dbg(p_tas2562->dev, "IRQ status : 0x%x, 0x%x, 0x%x, 0x%x\n", nDevInt3Status, nDevInt4Status, nDevInt3Status, nDevInt4Status); if (((nDevInt1Status & 0x7) != 0) || ((nDevInt2Status & 0x0f) != 0) || ((nDevInt3Status & 0x7) != 0) || ((nDevInt4Status & 0x0f) != 0)) { /* in case of INT_CLK, INT_OC, INT_OT, * INT_OVLT, INT_UVLT, INT_BO */ if ((nDevInt1Status & TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT) || (nDevInt3Status & TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT)) { p_tas2562->mn_err_code |= ERROR_CLOCK; dev_err(p_tas2562->dev, "TDM clock error!\n"); } else p_tas2562->mn_err_code &= ~ERROR_OVER_CURRENT; if ((nDevInt1Status & TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT) || (nDevInt3Status & TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT)) { p_tas2562->mn_err_code |= ERROR_OVER_CURRENT; dev_err(p_tas2562->dev, "SPK over current!\n"); } else p_tas2562->mn_err_code &= ~ERROR_OVER_CURRENT; if ((nDevInt1Status & TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT) || (nDevInt3Status & TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT)) { p_tas2562->mn_err_code |= ERROR_DIE_OVERTEMP; dev_err(p_tas2562->dev, "die over temperature!\n"); } else p_tas2562->mn_err_code &= ~ERROR_DIE_OVERTEMP; if ((nDevInt2Status & TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT) || (nDevInt4Status & TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT)) { p_tas2562->mn_err_code |= ERROR_OVER_VOLTAGE; dev_err(p_tas2562->dev, "SPK over voltage!\n"); } else p_tas2562->mn_err_code &= ~ERROR_UNDER_VOLTAGE; if ((nDevInt2Status & TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT) || (nDevInt4Status & TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT)) { p_tas2562->mn_err_code |= ERROR_UNDER_VOLTAGE; dev_err(p_tas2562->dev, "SPK under voltage!\n"); } else p_tas2562->mn_err_code &= ~ERROR_UNDER_VOLTAGE; if ((nDevInt2Status & TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT) || (nDevInt4Status & TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT)) { p_tas2562->mn_err_code |= ERROR_BROWNOUT; dev_err(p_tas2562->dev, "brownout!\n"); } else p_tas2562->mn_err_code &= ~ERROR_BROWNOUT; goto reload; } else { n_counter = 2; while (n_counter > 0) { if (chn & channel_left) n_result = p_tas2562->read(p_tas2562, channel_left, TAS2562_POWERCONTROL, &nDevInt1Status); if (n_result < 0) goto reload; if (chn & channel_right) n_result = p_tas2562->read(p_tas2562, channel_right, TAS2562_POWERCONTROL, &nDevInt3Status); if (n_result < 0) goto reload; if ((nDevInt1Status & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) != TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) { /* If only left should be power on */ if (chn == channel_left) break; /* If both should be power on */ if ((nDevInt3Status & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) != TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) break; } /*If only right should be power on */ else if (chn == channel_right) { if ((nDevInt3Status & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) != TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) break; } p_tas2562->read(p_tas2562, channel_left, TAS2562_LATCHEDINTERRUPTREG0, &irqreg); dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", __func__, irqreg, __LINE__); p_tas2562->read(p_tas2562, channel_right, TAS2562_LATCHEDINTERRUPTREG0, &irqreg); dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", __func__, irqreg, __LINE__); if (p_tas2562->dac_mute == 0) { n_result = p_tas2562->update_bits(p_tas2562, chn, TAS2562_POWERCONTROL, TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK, TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE); if (n_result < 0) goto reload; } else { n_result = p_tas2562->update_bits(p_tas2562, chn, TAS2562_POWERCONTROL, TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK, TAS2562_POWERCONTROL_OPERATIONALMODE10_MUTE); if (n_result < 0) goto reload; } dev_info(p_tas2562->dev, "set ICN to -80dB\n"); n_result = p_tas2562->bulk_write(p_tas2562, chn, TAS2562_ICN_THRESHOLD_REG, p_icn_threshold, sizeof(p_icn_threshold)); n_result = p_tas2562->bulk_write(p_tas2562, chn, TAS2562_ICN_HYSTERESIS_REG, p_icn_hysteresis, sizeof(p_icn_hysteresis)); p_tas2562->read(p_tas2562, channel_left, TAS2562_LATCHEDINTERRUPTREG0, &irqreg); dev_info(p_tas2562->dev, "IRQ reg is: %s, %d, %d\n", __func__, irqreg, __LINE__); p_tas2562->read(p_tas2562, channel_right, TAS2562_LATCHEDINTERRUPTREG0, &irqreg); dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", __func__, irqreg, __LINE__); n_counter--; if (n_counter > 0) { /* in case check pow status just after power on TAS2562 */ dev_dbg(p_tas2562->dev, "PowSts B: 0x%x, check again after 10ms\n", nDevInt1Status); msleep(20); } } if ((((nDevInt1Status & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) == TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) && (chn & channel_left)) || (((nDevInt3Status & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) == TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) && (chn & channel_right))) { dev_err(p_tas2562->dev, "%s, Critical ERROR REG[0x%x] = 0x%x\n", __func__, TAS2562_POWERCONTROL, nDevInt1Status); p_tas2562->mn_err_code |= ERROR_CLASSD_PWR; goto reload; } p_tas2562->mn_err_code &= ~ERROR_CLASSD_PWR; } n_result = p_tas2562->write(p_tas2562, channel_left, TAS2562_INTERRUPTMASKREG0, 0xf8); if (n_result < 0) goto reload; n_result = p_tas2562->write(p_tas2562, channel_left, TAS2562_INTERRUPTMASKREG1, 0xb1); if (n_result < 0) goto reload; goto end; reload: /* hardware reset and reload */ tas2562_load_config(p_tas2562); end: tas2562_enable_irq(p_tas2562, true); if (p_tas2562->mn_status_check) { if (!hrtimer_active(&p_tas2562->mtimer)) { u64 check_period = p_tas2562->mn_status_period * 1000; dev_dbg(p_tas2562->dev, "%s, start timer\n", __func__); hrtimer_start(&p_tas2562->mtimer, ns_to_ktime((u64)check_period * NSEC_PER_MSEC), HRTIMER_MODE_REL); } } #ifdef CONFIG_TAS2562_CODEC mutex_unlock(&p_tas2562->codec_lock); #endif } static void init_work_routine(struct work_struct *work) { struct tas2562_priv *p_tas2562 = container_of(work, struct tas2562_priv, init_work.work); int nResult = 0; //int irqreg; //dev_info(p_tas2562->dev, "%s\n", __func__); #ifdef CONFIG_TAS2562_CODEC mutex_lock(&p_tas2562->codec_lock); #endif p_tas2562->update_bits(p_tas2562, channel_both, TAS2562_POWERCONTROL, TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK, TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE); //dev_info(p_tas2562->dev, "set ICN to -80dB\n"); p_tas2562->bulk_write(p_tas2562, channel_both, TAS2562_ICN_THRESHOLD_REG, p_icn_threshold, sizeof(p_icn_threshold)); p_tas2562->bulk_write(p_tas2562, channel_both, TAS2562_ICN_HYSTERESIS_REG, p_icn_hysteresis, sizeof(p_icn_hysteresis)); nResult = gpio_get_value(p_tas2562->mn_irq_gpio); //dev_info(p_tas2562->dev, "%s, irq GPIO state: %d\n", __func__, nResult); #ifdef CONFIG_TAS2562_CODEC mutex_unlock(&p_tas2562->codec_lock); #endif } static irqreturn_t tas2562_irq_handler(int irq, void *dev_id) { struct tas2562_priv *p_tas2562 = (struct tas2562_priv *)dev_id; /* get IRQ status after 100 ms */ schedule_delayed_work(&p_tas2562->irq_work, msecs_to_jiffies(100)); return IRQ_HANDLED; } static int tas2562_runtime_suspend(struct tas2562_priv *p_tas2562) { dev_dbg(p_tas2562->dev, "%s\n", __func__); p_tas2562->mb_runtime_suspend = true; if (p_tas2562->mn_status_check) { if (hrtimer_active(&p_tas2562->mtimer)) { dev_dbg(p_tas2562->dev, "cancel timer\n"); hrtimer_cancel(&p_tas2562->mtimer); } if (delayed_work_pending(&p_tas2562->status_work)) { dev_dbg(p_tas2562->dev, "cancel status work\n"); cancel_delayed_work_sync(&p_tas2562->status_work); } } if (delayed_work_pending(&p_tas2562->irq_work)) { dev_dbg(p_tas2562->dev, "cancel IRQ work\n"); cancel_delayed_work_sync(&p_tas2562->irq_work); } return 0; } static int tas2562_runtime_resume(struct tas2562_priv *p_tas2562) { dev_dbg(p_tas2562->dev, "%s\n", __func__); if (p_tas2562->mn_status_check) { if (p_tas2562->mb_power_up) { u64 check_period = p_tas2562->mn_status_period * 1000; if (!hrtimer_active(&p_tas2562->mtimer)) { dev_dbg(p_tas2562->dev, "%s, start check timer\n", __func__); hrtimer_start(&p_tas2562->mtimer, ns_to_ktime((u64)check_period * NSEC_PER_MSEC), HRTIMER_MODE_REL); } } } p_tas2562->mb_runtime_suspend = false; return 0; } static int tas2562_pm_suspend(struct device *dev) { struct tas2562_priv *p_tas2562 = dev_get_drvdata(dev); if(!p_tas2562){ dev_err(p_tas2562->dev, "drvdata is NULL\n"); return -EINVAL; } mutex_lock(&p_tas2562->codec_lock); tas2562_runtime_suspend(p_tas2562); mutex_unlock(&p_tas2562->codec_lock); return 0; } static int tas2562_pm_resume(struct device *dev) { struct tas2562_priv *p_tas2562 = dev_get_drvdata(dev); if(!p_tas2562){ dev_err(p_tas2562->dev, "drvdata is NULL\n"); return -EINVAL; } mutex_lock(&p_tas2562->codec_lock); tas2562_runtime_resume(p_tas2562); mutex_unlock(&p_tas2562->codec_lock); return 0; } /* Added for Mute Issue */ static void status_work_routine(struct work_struct *work) { struct tas2562_priv *p_tas2562 = container_of(work, struct tas2562_priv, status_work.work); unsigned int nDevInt0Status = 0, nDevInt1Status = 0, nDevInt2Status = 0, nDevInt3Status = 0, nDevInt4Status = 0, nDevInt5Status = 0; int n_result = 0; enum channel chn; #ifdef CONFIG_TAS2562_CODEC mutex_lock(&p_tas2562->codec_lock); #endif if (p_tas2562->mb_runtime_suspend) { dev_info(p_tas2562->dev, "%s, Runtime Suspended\n", __func__); goto end; } if (p_tas2562->mn_power_state == TAS2562_POWER_SHUTDOWN) { dev_info(p_tas2562->dev, "%s, device not powered\n", __func__); goto end; } /* Read latched interrupt status registers */ if ((p_tas2562->spk_l_control == 1) && (p_tas2562->spk_r_control == 1) && (p_tas2562->mn_channels == 2)) chn = channel_both; else if (p_tas2562->spk_l_control == 1) chn = channel_left; else if ((p_tas2562->spk_r_control == 1) && (p_tas2562->mn_channels == 2)) chn = channel_right; else chn = channel_left; if (chn & channel_left) { n_result = p_tas2562->read(p_tas2562, channel_left, TAS2562_LATCHEDINTERRUPTREG0, &nDevInt0Status); n_result = p_tas2562->read(p_tas2562, channel_left, TAS2562_LATCHEDINTERRUPTREG1, &nDevInt1Status); n_result = p_tas2562->read(p_tas2562, channel_left, TAS2562_LATCHEDINTERRUPTREG2, &nDevInt2Status); n_result = p_tas2562->read(p_tas2562, channel_left, TAS2562_LATCHEDINTERRUPTREG3, &nDevInt3Status); n_result = p_tas2562->read(p_tas2562, channel_left, TAS2562_LATCHEDINTERRUPTREG4, &nDevInt4Status); n_result = p_tas2562->read(p_tas2562, channel_left, TAS2562_LATCHEDINTERRUPTREG5, &nDevInt5Status); dev_info(p_tas2562->dev, "%s: channel_left : 0x%x, 0x%x, 0x%x, 0x%x 0x%x 0x%x\n", __func__, nDevInt0Status, nDevInt1Status, nDevInt2Status, nDevInt3Status, nDevInt4Status, nDevInt5Status); } if (chn & channel_right) { /* Clear all values */ nDevInt0Status = 0; nDevInt1Status = 0; nDevInt2Status = 0; nDevInt3Status = 0; nDevInt4Status = 0; nDevInt5Status = 0; n_result = p_tas2562->read(p_tas2562, channel_right, TAS2562_LATCHEDINTERRUPTREG0, &nDevInt0Status); n_result = p_tas2562->read(p_tas2562, channel_right, TAS2562_LATCHEDINTERRUPTREG1, &nDevInt1Status); n_result = p_tas2562->read(p_tas2562, channel_right, TAS2562_LATCHEDINTERRUPTREG2, &nDevInt2Status); n_result = p_tas2562->read(p_tas2562, channel_right, TAS2562_LATCHEDINTERRUPTREG3, &nDevInt3Status); n_result = p_tas2562->read(p_tas2562, channel_right, TAS2562_LATCHEDINTERRUPTREG4, &nDevInt4Status); n_result = p_tas2562->read(p_tas2562, channel_right, TAS2562_LATCHEDINTERRUPTREG5, &nDevInt5Status); dev_info(p_tas2562->dev, "%s: channel_right : 0x%x, 0x%x, 0x%x, 0x%x 0x%x 0x%x\n", __func__, nDevInt0Status, nDevInt1Status, nDevInt2Status, nDevInt3Status, nDevInt4Status, nDevInt5Status); } /* Read Status register */ /* Clear register */ nDevInt0Status = 0; if (chn & channel_left) { n_result = p_tas2562->read(p_tas2562, channel_left, TAS2562_INTSTATUS, &nDevInt0Status); if (n_result < 0) goto reload; if (((nDevInt0Status & TAS2562_INTSTATUS_CLASSD) == 0) || ((nDevInt0Status & TAS2562_INTSTATUS_DAC) == 0)) { dev_err(p_tas2562->dev, "%s: Device ERROR!", __func__); dev_err(p_tas2562->dev, "%s: channel_left : Status : 0x%x\n", __func__, nDevInt0Status); goto reload; } } nDevInt0Status = 0; if (chn & channel_right) { n_result = p_tas2562->read(p_tas2562, channel_right, TAS2562_INTSTATUS, &nDevInt0Status); if (n_result < 0) goto reload; if (((nDevInt0Status & TAS2562_INTSTATUS_CLASSD) == 0) || ((nDevInt0Status & TAS2562_INTSTATUS_DAC) == 0)) { dev_info(p_tas2562->dev, "%s: channel_right : Status : 0x%x\n", __func__, nDevInt0Status); goto reload; } } if (!hrtimer_active(&p_tas2562->mtimer)) { u64 check_period = p_tas2562->mn_status_period * 1000; dev_dbg(p_tas2562->dev, "%s, start timer\n", __func__); hrtimer_start(&p_tas2562->mtimer, ns_to_ktime((u64)check_period * NSEC_PER_MSEC), HRTIMER_MODE_REL); } goto end; reload: /* hardware reset and reload */ tas2562_reset_reload(p_tas2562); end: #ifdef CONFIG_TAS2562_CODEC mutex_unlock(&p_tas2562->codec_lock); #endif } static enum hrtimer_restart timer_func(struct hrtimer *timer) { struct tas2562_priv *p_tas2562 = container_of(timer, struct tas2562_priv, mtimer); if (p_tas2562->mb_power_up) { if (!delayed_work_pending(&p_tas2562->status_work)) schedule_delayed_work(&p_tas2562->status_work, msecs_to_jiffies(20)); } return HRTIMER_NORESTART; } static int tas2562_parse_dt(struct device *dev, struct tas2562_priv *p_tas2562) { struct device_node *np = dev->of_node; int rc = 0, ret = 0; rc = of_property_read_u32(np, "ti,channels", &p_tas2562->mn_channels); if (rc) { dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,channels", np->full_name, rc); } else { dev_dbg(p_tas2562->dev, "ti,channels=%d", p_tas2562->mn_channels); } rc = of_property_read_u32(np, "ti,left-channel", &p_tas2562->mn_l_addr); if (rc) { dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,left-channel", np->full_name, rc); } else { dev_dbg(p_tas2562->dev, "ti,left-channel=0x%x", p_tas2562->mn_l_addr); } if(p_tas2562->mn_channels != 1) { rc = of_property_read_u32(np, "ti,right-channel", &p_tas2562->mn_r_addr); if (rc) { dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,right-channel", np->full_name, rc); } else { dev_dbg(p_tas2562->dev, "ti,right-channel=0x%x", p_tas2562->mn_r_addr); } } p_tas2562->mn_reset_gpio = of_get_named_gpio(np, "ti,reset-gpio", 0); if (!gpio_is_valid(p_tas2562->mn_reset_gpio)) { dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,reset-gpio", np->full_name, p_tas2562->mn_reset_gpio); } else { dev_dbg(p_tas2562->dev, "ti,reset-gpio=%d", p_tas2562->mn_reset_gpio); } if(p_tas2562->mn_channels != 1) { p_tas2562->mn_reset_gpio2 = of_get_named_gpio(np, "ti,reset-gpio2", 0); if (!gpio_is_valid(p_tas2562->mn_reset_gpio2)) { dev_dbg(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,reset-gpio2", np->full_name, p_tas2562->mn_reset_gpio2); } else { dev_dbg(p_tas2562->dev, "ti,reset-gpio2=%d", p_tas2562->mn_reset_gpio2); } } p_tas2562->mn_irq_gpio = of_get_named_gpio(np, "ti,irq-gpio", 0); if (!gpio_is_valid(p_tas2562->mn_irq_gpio)) { dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,irq-gpio", np->full_name, p_tas2562->mn_irq_gpio); } else { dev_dbg(p_tas2562->dev, "ti,irq-gpio=%d", p_tas2562->mn_irq_gpio); } if(p_tas2562->mn_channels != 1) { p_tas2562->mn_irq_gpio2 = of_get_named_gpio(np, "ti,irq-gpio2", 0); if (!gpio_is_valid(p_tas2562->mn_irq_gpio2)) { dev_dbg(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,irq-gpio2", np->full_name, p_tas2562->mn_irq_gpio2); } else { dev_dbg(p_tas2562->dev, "ti,irq-gpio2=%d", p_tas2562->mn_irq_gpio2); } } /* Added for Boost Clock Source Change - Mute Issue */ rc = of_property_read_u32(np, "ti,bst-clk-src", &p_tas2562->mn_bst_clk_src); if (rc) { p_tas2562->mn_bst_clk_src = 0; dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,bst-clk-src", np->full_name, rc); } else { dev_dbg(p_tas2562->dev, "ti,bst-clk-src=0x%x", p_tas2562->mn_bst_clk_src); } /* Added for Mute Issue */ rc = of_property_read_u32(np, "ti,status-check", &p_tas2562->mn_status_check); if (rc) { p_tas2562->mn_status_check = 0; dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,status-check", np->full_name, rc); } else { dev_dbg(p_tas2562->dev, "ti,status-check=0x%x", p_tas2562->mn_status_check); } /* Added for Mute Issue */ rc = of_property_read_u32(np, "ti,status-period", &p_tas2562->mn_status_period); if (rc) { p_tas2562->mn_status_period = 1; dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,status-period", np->full_name, rc); } else { dev_dbg(p_tas2562->dev, "ti,status-period=0x%x", p_tas2562->mn_status_period); } #ifdef CONFIG_TAS25XX_ALGO tas25xx_parse_algo_dt(np); #endif /*CONFIG_TAS25XX_ALGO*/ return ret; } static int tas2562_i2c_probe(struct i2c_client *p_client, const struct i2c_device_id *id) { struct tas2562_priv *p_tas2562; int n_result; dev_err(&p_client->dev, "Driver ID: %s\n", TAS2562_DRIVER_ID); dev_info(&p_client->dev, "%s enter\n", __func__); p_tas2562 = devm_kzalloc(&p_client->dev, sizeof(struct tas2562_priv), GFP_KERNEL); if (p_tas2562 == NULL) { /* dev_err(&p_client->dev, "failed to get i2c device\n"); */ n_result = -ENOMEM; goto err; } p_tas2562->client = p_client; p_tas2562->dev = &p_client->dev; i2c_set_clientdata(p_client, p_tas2562); dev_set_drvdata(&p_client->dev, p_tas2562); p_tas2562->regmap = devm_regmap_init_i2c(p_client, &tas2562_i2c_regmap); if (IS_ERR(p_tas2562->regmap)) { n_result = PTR_ERR(p_tas2562->regmap); dev_err(&p_client->dev, "Failed to allocate register map: %d\n", n_result); goto err; } if (p_client->dev.of_node) tas2562_parse_dt(&p_client->dev, p_tas2562); if (gpio_is_valid(p_tas2562->mn_reset_gpio)) { n_result = gpio_request(p_tas2562->mn_reset_gpio, "TAS2562_RESET"); if (n_result) { dev_err(p_tas2562->dev, "%s: Failed to request gpio %d\n", __func__, p_tas2562->mn_reset_gpio); n_result = -EINVAL; goto err; } tas2562_hw_reset(p_tas2562); } if (gpio_is_valid(p_tas2562->mn_reset_gpio2) && (p_tas2562->mn_channels == 2)) { n_result = gpio_request(p_tas2562->mn_reset_gpio2, "TAS2562_RESET2"); if (n_result) { dev_err(p_tas2562->dev, "%s: Failed to request gpio %d\n", __func__, p_tas2562->mn_reset_gpio2); n_result = -EINVAL; goto err; } tas2562_hw_reset(p_tas2562); } p_tas2562->read = tas2562_dev_read; p_tas2562->write = tas2562_dev_write; p_tas2562->bulk_read = tas2562_dev_bulk_read; p_tas2562->bulk_write = tas2562_dev_bulk_write; p_tas2562->update_bits = tas2562_dev_update_bits; p_tas2562->hw_reset = tas2562_hw_reset; p_tas2562->enable_irq = tas2562_enable_irq; #ifdef CODEC_PM p_tas2562->runtime_suspend = tas2562_runtime_suspend; p_tas2562->runtime_resume = tas2562_runtime_resume; p_tas2562->mn_power_state = TAS2562_POWER_SHUTDOWN; #endif p_tas2562->mn_power_state = TAS2562_POWER_SHUTDOWN; p_tas2562->spk_l_control = 1; mutex_init(&p_tas2562->dev_lock); dev_info(&p_client->dev, "Before SW reset\n"); /* Reset the chip */ n_result = tas2562_dev_write(p_tas2562, channel_both, TAS2562_SOFTWARERESET, 0x01); if (n_result < 0) { dev_err(&p_client->dev, "I2c fail, %d\n", n_result); goto err; } dev_info(&p_client->dev, "After SW reset\n"); /*Initialize IRQ-Work Routin irrespective or IRQ GPIO *as it is used for PowerUp function also */ INIT_DELAYED_WORK(&p_tas2562->irq_work, irq_work_routine); if (gpio_is_valid(p_tas2562->mn_irq_gpio)) { n_result = gpio_request(p_tas2562->mn_irq_gpio, "TAS2562-IRQ"); if (n_result < 0) { dev_err(p_tas2562->dev, "%s: GPIO %d request error\n", __func__, p_tas2562->mn_irq_gpio); goto err; } gpio_direction_input(p_tas2562->mn_irq_gpio); tas2562_dev_write(p_tas2562, channel_both, TAS2562_MISCCONFIGURATIONREG0, 0xce); p_tas2562->mn_irq = gpio_to_irq(p_tas2562->mn_irq_gpio); dev_info(p_tas2562->dev, "irq = %d\n", p_tas2562->mn_irq); n_result = request_threaded_irq(p_tas2562->mn_irq, tas2562_irq_handler, NULL, IRQF_TRIGGER_FALLING|IRQF_ONESHOT, p_client->name, p_tas2562); if (n_result < 0) { dev_err(p_tas2562->dev, "request_irq failed, %d\n", n_result); goto err; } disable_irq_nosync(p_tas2562->mn_irq); } if (gpio_is_valid(p_tas2562->mn_irq_gpio2) && (p_tas2562->mn_channels == 2)) { n_result = gpio_request(p_tas2562->mn_irq_gpio2, "TAS2562-IRQ2"); if (n_result < 0) { dev_err(p_tas2562->dev, "%s: GPIO %d request error\n", __func__, p_tas2562->mn_irq_gpio2); goto err; } gpio_direction_input(p_tas2562->mn_irq_gpio2); tas2562_dev_write(p_tas2562, channel_both, TAS2562_MISCCONFIGURATIONREG0, 0xce); p_tas2562->mn_irq2 = gpio_to_irq(p_tas2562->mn_irq_gpio2); dev_info(p_tas2562->dev, "irq = %d\n", p_tas2562->mn_irq2); n_result = request_threaded_irq(p_tas2562->mn_irq2, tas2562_irq_handler, NULL, IRQF_TRIGGER_FALLING|IRQF_ONESHOT, p_client->name, p_tas2562); if (n_result < 0) { dev_err(p_tas2562->dev, "request_irq failed, %d\n", n_result); goto err; } disable_irq_nosync(p_tas2562->mn_irq2); } tas2562_enable_irq(p_tas2562, true); INIT_DELAYED_WORK(&p_tas2562->init_work, init_work_routine); #ifdef CONFIG_TAS2562_CODEC mutex_init(&p_tas2562->codec_lock); n_result = tas2562_register_codec(p_tas2562); if (n_result < 0) { dev_err(p_tas2562->dev, "register codec failed, %d\n", n_result); goto err; } #endif #ifdef CONFIG_TAS2562_MISC mutex_init(&p_tas2562->file_lock); n_result = tas2562_register_misc(p_tas2562); if (n_result < 0) { dev_err(p_tas2562->dev, "register codec failed, %d\n", n_result); goto err; } #endif if (p_tas2562->mn_status_check) { INIT_DELAYED_WORK(&p_tas2562->status_work, status_work_routine); hrtimer_init(&p_tas2562->mtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); p_tas2562->mtimer.function = timer_func; } err: return n_result; } static int tas2562_i2c_remove(struct i2c_client *p_client) { struct tas2562_priv *p_tas2562 = i2c_get_clientdata(p_client); dev_info(p_tas2562->dev, "%s\n", __func__); if (p_tas2562->mn_status_check) { if (hrtimer_active(&p_tas2562->mtimer)) { dev_dbg(p_tas2562->dev, "cancel timer\n"); hrtimer_cancel(&p_tas2562->mtimer); } if (delayed_work_pending(&p_tas2562->status_work)) { dev_dbg(p_tas2562->dev, "cancel status work\n"); cancel_delayed_work_sync(&p_tas2562->status_work); } } if (delayed_work_pending(&p_tas2562->irq_work)) { dev_dbg(p_tas2562->dev, "cancel IRQ work\n"); cancel_delayed_work_sync(&p_tas2562->irq_work); } #ifdef CONFIG_TAS2562_CODEC tas2562_deregister_codec(p_tas2562); mutex_destroy(&p_tas2562->codec_lock); #endif #ifdef CONFIG_TAS2562_MISC tas2562_deregister_misc(p_tas2562); mutex_destroy(&p_tas2562->file_lock); #endif if (gpio_is_valid(p_tas2562->mn_reset_gpio)) gpio_free(p_tas2562->mn_reset_gpio); if (gpio_is_valid(p_tas2562->mn_irq_gpio)) gpio_free(p_tas2562->mn_irq_gpio); if (gpio_is_valid(p_tas2562->mn_reset_gpio2)) gpio_free(p_tas2562->mn_reset_gpio2); if (gpio_is_valid(p_tas2562->mn_irq_gpio2)) gpio_free(p_tas2562->mn_irq_gpio2); return 0; } static const struct i2c_device_id tas2562_i2c_id[] = { { "tas2562", 0}, { } }; MODULE_DEVICE_TABLE(i2c, tas2562_i2c_id); #if defined(CONFIG_OF) static const struct of_device_id tas2562_of_match[] = { { .compatible = "ti,tas2562" }, {}, }; MODULE_DEVICE_TABLE(of, tas2562_of_match); #endif static const struct dev_pm_ops tas2562_pm_ops = { .suspend = tas2562_pm_suspend, .resume = tas2562_pm_resume }; static struct i2c_driver tas2562_i2c_driver = { .driver = { .name = "tas2562", .owner = THIS_MODULE, #if defined(CONFIG_OF) .of_match_table = of_match_ptr(tas2562_of_match), #endif .pm = &tas2562_pm_ops, }, .probe = tas2562_i2c_probe, .remove = tas2562_i2c_remove, .id_table = tas2562_i2c_id, }; module_i2c_driver(tas2562_i2c_driver); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("TAS2562 I2C Smart Amplifier driver"); MODULE_LICENSE("GPL v2"); #endif