blob: 613d6e61dbadcbaf62e3d31dc399d105b78bacd0 [file] [edit]
/*
* ALSA SoC Texas Instruments TAS256X 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.
*
*/
#if IS_ENABLED(CONFIG_TAS256X_REGMAP)
#ifdef CONFIG_DYNAMIC_DEBUG
#define DEBUG 5
#endif
#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 <sound/tlv.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqnr.h>
#include <linux/pm.h>
#include <linux/version.h>
#include "physical_layer/inc/tas256x.h"
#include "logical_layer/inc/tas256x-logic.h"
#include "physical_layer/inc/tas256x-device.h"
#include "os_layer/inc/tas256x-codec.h"
#include "os_layer/inc/tas256x-regmap.h"
#include "misc/tas256x-misc.h"
#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
#include "algo/inc/tas_smart_amp_v2.h"
#if IS_ENABLED(CONFIG_PLATFORM_QCOM)
#include <dsp/tas_qualcomm.h>
static dc_detection_data_t s_dc_detect;
#endif /*CONFIG_PLATFORM_QCOM*/
#endif /*CONFIG_TAS25XX_ALGO*/
/*For mixer_control implementation*/
#define MAX_STRING 200
static const char *dts_tag[][3] = {
{
"ti,left-channel",
"ti,reset-gpio",
"ti,irq-gpio"
},
{
"ti,right-channel",
"ti,reset-gpio2",
"ti,irq-gpio2"
}
};
static const char *reset_gpio_label[2] = {
"TAS256X_RESET", "TAS256X_RESET2"
};
static const char *irq_gpio_label[2] = {
"TAS256X-IRQ", "TAS256X-IRQ2"
};
static int tas256x_regmap_write(void *plat_data, unsigned int i2c_addr,
unsigned int reg, unsigned int value)
{
int nResult = 0;
int retry_count = TAS256X_I2C_RETRY_COUNT;
struct linux_platform *platform_data =
(struct linux_platform *)plat_data;
if (platform_data->i2c_suspend)
return ERROR_I2C_SUSPEND;
platform_data->client->addr = i2c_addr;
while (retry_count--) {
nResult = regmap_write(platform_data->regmap, reg,
value);
if (nResult >= 0)
break;
msleep(20);
}
if (retry_count == -1)
return ERROR_I2C_FAILED;
else
return 0;
}
static int tas256x_regmap_bulk_write(void *plat_data, unsigned int i2c_addr,
unsigned int reg, unsigned char *pData,
unsigned int nLength)
{
int nResult = 0;
int retry_count = TAS256X_I2C_RETRY_COUNT;
struct linux_platform *platform_data =
(struct linux_platform *)plat_data;
if (platform_data->i2c_suspend)
return ERROR_I2C_SUSPEND;
platform_data->client->addr = i2c_addr;
while (retry_count--) {
nResult = regmap_bulk_write(platform_data->regmap, reg,
pData, nLength);
if (nResult >= 0)
break;
msleep(20);
}
if (retry_count == -1)
return ERROR_I2C_FAILED;
else
return 0;
}
static int tas256x_regmap_read(void *plat_data, unsigned int i2c_addr,
unsigned int reg, unsigned int *value)
{
int nResult = 0;
int retry_count = TAS256X_I2C_RETRY_COUNT;
struct linux_platform *platform_data =
(struct linux_platform *)plat_data;
if (platform_data->i2c_suspend)
return ERROR_I2C_SUSPEND;
platform_data->client->addr = i2c_addr;
while (retry_count--) {
nResult = regmap_read(platform_data->regmap, reg,
value);
if (nResult >= 0)
break;
msleep(20);
}
if (retry_count == -1)
return ERROR_I2C_FAILED;
else
return 0;
}
static int tas256x_regmap_bulk_read(void *plat_data, unsigned int i2c_addr,
unsigned int reg, unsigned char *pData,
unsigned int nLength)
{
int nResult = 0;
int retry_count = TAS256X_I2C_RETRY_COUNT;
struct linux_platform *platform_data =
(struct linux_platform *)plat_data;
if (platform_data->i2c_suspend)
return ERROR_I2C_SUSPEND;
platform_data->client->addr = i2c_addr;
while (retry_count--) {
nResult = regmap_bulk_read(platform_data->regmap, reg,
pData, nLength);
if (nResult >= 0)
break;
msleep(20);
}
if (retry_count == -1)
return ERROR_I2C_FAILED;
else
return 0;
}
static int tas256x_regmap_update_bits(void *plat_data, unsigned int i2c_addr,
unsigned int reg, unsigned int mask,
unsigned int value)
{
int nResult = 0;
int retry_count = TAS256X_I2C_RETRY_COUNT;
struct linux_platform *platform_data =
(struct linux_platform *)plat_data;
if (platform_data->i2c_suspend)
return ERROR_I2C_SUSPEND;
platform_data->client->addr = i2c_addr;
while (retry_count--) {
nResult = regmap_update_bits(platform_data->regmap, reg,
mask, value);
if (nResult >= 0)
break;
msleep(20);
}
if (retry_count == -1)
return ERROR_I2C_FAILED;
else
return 0;
}
static char const *tas2564_rx_mode_text[] = {"Speaker", "Receiver"};
static const struct soc_enum tas2564_rx_mode_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2564_rx_mode_text),
tas2564_rx_mode_text),
};
static int tas2564_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct tas256x_priv *p_tas256x = NULL;
struct linux_platform *plat_data = NULL;
int ret = -1;
if (codec == NULL) {
pr_err("%s:codec is NULL\n", __func__);
return ret;
}
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
p_tas256x = snd_soc_component_get_drvdata(codec);
#else
p_tas256x = snd_soc_codec_get_drvdata(codec);
#endif
if (p_tas256x == NULL) {
pr_err("%s:p_tas256x is NULL\n", __func__);
return ret;
}
plat_data = (struct linux_platform *) p_tas256x->platform_data;
if (strnstr(ucontrol->id.name, "LEFT", MAX_STRING))
ret = tas2564_rx_mode_update(p_tas256x,
ucontrol->value.integer.value[0], channel_left);
else if (strnstr(ucontrol->id.name, "RIGHT", MAX_STRING))
ret = tas2564_rx_mode_update(p_tas256x,
ucontrol->value.integer.value[0], channel_right);
else
dev_err(plat_data->dev, "Invalid Channel %s\n",
ucontrol->id.name);
return ret;
}
static int tas2564_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret = -1;
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct linux_platform *plat_data = NULL;
struct tas256x_priv *p_tas256x = NULL;
if (codec == NULL) {
pr_err("%s:codec is NULL\n", __func__);
return ret;
}
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
p_tas256x = snd_soc_component_get_drvdata(codec);
#else
p_tas256x = snd_soc_codec_get_drvdata(codec);
#endif
if (p_tas256x == NULL) {
pr_err("%s:p_tas256x is NULL\n", __func__);
return ret;
}
plat_data = (struct linux_platform *) p_tas256x->platform_data;
if (strnstr(ucontrol->id.name, "LEFT", MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[0]->rx_mode;
else if (strnstr(ucontrol->id.name, "RIGHT", MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[1]->rx_mode;
else
dev_err(plat_data->dev, "Invalid Channel %s\n",
ucontrol->id.name);
return 0;
}
static int tas256x_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct tas256x_priv *p_tas256x = NULL;
int ret = -1;
if ((codec == NULL) || (mc == NULL)) {
pr_err("%s:codec or control is NULL\n", __func__);
return ret;
}
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
p_tas256x = snd_soc_component_get_drvdata(codec);
#else
p_tas256x = snd_soc_codec_get_drvdata(codec);
#endif
if (p_tas256x == NULL) {
pr_err("%s:p_tas256x is NULL\n", __func__);
return ret;
}
if (ucontrol->value.integer.value[0] > mc->max)
return ret;
switch (mc->reg) {
case DVC_PCM:
ret = tas256x_update_playback_volume(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case LIM_MAX_ATN:
ret = tas256x_update_lim_max_attenuation(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case LIMB_INF_PT:
ret = tas256x_update_lim_inflection_point(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case LIMB_SLOPE:
ret = tas256x_update_lim_slope(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case LIMB_ATK_RT:
ret = tas256x_update_limiter_attack_rate(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case LIMB_RLS_RT:
ret = tas256x_update_limiter_release_rate(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case LIMB_RLS_ST:
ret = tas256x_update_limiter_release_step_size(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case LIMB_ATK_ST:
ret = tas256x_update_limiter_attack_step_size(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case BOP_ATK_RT:
ret = tas256x_update_bop_attack_rate(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case BOP_ATK_ST:
ret = tas256x_update_bop_attack_step_size(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case BOP_HLD_TM:
ret = tas256x_update_bop_hold_time(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case BST_VREG:
ret = tas256x_update_boost_voltage(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case BST_ILIM:
ret = tas256x_update_current_limit(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case LIMB_TH_MAX:
ret = tas256x_update_lim_max_thr(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case LIMB_TH_MIN:
ret = tas256x_update_lim_min_thr(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case BOP_TH:
ret = tas256x_update_bop_thr(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case BOSD_TH:
ret = tas256x_update_bosd_thr(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case CLASSH_TIMER:
ret = tas256x_update_classh_timer(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case AMPOUTPUT_LVL:
ret = tas256x_update_ampoutput_level(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case ICN_THR:
ret = tas256x_update_icn_threshold(p_tas256x,
ucontrol->value.integer.value[0], mc->shift);
break;
case ICN_HYST:
ret = tas256x_update_icn_hysterisis(p_tas256x,
ucontrol->value.integer.value[0],
p_tas256x->mn_sampling_rate, mc->shift);
break;
}
return ret;
}
static int tas256x_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct tas256x_priv *p_tas256x = NULL;
int ret = -1;
if ((codec == NULL) || (mc == NULL)) {
pr_err("%s:codec or control is NULL\n", __func__);
return ret;
}
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
p_tas256x = snd_soc_component_get_drvdata(codec);
#else
p_tas256x = snd_soc_codec_get_drvdata(codec);
#endif
if (p_tas256x == NULL) {
pr_err("%s:p_tas256x is NULL\n", __func__);
return ret;
}
switch (mc->reg) {
case DVC_PCM:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->dvc_pcm;
break;
case LIM_MAX_ATN:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->lim_max_attn;
break;
case LIMB_INF_PT:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->lim_infl_pt;
break;
case LIMB_SLOPE:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->lim_trk_slp;
break;
case LIMB_ATK_RT:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->lim_att_rate;
break;
case LIMB_RLS_RT:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->lim_rel_rate;
break;
case LIMB_RLS_ST:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->lim_rel_stp_size;
break;
case LIMB_ATK_ST:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->lim_att_stp_size;
break;
case BOP_ATK_RT:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->bop_att_rate;
break;
case BOP_ATK_ST:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->bop_att_stp_size;
break;
case BOP_HLD_TM:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->bop_hld_time;
break;
case BST_VREG:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->bst_vltg;
break;
case BST_ILIM:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->bst_ilm;
break;
case LIMB_TH_MAX:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->lim_thr_max;
break;
case LIMB_TH_MIN:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->lim_thr_min;
break;
case BOP_TH:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->bop_thd;
break;
case BOSD_TH:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->bosd_thd;
break;
case CLASSH_TIMER:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->classh_timer;
break;
case AMPOUTPUT_LVL:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->ampoutput_lvl;
break;
case ICN_THR:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->icn_thr;
break;
case ICN_HYST:
ucontrol->value.integer.value[0] =
p_tas256x->devs[mc->shift-1]->icn_hyst;
break;
}
return 0;
}
static int tas256x_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int ret = -1;
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct tas256x_priv *p_tas256x = NULL;
struct linux_platform *plat_data = NULL;
if (codec == NULL) {
pr_err("%s:codec is NULL\n", __func__);
return ret;
}
if (ucontrol == NULL) {
pr_err("%s:ucontrol is NULL\n", __func__);
return ret;
}
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
p_tas256x = snd_soc_component_get_drvdata(codec);
#else
p_tas256x = snd_soc_codec_get_drvdata(codec);
#endif
if (p_tas256x == NULL) {
pr_err("%s:p_tas256x is NULL\n", __func__);
return ret;
}
plat_data = (struct linux_platform *) p_tas256x->platform_data;
if (strnstr(ucontrol->id.name, "Version", MAX_STRING)) {
ucontrol->value.integer.value[0] = 0;
} else if (strnstr(ucontrol->id.name, "LEFT", MAX_STRING)) {
if (strnstr(ucontrol->id.name, "LIMITER SWITCH",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[0]->lim_switch;
else if (strnstr(ucontrol->id.name, "BOP ENABLE",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[0]->bop_enable;
else if (strnstr(ucontrol->id.name, "BOP MUTE",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[0]->bop_mute;
else if (strnstr(ucontrol->id.name, "BROWNOUT SHUTDOWN",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[0]->bosd_enable;
else if (strnstr(ucontrol->id.name, "VBAT LPF",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[0]->vbat_lpf;
else if (strnstr(ucontrol->id.name, "RECIEVER ENABLE",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[0]->reciever_enable;
else if (strnstr(ucontrol->id.name, "NOISE GATE",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[0]->noise_gate;
else
dev_err(plat_data->dev, "Invalid controll %s\n",
ucontrol->id.name);
} else if (strnstr(ucontrol->id.name, "RIGHT", MAX_STRING)) {
if (strnstr(ucontrol->id.name, "LIMITER SWITCH",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[1]->lim_switch;
else if (strnstr(ucontrol->id.name, "BOP ENABLE",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[1]->bop_enable;
else if (strnstr(ucontrol->id.name, "BOP MUTE",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[1]->bop_mute;
else if (strnstr(ucontrol->id.name, "BROWNOUT SHUTDOWN",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[1]->bosd_enable;
else if (strnstr(ucontrol->id.name, "VBAT LPF",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[1]->vbat_lpf;
else if (strnstr(ucontrol->id.name, "RECIEVER ENABLE",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[1]->reciever_enable;
else if (strnstr(ucontrol->id.name, "NOISE GATE",
MAX_STRING))
ucontrol->value.integer.value[0] =
p_tas256x->devs[1]->noise_gate;
else
dev_err(plat_data->dev, "Invalid controll %s\n",
ucontrol->id.name);
} else {
dev_err(plat_data->dev, "Invalid Channel %s\n",
ucontrol->id.name);
}
return 0;
}
static int tas256x_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct tas256x_priv *p_tas256x = NULL;
struct linux_platform *plat_data = NULL;
int ret = -1;
if ((codec == NULL) || (mc == NULL)) {
pr_err("%s:codec or control is NULL\n", __func__);
return ret;
}
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
p_tas256x = snd_soc_component_get_drvdata(codec);
#else
p_tas256x = snd_soc_codec_get_drvdata(codec);
#endif
if (p_tas256x == NULL) {
pr_err("%s:p_tas256x is NULL\n", __func__);
return ret;
}
plat_data = (struct linux_platform *) p_tas256x->platform_data;
if (strnstr(ucontrol->id.name, "LEFT", MAX_STRING)) {
if (strnstr(ucontrol->id.name, "LIMITER SWITCH",
MAX_STRING))
ret = tas256x_update_limiter_enable(p_tas256x,
ucontrol->value.integer.value[0],
channel_left);
else if (strnstr(ucontrol->id.name, "BOP ENABLE",
MAX_STRING))
ret = tas256x_update_bop_enable(p_tas256x,
ucontrol->value.integer.value[0],
channel_left);
else if (strnstr(ucontrol->id.name, "BOP MUTE",
MAX_STRING))
ret = tas256x_update_bop_mute(p_tas256x,
ucontrol->value.integer.value[0],
channel_left);
else if (strnstr(ucontrol->id.name, "BROWNOUT SHUTDOWN",
MAX_STRING))
ret = tas256x_update_bop_shutdown_enable(p_tas256x,
ucontrol->value.integer.value[0],
channel_left);
else if (strnstr(ucontrol->id.name, "VBAT LPF",
MAX_STRING))
ret = tas256x_update_vbat_lpf(p_tas256x,
ucontrol->value.integer.value[0],
channel_left);
else if (strnstr(ucontrol->id.name, "RECIEVER ENABLE",
MAX_STRING))
ret = tas256x_enable_reciever_mode(p_tas256x,
ucontrol->value.integer.value[0],
channel_left);
else if (strnstr(ucontrol->id.name, "NOISE GATE",
MAX_STRING))
ret = tas256x_enable_noise_gate(p_tas256x,
ucontrol->value.integer.value[0],
channel_left);
else
dev_err(plat_data->dev, "Invalid Control %s\n",
ucontrol->id.name);
} else if (strnstr(ucontrol->id.name, "RIGHT", MAX_STRING)) {
if (strnstr(ucontrol->id.name, "LIMITER SWITCH",
MAX_STRING))
ret = tas256x_update_limiter_enable(p_tas256x,
ucontrol->value.integer.value[0],
channel_right);
else if (strnstr(ucontrol->id.name, "BOP ENABLE",
MAX_STRING))
ret = tas256x_update_bop_enable(p_tas256x,
ucontrol->value.integer.value[0],
channel_right);
else if (strnstr(ucontrol->id.name, "BOP MUTE",
MAX_STRING))
ret = tas256x_update_bop_mute(p_tas256x,
ucontrol->value.integer.value[0],
channel_right);
else if (strnstr(ucontrol->id.name, "BROWNOUT SHUTDOWN",
MAX_STRING))
ret = tas256x_update_bop_shutdown_enable(p_tas256x,
ucontrol->value.integer.value[0],
channel_right);
else if (strnstr(ucontrol->id.name, "VBAT LPF",
MAX_STRING))
ret = tas256x_update_vbat_lpf(p_tas256x,
ucontrol->value.integer.value[0],
channel_right);
else if (strnstr(ucontrol->id.name, "RECIEVER ENABLE",
MAX_STRING))
ret = tas256x_enable_reciever_mode(p_tas256x,
ucontrol->value.integer.value[0],
channel_right);
else if (strnstr(ucontrol->id.name, "NOISE GATE",
MAX_STRING))
ret = tas256x_enable_noise_gate(p_tas256x,
ucontrol->value.integer.value[0],
channel_right);
else
dev_err(plat_data->dev, "Invalid control %s\n",
ucontrol->id.name);
} else {
dev_err(plat_data->dev, "Invalid Channel %s\n",
ucontrol->id.name);
}
return ret;
}
static char const *tas256x_rx_switch_text[] = {"DISABLE", "ENABLE"};
static const struct soc_enum tas256x_rx_switch_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas256x_rx_switch_text),
tas256x_rx_switch_text),
};
static char const *tas256x_version_text[] = {TAS256X_DRIVER_TAG};
static const struct soc_enum tas256x_version_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas256x_version_text),
tas256x_version_text),
};
static const struct snd_kcontrol_new tas256x_left_controls[] = {
SOC_ENUM_EXT("TAS256X Version", tas256x_version_enum[0],
tas256x_enum_get, NULL),
SOC_SINGLE_EXT("TAS256X PLAYBACK VOLUME LEFT", DVC_PCM, 1, 56, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM MAX ATTN LEFT", LIM_MAX_ATN, 1, 15, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM THR MAX LEFT", LIMB_TH_MAX,
1, 26, 0, tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM THR MIN LEFT", LIMB_TH_MIN,
1, 26, 0, tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM INFLECTION POINT LEFT", LIMB_INF_PT,
1, 40, 0, tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM SLOPE LEFT", LIMB_SLOPE, 1, 6, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOP THR LEFT", BOP_TH,
1, 15, 0, tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOSD THR LEFT", BOSD_TH,
1, 15, 0, tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM ATTACT RATE LEFT", LIMB_ATK_RT, 1, 7, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM RELEASE RATE LEFT", LIMB_RLS_RT, 1, 7, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM ATTACK STEP LEFT", LIMB_ATK_ST, 1, 3, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM RELEASE STEP LEFT", LIMB_RLS_ST, 1, 3, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOP ATTACK RATE LEFT", BOP_ATK_RT, 1, 7, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOP ATTACK STEP LEFT", BOP_ATK_ST, 1, 3, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOP HOLD TIME LEFT", BOP_HLD_TM, 1, 7, 0,
tas256x_get, tas256x_put),
SOC_ENUM_EXT("TAS256X LIMITER SWITCH LEFT", tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_ENUM_EXT("TAS256X BOP ENABLE LEFT", tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_ENUM_EXT("TAS256X BOP MUTE LEFT", tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_ENUM_EXT("TAS256X BROWNOUT SHUTDOWN LEFT",
tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_SINGLE_EXT("TAS256X CLASSH TIMER LEFT", CLASSH_TIMER, 1, 22, 0,
tas256x_get, tas256x_put),
SOC_ENUM_EXT("TAS256X RECIEVER ENABLE LEFT", tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_SINGLE_EXT("TAS256X ICN THRESHOLD LEFT", ICN_THR, 1, 84, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X ICN HYSTERISIS LEFT", ICN_HYST, 1, 19, 0,
tas256x_get, tas256x_put),
SOC_ENUM_EXT("TAS256X NOISE GATE LEFT", tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
};
static const struct snd_kcontrol_new tas256x_right_controls[] = {
SOC_SINGLE_EXT("TAS256X PLAYBACK VOLUME RIGHT", DVC_PCM, 2, 56, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM MAX ATTN RIGHT", LIM_MAX_ATN, 2, 15, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM THR MAX RIGHT", LIMB_TH_MAX,
2, 26, 0, tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM THR MIN RIGHT", LIMB_TH_MIN,
2, 26, 0, tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM INFLECTION POINT RIGHT", LIMB_INF_PT,
2, 40, 0, tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM SLOPE RIGHT", LIMB_SLOPE, 2, 6, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOP THR RIGHT", BOP_TH,
2, 15, 0, tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOSD THR RIGHT", BOSD_TH,
2, 15, 0, tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM ATTACT RATE RIGHT", LIMB_ATK_RT, 2, 7, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM RELEASE RATE RIGHT", LIMB_RLS_RT, 2, 7, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM ATTACK STEP RIGHT", LIMB_ATK_ST, 2, 3, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X LIM RELEASE STEP RIGHT", LIMB_RLS_ST, 2, 3, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOP ATTACK RATE RIGHT", BOP_ATK_RT, 2, 7, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOP ATTACK STEP RIGHT", BOP_ATK_ST, 2, 3, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOP HOLD TIME RIGHT", BOP_HLD_TM, 2, 7, 0,
tas256x_get, tas256x_put),
SOC_ENUM_EXT("TAS256X LIMITER SWITCH RIGHT", tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_ENUM_EXT("TAS256X BOP ENABLE RIGHT", tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_ENUM_EXT("TAS256X BOP MUTE RIGHT", tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_ENUM_EXT("TAS256X BROWNOUT SHUTDOWN RIGHT",
tas256x_rx_switch_enum[0], tas256x_enum_get, tas256x_enum_put),
SOC_SINGLE_EXT("TAS256X CLASSH TIMER RIGHT", CLASSH_TIMER, 2, 22, 0,
tas256x_get, tas256x_put),
SOC_ENUM_EXT("TAS256X RECIEVER ENABLE RIGHT", tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_SINGLE_EXT("TAS256X ICN THRESHOLD RIGHT", ICN_THR, 2, 84, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X ICN HYSTERISIS RIGHT", ICN_HYST, 2, 19, 0,
tas256x_get, tas256x_put),
SOC_ENUM_EXT("TAS256X NOISE GATE RIGHT", tas256x_rx_switch_enum[0],
tas256x_enum_get, tas256x_enum_put),
};
static char const *tas2564_vbat_lpf_text[] = {
"DISABLE", "HZ_10", "HZ_100", "KHZ_1"};
static const struct soc_enum tas2564_vbat_lpf_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2564_vbat_lpf_text),
tas2564_vbat_lpf_text),
};
static char const *tas2562_vbat_lpf_text[] = {
"DISABLE", "HZ_100", "KHZ_1", "KHZ_10"};
static const struct soc_enum tas2562_vbat_lpf_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2562_vbat_lpf_text),
tas2562_vbat_lpf_text),
};
static const struct snd_kcontrol_new tas2564_left_controls[] = {
SOC_ENUM_EXT("TAS256X RX MODE LEFT", tas2564_rx_mode_enum[0],
tas2564_get, tas2564_put),
SOC_ENUM_EXT("TAS256X VBAT LPF LEFT", tas2564_vbat_lpf_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_SINGLE_EXT("TAS256X BOOST VOLTAGE LEFT", BST_VREG, 1, 15, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOOST CURRENT LEFT", BST_ILIM, 1, 63, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X AMP OUTPUT LVL LEFT", AMPOUTPUT_LVL, 1, 0x1C, 0,
tas256x_get, tas256x_put),
};
static const struct snd_kcontrol_new tas2564_right_controls[] = {
SOC_ENUM_EXT("TAS256X RX MODE RIGHT", tas2564_rx_mode_enum[0],
tas2564_get, tas2564_put),
SOC_ENUM_EXT("TAS256X VBAT LPF RIGHT", tas2564_vbat_lpf_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_SINGLE_EXT("TAS256X BOOST VOLTAGE RIGHT", BST_VREG, 2, 15, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOOST CURRENT RIGHT", BST_ILIM, 2, 63, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X AMP OUTPUT LVL RIGHT", AMPOUTPUT_LVL, 2, 0x1C, 0,
tas256x_get, tas256x_put),
};
static const struct snd_kcontrol_new tas2562_left_controls[] = {
SOC_ENUM_EXT("TAS256X VBAT LPF LEFT", tas2562_vbat_lpf_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_SINGLE_EXT("TAS256X BOOST VOLTAGE LEFT", BST_VREG, 1, 12, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOOST CURRENT LEFT", BST_ILIM, 1, 55, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X AMP OUTPUT LVL LEFT", AMPOUTPUT_LVL, 1, 0x1C, 0,
tas256x_get, tas256x_put),
};
static const struct snd_kcontrol_new tas2562_right_controls[] = {
SOC_ENUM_EXT("TAS256X VBAT LPF RIGHT", tas2562_vbat_lpf_enum[0],
tas256x_enum_get, tas256x_enum_put),
SOC_SINGLE_EXT("TAS256X BOOST VOLTAGE RIGHT", BST_VREG, 2, 12, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X BOOST CURRENT RIGHT", BST_ILIM, 2, 55, 0,
tas256x_get, tas256x_put),
SOC_SINGLE_EXT("TAS256X AMP OUTPUT LVL RIGHT", AMPOUTPUT_LVL, 2, 0x1C, 0,
tas256x_get, tas256x_put),
};
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
static int tas2564_probe(struct tas256x_priv *p_tas256x,
struct snd_soc_component *codec, int chn)
{
int ret = -1;
struct linux_platform *plat_data = NULL;
if ((!p_tas256x) || (!codec)) {
pr_err("tas256x:%s p_tas256x or codec is Null\n", __func__);
return ret;
}
plat_data = (struct linux_platform *) p_tas256x->platform_data;
dev_dbg(plat_data->dev, "%s channel %d", __func__, chn);
tas256x_update_default_params(p_tas256x, chn);
if (chn == channel_left) {
ret = snd_soc_add_component_controls(codec, tas256x_left_controls,
ARRAY_SIZE(tas256x_left_controls));
ret = snd_soc_add_component_controls(codec, tas2564_left_controls,
ARRAY_SIZE(tas2564_left_controls));
} else if (chn == channel_right) {
ret = snd_soc_add_component_controls(codec, tas256x_right_controls,
ARRAY_SIZE(tas256x_right_controls));
ret = snd_soc_add_component_controls(codec, tas2564_right_controls,
ARRAY_SIZE(tas2564_right_controls));
} else {
dev_err(plat_data->dev, "Invalid Channel %d\n", chn);
}
return ret;
}
#else
static int tas2564_probe(struct tas256x_priv *p_tas256x,
struct snd_soc_codec *codec, int chn)
{
int ret = -1;
struct linux_platform *plat_data = NULL;
if ((!p_tas256x) || (!codec)) {
pr_err("tas256x:%s p_tas256x or codec is Null\n", __func__);
return ret;
}
plat_data = (struct linux_platform *) p_tas256x->platform_data;
dev_dbg(plat_data->dev, "%s channel %d", __func__, chn);
tas256x_update_default_params(p_tas256x, chn);
if (chn == channel_left) {
ret = snd_soc_add_codec_controls(codec, tas256x_left_controls,
ARRAY_SIZE(tas256x_left_controls));
ret = snd_soc_add_codec_controls(codec, tas2564_left_controls,
ARRAY_SIZE(tas2564_left_controls));
} else if (chn == channel_right) {
ret = snd_soc_add_codec_controls(codec, tas256x_right_controls,
ARRAY_SIZE(tas256x_right_controls));
ret = snd_soc_add_codec_controls(codec, tas2564_right_controls,
ARRAY_SIZE(tas2564_right_controls));
} else {
dev_err(plat_data->dev, "Invalid Channel %d\n", chn);
}
return ret;
}
#endif
#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
static int tas2562_probe(struct tas256x_priv *p_tas256x,
struct snd_soc_component *codec, int chn)
{
struct linux_platform *plat_data = NULL;
int ret = -1;
if ((!p_tas256x) || (!codec)) {
pr_err("tas256x:%s p_tas256x or codec is Null\n", __func__);
return ret;
}
plat_data = (struct linux_platform *) p_tas256x->platform_data;
dev_dbg(plat_data->dev, "%s channel %d", __func__, chn);
tas256x_update_default_params(p_tas256x, chn);
if (chn == channel_left) {
ret = snd_soc_add_component_controls(codec, tas256x_left_controls,
ARRAY_SIZE(tas256x_left_controls));
ret = snd_soc_add_component_controls(codec, tas2562_left_controls,
ARRAY_SIZE(tas2562_left_controls));
} else if (chn == channel_right) {
ret = snd_soc_add_component_controls(codec, tas256x_right_controls,
ARRAY_SIZE(tas256x_right_controls));
ret = snd_soc_add_component_controls(codec, tas2562_right_controls,
ARRAY_SIZE(tas2562_right_controls));
} else {
dev_err(plat_data->dev, "Invalid Channel %d\n", chn);
}
return ret;
}
#else
static int tas2562_probe(struct tas256x_priv *p_tas256x,
struct snd_soc_codec *codec, int chn)
{
struct linux_platform *plat_data = NULL;
int ret = -1;
if ((!p_tas256x) || (!codec)) {
pr_err("tas256x:%s p_tas256x or codec is Null\n", __func__);
return ret;
}
plat_data = (struct linux_platform *) p_tas256x->platform_data;
dev_dbg(plat_data->dev, "%s channel %d", __func__, chn);
tas256x_update_default_params(p_tas256x, chn);
if (chn == channel_left) {
ret = snd_soc_add_codec_controls(codec, tas256x_left_controls,
ARRAY_SIZE(tas256x_left_controls));
ret = snd_soc_add_codec_controls(codec, tas2562_left_controls,
ARRAY_SIZE(tas2562_left_controls));
} else if (chn == channel_right) {
ret = snd_soc_add_codec_controls(codec, tas256x_right_controls,
ARRAY_SIZE(tas256x_right_controls));
ret = snd_soc_add_codec_controls(codec, tas2562_right_controls,
ARRAY_SIZE(tas2562_right_controls));
} else {
dev_err(plat_data->dev, "Invalid Channel %d\n", chn);
}
return ret;
}
#endif
static bool tas256x_volatile(struct device *dev, unsigned int reg)
{
return true;
}
static bool tas256x_writeable(struct device *dev, unsigned int reg)
{
return true;
}
static const struct regmap_config tas256x_i2c_regmap = {
.reg_bits = 8,
.val_bits = 8,
.writeable_reg = tas256x_writeable,
.volatile_reg = tas256x_volatile,
.cache_type = REGCACHE_NONE,
.max_register = 1 * 128,
};
static void tas256x_hw_reset(struct tas256x_priv *p_tas256x)
{
struct linux_platform *plat_data = NULL;
int i = 0;
plat_data = (struct linux_platform *) p_tas256x->platform_data;
for (i = 0; i < p_tas256x->mn_channels; i++) {
if (gpio_is_valid(p_tas256x->devs[i]->mn_reset_gpio)) {
gpio_direction_output(
p_tas256x->devs[i]->mn_reset_gpio, 0);
}
}
msleep(20);
for (i = 0; i < p_tas256x->mn_channels; i++) {
if (gpio_is_valid(p_tas256x->devs[i]->mn_reset_gpio)) {
gpio_direction_output(
p_tas256x->devs[i]->mn_reset_gpio, 1);
}
p_tas256x->devs[i]->mn_current_book = -1;
p_tas256x->devs[i]->mn_current_page = -1;
}
msleep(20);
dev_info(plat_data->dev, "reset gpio up !!\n");
}
static void tas256x_enable_irq(struct tas256x_priv *p_tas256x, bool enable)
{
static int irq_enabled[2] = {0};
struct irq_desc *desc = NULL;
struct linux_platform *plat_data = NULL;
int i = 0;
plat_data = (struct linux_platform *) p_tas256x->platform_data;
if (enable) {
if (p_tas256x->mb_irq_eable)
return;
for (i = 0; i < p_tas256x->mn_channels; i++) {
if (gpio_is_valid(p_tas256x->devs[i]->mn_irq_gpio) &&
irq_enabled[i] == 0) {
desc = irq_data_to_desc(irq_get_irq_data(
p_tas256x->devs[i]->mn_irq));
dev_dbg(plat_data->dev, "### enable-irq %d, irq desc=%p, depth=%d",
p_tas256x->devs[i]->mn_irq, desc, desc ? desc->depth : 0);
if (desc && desc->depth > 0)
enable_irq(p_tas256x->devs[i]->mn_irq);
else
dev_info(plat_data->dev,
"### irq already enabled");
irq_enabled[i] = 1;
}
}
p_tas256x->mb_irq_eable = true;
} else {
for (i = 0; i < p_tas256x->mn_channels; i++) {
if (gpio_is_valid(p_tas256x->devs[i]->mn_irq_gpio)
&& irq_enabled[i] == 1) {
desc = irq_data_to_desc(irq_get_irq_data(
p_tas256x->devs[i]->mn_irq));
dev_dbg(plat_data->dev, "### disable-irq %d, irq desc=%p, depth=%d",
p_tas256x->devs[i]->mn_irq, desc, desc ? desc->depth : 0);
disable_irq_nosync(p_tas256x->devs[i]->mn_irq);
irq_enabled[i] = 0;
}
}
p_tas256x->mb_irq_eable = false;
}
}
static void irq_work_routine(struct work_struct *work)
{
struct tas256x_priv *p_tas256x =
container_of(work, struct tas256x_priv, irq_work.work);
struct linux_platform *plat_data = NULL;
plat_data = (struct linux_platform *) p_tas256x->platform_data;
dev_info(plat_data->dev, "%s\n", __func__);
#if IS_ENABLED(CONFIG_TAS256X_CODEC)
mutex_lock(&p_tas256x->codec_lock);
#endif
if (plat_data->mb_runtime_suspend) {
pr_info("%s, Runtime Suspended\n", __func__);
goto end;
}
/*Logical Layer IRQ function, return is ignored*/
tas256x_irq_work_func(p_tas256x);
end:
#if IS_ENABLED(CONFIG_TAS256X_CODEC)
mutex_unlock(&p_tas256x->codec_lock);
#endif
return;
}
static void init_work_routine(struct work_struct *work)
{
struct tas256x_priv *p_tas256x =
container_of(work, struct tas256x_priv, init_work.work);
#if IS_ENABLED(CONFIG_TAS256X_CODEC)
mutex_lock(&p_tas256x->codec_lock);
#endif
/*Init Work Function. return is ignored*/
tas256x_init_work_func(p_tas256x);
#if IS_ENABLED(CONFIG_TAS256X_CODEC)
mutex_unlock(&p_tas256x->codec_lock);
#endif
}
static irqreturn_t tas256x_irq_handler(int irq, void *dev_id)
{
struct tas256x_priv *p_tas256x = (struct tas256x_priv *)dev_id;
/* get IRQ status after 100 ms */
schedule_delayed_work(&p_tas256x->irq_work, msecs_to_jiffies(100));
return IRQ_HANDLED;
}
static int tas256x_runtime_suspend(struct tas256x_priv *p_tas256x)
{
struct linux_platform *plat_data = NULL;
plat_data = (struct linux_platform *) p_tas256x->platform_data;
dev_dbg(plat_data->dev, "%s\n", __func__);
plat_data->mb_runtime_suspend = true;
if (delayed_work_pending(&p_tas256x->irq_work)) {
dev_dbg(plat_data->dev, "cancel IRQ work\n");
cancel_delayed_work(&p_tas256x->irq_work);
}
return 0;
}
static int tas256x_runtime_resume(struct tas256x_priv *p_tas256x)
{
struct linux_platform *plat_data = NULL;
plat_data = (struct linux_platform *) p_tas256x->platform_data;
dev_dbg(plat_data->dev, "%s\n", __func__);
plat_data->mb_runtime_suspend = false;
return 0;
}
static int tas256x_pm_suspend(struct device *dev)
{
struct tas256x_priv *p_tas256x = dev_get_drvdata(dev);
if (!p_tas256x) {
dev_err(dev, "drvdata is NULL\n");
return -EINVAL;
}
mutex_lock(&p_tas256x->codec_lock);
tas256x_runtime_suspend(p_tas256x);
mutex_unlock(&p_tas256x->codec_lock);
return 0;
}
static int tas256x_pm_resume(struct device *dev)
{
struct tas256x_priv *p_tas256x = dev_get_drvdata(dev);
if (!p_tas256x) {
dev_err(dev, "drvdata is NULL\n");
return -EINVAL;
}
mutex_lock(&p_tas256x->codec_lock);
tas256x_runtime_resume(p_tas256x);
mutex_unlock(&p_tas256x->codec_lock);
return 0;
}
#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
#if IS_ENABLED(CONFIG_PLATFORM_QCOM)
struct tas256x_priv *g_p_tas256x;
void tas256x_software_reset(void *prv_data)
{
pr_err("[TI-SmartPA:%s]\n", __func__);
schedule_delayed_work(&g_p_tas256x->dc_work, msecs_to_jiffies(10));
}
static void dc_work_routine(struct work_struct *work)
{
struct tas256x_priv *p_tas256x =
container_of(work, struct tas256x_priv, dc_work.work);
struct linux_platform *plat_data = NULL;
plat_data = (struct linux_platform *) p_tas256x->platform_data;
#if IS_ENABLED(CONFIG_TAS256X_CODEC)
mutex_lock(&p_tas256x->codec_lock);
#endif
tas256x_dc_work_func(p_tas256x, s_dc_detect.channel);
#if IS_ENABLED(CONFIG_TAS256X_CODEC)
mutex_unlock(&p_tas256x->codec_lock);
#endif
}
#endif/*CONFIG_PLATFORM_QCOM*/
#endif /*CONFIG_TAS25XX_ALGO*/
static void schedule_init_work(struct tas256x_priv *p_tas256x)
{
schedule_delayed_work(&p_tas256x->init_work, msecs_to_jiffies(50));
}
static int tas256x_parse_dt(struct device *dev,
struct tas256x_priv *p_tas256x)
{
struct device_node *np = dev->of_node;
int rc = 0, i = 0;
struct linux_platform *plat_data = NULL;
plat_data = (struct linux_platform *) p_tas256x->platform_data;
rc = of_property_read_u32(np, "ti,channels", &p_tas256x->mn_channels);
if (rc) {
dev_err(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
"ti,channels", np->full_name, rc);
goto EXIT;
} else {
dev_dbg(plat_data->dev, "ti,channels=%d",
p_tas256x->mn_channels);
}
/*the device structures array*/
p_tas256x->devs =
kmalloc(p_tas256x->mn_channels * sizeof(struct tas_device *),
GFP_KERNEL);
for (i = 0; i < p_tas256x->mn_channels; i++) {
p_tas256x->devs[i] = kmalloc(sizeof(struct tas_device),
GFP_KERNEL);
if (p_tas256x->devs[i] == NULL) {
dev_err(plat_data->dev,
"%s:%u:kmalloc failed!\n", __func__, __LINE__);
rc = -1;
break;
}
rc = of_property_read_u32(np, dts_tag[i][0],
&p_tas256x->devs[i]->mn_addr);
if (rc) {
dev_err(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
dts_tag[i][0], np->full_name, rc);
break;
} else {
dev_dbg(plat_data->dev, "%s = 0x%02x",
dts_tag[i][0], p_tas256x->devs[i]->mn_addr);
}
p_tas256x->devs[i]->mn_reset_gpio =
of_get_named_gpio(np, dts_tag[i][1], 0);
if (!gpio_is_valid(p_tas256x->devs[i]->mn_reset_gpio))
dev_err(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
dts_tag[i][1], np->full_name,
p_tas256x->devs[i]->mn_reset_gpio);
else
dev_dbg(plat_data->dev, "%s = %d",
dts_tag[i][1],
p_tas256x->devs[i]->mn_reset_gpio);
p_tas256x->devs[i]->mn_irq_gpio =
of_get_named_gpio(np, dts_tag[i][2], 0);
if (!gpio_is_valid(p_tas256x->devs[i]->mn_irq_gpio)) {
dev_err(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
dts_tag[i][2], np->full_name,
p_tas256x->devs[i]->mn_irq_gpio);
} else {
dev_dbg(plat_data->dev, "%s = %d",
dts_tag[i][2],
p_tas256x->devs[i]->mn_irq_gpio);
}
p_tas256x->devs[i]->spk_control = 1;
}
if (rc)
goto EXIT;
rc = of_property_read_u32(np, "ti,frame-start", &p_tas256x->mn_frame_start);
if (rc) {
dev_info(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
"ti,frame-start", np->full_name, rc);
p_tas256x->mn_frame_start = 0;
} else {
dev_dbg(plat_data->dev, "ti,frame-start=0x%x",
p_tas256x->mn_frame_start);
}
rc = of_property_read_u32(np, "ti,rx-offset", &p_tas256x->mn_rx_offset);
if (rc) {
dev_info(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
"ti,rx-offset", np->full_name, rc);
p_tas256x->mn_rx_offset = 1;
} else {
dev_dbg(plat_data->dev, "ti,rx-offset=0x%x",
p_tas256x->mn_rx_offset);
}
rc = of_property_read_u32(np, "ti,rx-edge", &p_tas256x->mn_rx_edge);
if (rc) {
dev_info(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
"ti,rx-edge", np->full_name, rc);
p_tas256x->mn_rx_edge = 1;
} else {
dev_dbg(plat_data->dev, "ti,rx-edge=0x%x",
p_tas256x->mn_rx_edge);
}
rc = of_property_read_u32(np, "ti,tx-offset", &p_tas256x->mn_tx_offset);
if (rc) {
dev_info(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
"ti,tx-offset", np->full_name, rc);
p_tas256x->mn_tx_offset = 0;
} else {
dev_dbg(plat_data->dev, "ti,tx-offset=0x%x",
p_tas256x->mn_tx_offset);
}
rc = of_property_read_u32(np, "ti,tx-edge", &p_tas256x->mn_tx_edge);
if (rc) {
dev_info(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
"ti,tx-edge", np->full_name, rc);
p_tas256x->mn_tx_edge = 0;
} else {
dev_dbg(plat_data->dev, "ti,tx-edge=0x%x",
p_tas256x->mn_tx_edge);
}
rc = of_property_read_u32(np, "ti,iv-width", &p_tas256x->mn_iv_width);
if (rc) {
dev_err(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
"ti,iv-width", np->full_name, rc);
} else {
dev_dbg(plat_data->dev, "ti,iv-width=0x%x",
p_tas256x->mn_iv_width);
}
rc = of_property_read_u32(np, "ti,vbat-mon", &p_tas256x->mn_vbat);
if (rc) {
dev_err(plat_data->dev,
"Looking up %s property in node %s failed %d\n",
"ti,vbat-mon", np->full_name, rc);
} else {
dev_dbg(plat_data->dev, "ti,vbat-mon=0x%x",
p_tas256x->mn_vbat);
}
#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
/* Needs to be enabled always */
tas25xx_parse_algo_dt(np);
#endif /*CONFIG_TAS25XX_ALGO*/
EXIT:
return rc;
}
static int tas256x_i2c_probe(struct i2c_client *p_client,
const struct i2c_device_id *id)
{
struct tas256x_priv *p_tas256x;
struct linux_platform *plat_data;
int n_result = 0;
int i = 0;
dev_info(&p_client->dev, "Driver Tag: %s\n", TAS256X_DRIVER_TAG);
dev_info(&p_client->dev, "%s enter\n", __func__);
p_tas256x = devm_kzalloc(&p_client->dev,
sizeof(struct tas256x_priv), GFP_KERNEL);
if (p_tas256x == NULL) {
dev_err(&p_client->dev, "failed to allocate memory\n");
n_result = -ENOMEM;
goto err;
}
plat_data = (struct linux_platform *)devm_kzalloc(&p_client->dev,
sizeof(struct linux_platform), GFP_KERNEL);
if (p_tas256x == NULL) {
dev_err(&p_client->dev, "failed to allocate memory\n");
n_result = -ENOMEM;
goto err;
}
p_tas256x->platform_data = plat_data;
#if IS_ENABLED(CONFIG_TAS256X_REGBIN_PARSER)
p_tas256x->profile_cfg_id = -1;
#endif
p_tas256x->plat_write = tas256x_regmap_write;
p_tas256x->plat_read = tas256x_regmap_read;
p_tas256x->plat_bulk_write = tas256x_regmap_bulk_write;
p_tas256x->plat_bulk_read = tas256x_regmap_bulk_read;
p_tas256x->plat_update_bits = tas256x_regmap_update_bits;
plat_data->client = p_client;
plat_data->dev = &p_client->dev;
i2c_set_clientdata(p_client, p_tas256x);
dev_set_drvdata(&p_client->dev, p_tas256x);
plat_data->regmap = devm_regmap_init_i2c(p_client,
&tas256x_i2c_regmap);
if (IS_ERR(plat_data->regmap)) {
n_result = PTR_ERR(plat_data->regmap);
dev_err(&p_client->dev,
"Failed to allocate register map: %d\n",
n_result);
goto err;
}
mutex_init(&p_tas256x->dev_lock);
p_tas256x->hw_reset = tas256x_hw_reset;
p_tas256x->enable_irq = tas256x_enable_irq;
p_tas256x->schedule_init_work = schedule_init_work;
#if IS_ENABLED(CODEC_PM)
plat_data->runtime_suspend = tas256x_runtime_suspend;
plat_data->runtime_resume = tas256x_runtime_resume;
plat_data->mn_power_state = TAS256X_POWER_SHUTDOWN;
#endif
if (p_client->dev.of_node)
tas256x_parse_dt(&p_client->dev, p_tas256x);
for (i = 0; i < p_tas256x->mn_channels; i++) {
if (gpio_is_valid(p_tas256x->devs[i]->mn_reset_gpio)) {
n_result = gpio_request(
p_tas256x->devs[i]->mn_reset_gpio,
reset_gpio_label[i]);
if (n_result) {
dev_err(plat_data->dev,
"%s: Failed to request gpio %d\n",
__func__,
p_tas256x->devs[i]->mn_reset_gpio);
n_result = -EINVAL;
goto err;
}
}
}
n_result = tas256x_register_device(p_tas256x);
if (n_result < 0)
goto err;
for (i = 0; i < p_tas256x->mn_channels; i++) {
switch (p_tas256x->devs[i]->device_id) {
case DEVICE_TAS2562:
p_tas256x->devs[i]->dev_ops.tas_probe = tas2562_probe;
break;
case DEVICE_TAS2564:
p_tas256x->devs[i]->dev_ops.tas_probe = tas2564_probe;
break;
default:
p_tas256x->devs[i]->dev_ops.tas_probe = NULL;
break;
}
}
INIT_DELAYED_WORK(&p_tas256x->irq_work, irq_work_routine);
for (i = 0; i < p_tas256x->mn_channels; i++) {
if (gpio_is_valid(p_tas256x->devs[i]->mn_irq_gpio)) {
n_result =
gpio_request(
p_tas256x->devs[i]->mn_irq_gpio,
irq_gpio_label[i]);
if (n_result < 0) {
dev_err(plat_data->dev,
"%s:%u: ch 0x%02x: GPIO %d request error\n",
__func__, __LINE__,
p_tas256x->devs[i]->mn_addr,
p_tas256x->devs[i]->mn_irq_gpio);
goto err;
}
gpio_direction_input(p_tas256x->devs[i]->mn_irq_gpio);
/*tas256x_dev_write(p_tas256x,
* (i == 0)? channel_left : channel_right,
* TAS256X_MISCCONFIGURATIONREG0, 0xce);
*/
p_tas256x->devs[i]->mn_irq =
gpio_to_irq(p_tas256x->devs[i]->mn_irq_gpio);
dev_info(plat_data->dev, "irq = %d\n",
p_tas256x->devs[i]->mn_irq);
n_result = request_threaded_irq(
p_tas256x->devs[i]->mn_irq,
tas256x_irq_handler,
NULL,
IRQF_TRIGGER_FALLING|IRQF_ONESHOT,
p_client->name, p_tas256x);
if (n_result < 0) {
dev_err(plat_data->dev,
"request_irq failed, %d\n", n_result);
goto err;
}
disable_irq_nosync(p_tas256x->devs[i]->mn_irq);
}
}
tas256x_enable_irq(p_tas256x, true);
INIT_DELAYED_WORK(&p_tas256x->init_work, init_work_routine);
#if IS_ENABLED(CONFIG_TAS256X_CODEC)
mutex_init(&p_tas256x->codec_lock);
n_result = tas256x_register_codec(p_tas256x);
if (n_result < 0) {
dev_err(plat_data->dev,
"register codec failed, %d\n", n_result);
goto err;
}
#endif
#if IS_ENABLED(CONFIG_TAS256X_MISC)
mutex_init(&p_tas256x->file_lock);
n_result = tas256x_register_misc(p_tas256x);
if (n_result < 0) {
dev_err(plat_data->dev, "register codec failed %d\n",
n_result);
goto err;
}
#endif
#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
#if IS_ENABLED(CONFIG_PLATFORM_QCOM)
INIT_DELAYED_WORK(&p_tas256x->dc_work, dc_work_routine);
g_p_tas256x = p_tas256x;
register_tas256x_reset_func(tas256x_software_reset, &s_dc_detect);
#endif /*CONFIG_PLATFORM_QCOM*/
#endif /*CONFIG_TAS25XX_ALGO*/
err:
return n_result;
}
static void tas256x_i2c_remove(struct i2c_client *p_client)
{
int i = 0;
struct tas256x_priv *p_tas256x = i2c_get_clientdata(p_client);
struct linux_platform *plat_data = NULL;
plat_data = (struct linux_platform *) p_tas256x->platform_data;
dev_info(plat_data->dev, "%s\n", __func__);
/*Cancel all the work routine before exiting*/
cancel_delayed_work_sync(&p_tas256x->irq_work);
cancel_delayed_work_sync(&p_tas256x->init_work);
cancel_delayed_work_sync(&p_tas256x->dc_work);
#if IS_ENABLED(CONFIG_TAS256X_CODEC)
tas256x_deregister_codec(p_tas256x);
mutex_destroy(&p_tas256x->codec_lock);
#endif
#if IS_ENABLED(CONFIG_TAS256X_MISC)
tas256x_deregister_misc(p_tas256x);
mutex_destroy(&p_tas256x->file_lock);
#endif
mutex_destroy(&p_tas256x->dev_lock);
for (i = 0; i < p_tas256x->mn_channels; i++) {
if (gpio_is_valid(p_tas256x->devs[i]->mn_reset_gpio))
gpio_free(p_tas256x->devs[i]->mn_reset_gpio);
if (gpio_is_valid(p_tas256x->devs[i]->mn_irq_gpio))
gpio_free(p_tas256x->devs[i]->mn_irq_gpio);
kfree(p_tas256x->devs[i]);
}
kfree(p_tas256x->devs);
}
static const struct i2c_device_id tas256x_i2c_id[] = {
{ "tas256x", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, tas256x_i2c_id);
#if defined(CONFIG_OF)
static const struct of_device_id tas256x_of_match[] = {
{ .compatible = "ti, tas256x" },
{ .compatible = "ti, tas2562" },
{ .compatible = "ti, tas2564" },
{},
};
MODULE_DEVICE_TABLE(of, tas256x_of_match);
#endif
static const struct dev_pm_ops tas256x_pm_ops = {
.suspend = tas256x_pm_suspend,
.resume = tas256x_pm_resume
};
static struct i2c_driver tas256x_i2c_driver = {
.driver = {
.name = "tas256x",
.owner = THIS_MODULE,
#if defined(CONFIG_OF)
.of_match_table = of_match_ptr(tas256x_of_match),
#endif
.pm = &tas256x_pm_ops,
},
.probe = tas256x_i2c_probe,
.remove = tas256x_i2c_remove,
.id_table = tas256x_i2c_id,
};
module_i2c_driver(tas256x_i2c_driver);
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("TAS256X I2C Smart Amplifier driver");
MODULE_LICENSE("GPL v2");
#endif