2024-06-15 16:02:09 -03:00
|
|
|
/*
|
|
|
|
* 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 <linux/module.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/pm.h>
|
|
|
|
#include <linux/i2c.h>
|
|
|
|
#include <linux/gpio.h>
|
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
#include <linux/firmware.h>
|
|
|
|
#include <linux/regmap.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_gpio.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <sound/soc.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/pm.h>
|
|
|
|
|
|
|
|
#include "tas2562.h"
|
|
|
|
#include "tas2562-codec.h"
|
|
|
|
#include "tas2562-misc.h"
|
|
|
|
#include "tas2562-bigdata.h"
|
|
|
|
#ifdef CONFIG_TAS25XX_ALGO
|
|
|
|
#include <sound/smart_amp.h>
|
|
|
|
#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);
|
|
|
|
|
2023-01-05 18:04:50 +09:00
|
|
|
goto reload;
|
2024-06-15 16:02:09 -03:00
|
|
|
} 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
|