/* * 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" #include "tas2562-bigdata.h" #ifdef CONFIG_TAS25XX_ALGO #include #endif /*CONFIG_TAS25XX_ALGO*/ 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; static int irq2_enabled; 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 (gpio_is_valid(p_tas2562->mn_irq_gpio2) && irq2_enabled == 0) { desc = irq_to_desc(p_tas2562->mn_irq2); if (desc && desc->depth > 0) enable_irq(p_tas2562->mn_irq2); else dev_info(p_tas2562->dev, "### irq already enabled"); 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 (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 tas2562_irq_work_routine(struct tas2562_priv *p_tas2562) { unsigned int nDevInt1Status = 0, nDevInt2Status = 0, nDevInt3Status = 0, nDevInt4Status = 0; int n_counter = 2; int n_result = 0; int irqreg; enum channel chn; int temp_err_code_l = 0, temp_err_code_r = 0; 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", nDevInt1Status, nDevInt2Status, 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"); /* Added for Big Data */ if (nDevInt1Status & TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT) temp_err_code_l |= ERROR_CLOCK; if (nDevInt3Status & TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT) temp_err_code_r |= ERROR_CLOCK; } else p_tas2562->mn_err_code &= ~ERROR_CLOCK; 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"); /* Added for Big Data */ if (nDevInt1Status & TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT) temp_err_code_l |= ERROR_OVER_CURRENT; if (nDevInt3Status & TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT) temp_err_code_r |= ERROR_OVER_CURRENT; } 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"); /* Added for Big Data */ if (nDevInt1Status & TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT) temp_err_code_l |= ERROR_DIE_OVERTEMP; if (nDevInt3Status & TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT) temp_err_code_r |= ERROR_DIE_OVERTEMP; } 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"); /* Added for Big Data */ if (nDevInt2Status & TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT) temp_err_code_l |= ERROR_OVER_VOLTAGE; if (nDevInt4Status & TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT) temp_err_code_r |= ERROR_OVER_VOLTAGE; } 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"); /* Added for Big Data */ if (nDevInt2Status & TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT) temp_err_code_l |= ERROR_UNDER_VOLTAGE; if (nDevInt4Status & TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT) temp_err_code_r |= ERROR_UNDER_VOLTAGE; } 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"); /* Added for Big Data */ if (nDevInt2Status & TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT) temp_err_code_l |= ERROR_BROWNOUT; if (nDevInt4Status & TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT) temp_err_code_r |= ERROR_BROWNOUT; } else p_tas2562->mn_err_code &= ~ERROR_BROWNOUT; /* Added for Big Data */ if (temp_err_code_l) tas2562_update_bigdata(channel_left, temp_err_code_l); if (temp_err_code_r) tas2562_update_bigdata(channel_right, temp_err_code_r); 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__); 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_both, TAS2562_INTERRUPTMASKREG0, 0xf8); if (n_result < 0) goto reload; n_result = p_tas2562->write(p_tas2562, channel_both, 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); #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); dev_info(p_tas2562->dev, "%s\n", __func__); #ifdef CONFIG_TAS2562_CODEC mutex_lock(&p_tas2562->codec_lock); #endif /* Clear latched IRQ before power on */ p_tas2562->update_bits(p_tas2562, channel_both, TAS2562_INTERRUPTCONFIGURATION, TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR_MASK, TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR); p_tas2562->write(p_tas2562, channel_both, TAS2562_INTERRUPTMASKREG0, 0xf8); p_tas2562->write(p_tas2562, channel_both, TAS2562_INTERRUPTMASKREG1, 0xb1); p_tas2562->enable_irq(p_tas2562, true); #ifdef CONFIG_TAS2562_CODEC mutex_unlock(&p_tas2562->codec_lock); #endif } static void irq_work_routine(struct work_struct *work) { struct tas2562_priv *p_tas2562 = container_of(work, struct tas2562_priv, irq_work.work); tas2562_irq_work_routine(p_tas2562); } static void irq_work_routine2(struct work_struct *work) { struct tas2562_priv *p_tas2562 = container_of(work, struct tas2562_priv, irq_work2.work); tas2562_irq_work_routine(p_tas2562); } static irqreturn_t tas2562_irq_handler(int irq, void *dev_id) { struct tas2562_priv *p_tas2562 = (struct tas2562_priv *)dev_id; dev_info(p_tas2562->dev, "%s IRQ scheduled for IRQ NO: %d\n", __func__, irq); /* get IRQ status after 100 ms */ schedule_delayed_work(&p_tas2562->irq_work, msecs_to_jiffies(100)); return IRQ_HANDLED; } static irqreturn_t tas2562_irq_handler2(int irq, void *dev_id) { struct tas2562_priv *p_tas2562 = (struct tas2562_priv *)dev_id; dev_info(p_tas2562->dev, "%s IRQ scheduled for IRQ NO: %d\n", __func__, irq); /* get IRQ status after 100 ms */ schedule_delayed_work(&p_tas2562->irq_work2, 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 (delayed_work_pending(&p_tas2562->irq_work)) { dev_dbg(p_tas2562->dev, "cancel IRQ work\n"); cancel_delayed_work_sync(&p_tas2562->irq_work); } if (delayed_work_pending(&p_tas2562->irq_work2)) { dev_dbg(p_tas2562->dev, "cancel IRQ work2\n"); cancel_delayed_work_sync(&p_tas2562->irq_work2); } return 0; } static int tas2562_runtime_resume(struct tas2562_priv *p_tas2562) { dev_dbg(p_tas2562->dev, "%s\n", __func__); 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) { pr_err("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) { pr_err("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 Big Data */ struct tas_bigdata *p_tas_bd; struct tas_bigdata *tas2562_get_sysfs_ptr(void) { if (!p_tas_bd) p_tas_bd = kzalloc(sizeof(struct tas_bigdata), GFP_KERNEL); if (!p_tas_bd) return ERR_PTR(-ENOMEM); return p_tas_bd; } /* IRQ Count related entries */ static ssize_t tas25xx_bd_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t tas25xx_bd_persist_show(struct device *dev, struct device_attribute *attr, char *buf); static DEVICE_ATTR(clock_error, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(clock_error_r, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(spk_over_current, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(spk_over_current_r, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(spk_over_voltage, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(spk_over_voltage_r, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(spk_under_voltage, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(spk_under_voltage_r, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(die_over_temp, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(die_over_temp_r, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(brown_out, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(brown_out_r, 0664, tas25xx_bd_show, NULL); static DEVICE_ATTR(clock_error_persist, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(clock_error_persist_r, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(spk_over_current_persist, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(spk_over_current_persist_r, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(spk_over_voltage_persist, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(spk_over_voltage_persist_r, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(spk_under_voltage_persist, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(spk_under_voltage_persist_r, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(die_over_temp_persist, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(die_over_temp_persist_r, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(brown_out_persist, 0664, tas25xx_bd_persist_show, NULL); static DEVICE_ATTR(brown_out_persist_r, 0664, tas25xx_bd_persist_show, NULL); static ssize_t tas25xx_bd_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; struct tas_bigdata *tas_bd = tas2562_get_sysfs_ptr(); if (IS_ERR(tas_bd)) { pr_err("[tas2562:%s] Error memory allocation\n", __func__); return PTR_ERR(tas_bd); } if (attr == &dev_attr_clock_error) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].count_clock_error); tas_bd->irqs[0].count_clock_error = 0; } else if (attr == &dev_attr_spk_over_current) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].count_spk_over_current); tas_bd->irqs[0].count_spk_over_current = 0; } else if (attr == &dev_attr_spk_over_voltage) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].count_spk_over_voltage); tas_bd->irqs[0].count_spk_over_voltage = 0; } else if (attr == &dev_attr_spk_under_voltage) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].count_spk_under_voltage); tas_bd->irqs[0].count_spk_under_voltage = 0; } else if (attr == &dev_attr_die_over_temp) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].count_die_over_temp); tas_bd->irqs[0].count_die_over_temp = 0; } else if (attr == &dev_attr_brown_out) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].count_brown_out); tas_bd->irqs[0].count_brown_out = 0; } else if (attr == &dev_attr_clock_error_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].count_clock_error); tas_bd->irqs[1].count_clock_error = 0; } else if (attr == &dev_attr_spk_over_current_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].count_spk_over_current); tas_bd->irqs[1].count_spk_over_current = 0; } else if (attr == &dev_attr_spk_over_voltage_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].count_spk_over_voltage); tas_bd->irqs[1].count_spk_over_voltage = 0; } else if (attr == &dev_attr_spk_under_voltage_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].count_spk_under_voltage); tas_bd->irqs[1].count_spk_under_voltage = 0; } else if (attr == &dev_attr_die_over_temp_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].count_die_over_temp); tas_bd->irqs[1].count_die_over_temp = 0; } else if (attr == &dev_attr_brown_out_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].count_brown_out); tas_bd->irqs[1].count_brown_out = 0; } return ret; } static ssize_t tas25xx_bd_persist_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret = 0; struct tas_bigdata *tas_bd = tas2562_get_sysfs_ptr(); if (IS_ERR(tas_bd)) { pr_err("tas2562:%s] Error memory allocation\n", __func__); return PTR_ERR(tas_bd); } if (attr == &dev_attr_clock_error_persist) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].persist_count_clock_error); } else if (attr == &dev_attr_spk_over_current_persist) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].persist_count_spk_over_current); } else if (attr == &dev_attr_spk_over_voltage_persist) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].persist_count_spk_over_voltage); } else if (attr == &dev_attr_spk_under_voltage_persist) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].persist_count_spk_under_voltage); } else if (attr == &dev_attr_die_over_temp_persist) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].persist_count_die_over_temp); } else if (attr == &dev_attr_brown_out_persist) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[0].persist_count_brown_out); } else if (attr == &dev_attr_clock_error_persist_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].persist_count_clock_error); } else if (attr == &dev_attr_spk_over_current_persist_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].persist_count_spk_over_current); } else if (attr == &dev_attr_spk_over_voltage_persist_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].persist_count_spk_over_voltage); } else if (attr == &dev_attr_spk_under_voltage_persist_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].persist_count_spk_under_voltage); } else if (attr == &dev_attr_die_over_temp_persist_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].persist_count_die_over_temp); } else if (attr == &dev_attr_brown_out_persist_r) { ret = snprintf(buf, MAX_STRING, "%d", tas_bd->irqs[1].persist_count_brown_out); } return ret; } static struct attribute *tas2562_bd_attr[] = { &dev_attr_clock_error.attr, &dev_attr_spk_over_current.attr, &dev_attr_spk_over_voltage.attr, &dev_attr_spk_under_voltage.attr, &dev_attr_die_over_temp.attr, &dev_attr_brown_out.attr, &dev_attr_clock_error_persist.attr, &dev_attr_spk_over_current_persist.attr, &dev_attr_spk_over_voltage_persist.attr, &dev_attr_spk_under_voltage_persist.attr, &dev_attr_die_over_temp_persist.attr, &dev_attr_brown_out_persist.attr, }; static struct attribute *tas2562_bd_attr_r[] = { &dev_attr_clock_error_r.attr, &dev_attr_spk_over_current_r.attr, &dev_attr_spk_over_voltage_r.attr, &dev_attr_spk_under_voltage_r.attr, &dev_attr_die_over_temp_r.attr, &dev_attr_brown_out_r.attr, &dev_attr_clock_error_persist_r.attr, &dev_attr_spk_over_current_persist_r.attr, &dev_attr_spk_over_voltage_persist_r.attr, &dev_attr_spk_under_voltage_persist_r.attr, &dev_attr_die_over_temp_persist_r.attr, &dev_attr_brown_out_persist_r.attr, }; static struct attribute *tas2562_bd_attr_m[ ARRAY_SIZE(tas2562_bd_attr) + ARRAY_SIZE(tas2562_bd_attr_r) + 1] = {NULL}; static struct attribute_group tas2562_bd_attr_grp = { .attrs = tas2562_bd_attr_m, }; void tas2562_update_bigdata(int chn, int err_code) { struct tas_bigdata *tas_bd = tas2562_get_sysfs_ptr(); if (chn & channel_left) { if (err_code & ERROR_CLOCK) { tas_bd->irqs[0].count_clock_error++; tas_bd->irqs[0].persist_count_clock_error++; } if (err_code & ERROR_OVER_CURRENT) { tas_bd->irqs[0].count_spk_over_current++; tas_bd->irqs[0].persist_count_spk_over_current++; } if (err_code & ERROR_OVER_VOLTAGE) { tas_bd->irqs[0].count_spk_over_voltage++; tas_bd->irqs[0].persist_count_spk_over_voltage++; } if (err_code & ERROR_UNDER_VOLTAGE) { tas_bd->irqs[0].count_spk_under_voltage++; tas_bd->irqs[0].persist_count_spk_under_voltage++; } if (err_code & ERROR_DIE_OVERTEMP) { tas_bd->irqs[0].count_die_over_temp++; tas_bd->irqs[0].persist_count_die_over_temp++; } if (err_code & ERROR_BROWNOUT) { tas_bd->irqs[0].count_brown_out++; tas_bd->irqs[0].persist_count_brown_out++; } } else if (chn & channel_right) { if (err_code & ERROR_CLOCK) { tas_bd->irqs[1].count_clock_error++; tas_bd->irqs[1].persist_count_clock_error++; } if (err_code & ERROR_OVER_CURRENT) { tas_bd->irqs[1].count_spk_over_current++; tas_bd->irqs[1].persist_count_spk_over_current++; } if (err_code & ERROR_OVER_VOLTAGE) { tas_bd->irqs[1].count_spk_over_voltage++; tas_bd->irqs[1].persist_count_spk_over_voltage++; } if (err_code & ERROR_UNDER_VOLTAGE) { tas_bd->irqs[1].count_spk_under_voltage++; tas_bd->irqs[1].persist_count_spk_under_voltage++; } if (err_code & ERROR_DIE_OVERTEMP) { tas_bd->irqs[1].count_die_over_temp++; tas_bd->irqs[1].persist_count_die_over_temp++; } if (err_code & ERROR_BROWNOUT) { tas_bd->irqs[1].count_brown_out++; tas_bd->irqs[1].persist_count_brown_out++; } } } static void tas2562_remove_bigdata(void) { if (p_tas_bd) { struct tas_bigdata *tas_bd = p_tas_bd; if (tas_bd->irq_dev) { sysfs_remove_group(&tas_bd->irq_dev->kobj, &tas2562_bd_attr_grp); device_destroy(tas_bd->bd_class, 1); } if (tas_bd->bd_class) class_destroy(tas_bd->bd_class); kfree(p_tas_bd); p_tas_bd = NULL; } } void tas2562_add_bigdata(uint8_t channels) { int32_t ret = 0; struct tas_bigdata *tas_bd = tas2562_get_sysfs_ptr(); if (IS_ERR(tas_bd)) { pr_err("[tas2562:%s] Error memory allocation\n", __func__); ret = PTR_ERR(tas_bd); goto err_dev; } memcpy(tas2562_bd_attr_m, tas2562_bd_attr, sizeof(tas2562_bd_attr)); if (channels == 2) { memcpy(tas2562_bd_attr_m + ARRAY_SIZE(tas2562_bd_attr), tas2562_bd_attr_r, sizeof(tas2562_bd_attr_r)); } tas_bd->bd_class = class_create(THIS_MODULE, TAS2562_SYSFS_CLASS_NAME); if (IS_ERR(tas_bd->bd_class)) { ret = PTR_ERR(tas_bd->bd_class); pr_err("[tas2562:%s] err class create\n", __func__); tas_bd->bd_class = NULL; goto err_dev; } tas_bd->irq_dev = device_create(tas_bd->bd_class, NULL, 1, NULL, TAS2562_IRQ_DIR_NAME); if (IS_ERR(tas_bd->irq_dev)) { pr_err("[tas2562:%s]Failed to create irqs\n", __func__); ret = PTR_ERR(tas_bd->irq_dev); tas_bd->irq_dev = NULL; goto err_dev; } ret = sysfs_create_group(&tas_bd->irq_dev->kobj, &tas2562_bd_attr_grp); if (ret) { pr_err("[tas2562:%s]Failed to create sysfs group\n", __func__); goto err_dev; } pr_info("[tas2562:%s] ret=%d\n", __func__, ret); return; err_dev: tas2562_remove_bigdata(); pr_err("[tas2562:%s] Error %d\n", __func__, ret); } 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); } } rc = of_property_read_u32(np, "ti,iv-width", &p_tas2562->mn_iv_width); if (rc) { dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,iv-width", np->full_name, rc); } else { dev_dbg(p_tas2562->dev, "ti,iv-width=0x%x", p_tas2562->mn_iv_width); } rc = of_property_read_u32(np, "ti,vbat-mon", &p_tas2562->mn_vbat); if (rc) { dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", "ti,vbat-mon", np->full_name, rc); } else { dev_dbg(p_tas2562->dev, "ti,vbat-mon=0x%x", p_tas2562->mn_vbat); } #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_info(&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 to 0 */ p_tas2562->valid_irq_gpio = 0; 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); INIT_DELAYED_WORK(&p_tas2562->irq_work, irq_work_routine); 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); /* Added for BigData */ p_tas2562->valid_irq_gpio = 1; } 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); INIT_DELAYED_WORK(&p_tas2562->irq_work2, irq_work_routine2); n_result = request_threaded_irq(p_tas2562->mn_irq2, tas2562_irq_handler2, 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); /* Added for BigData */ p_tas2562->valid_irq_gpio = 1; } INIT_DELAYED_WORK(&p_tas2562->init_work, init_work_routine); tas2562_add_bigdata(p_tas2562->mn_channels); #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 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__); tas2562_remove_bigdata(); #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