Merge "asoc: codecs: Fix recovery behavior for CSRA66X0"
This commit is contained in:
commit
b5e037bd5e
1 changed files with 197 additions and 4 deletions
|
@ -23,6 +23,8 @@
|
|||
#include <sound/soc.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include "csra66x0.h"
|
||||
|
||||
/* CSRA66X0 register default values */
|
||||
|
@ -258,8 +260,137 @@ struct csra66x0_priv {
|
|||
u32 irq_active_low;
|
||||
u32 in_cluster;
|
||||
u32 is_master;
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
struct dentry *debugfs_dir;
|
||||
struct dentry *debugfs_file_wo;
|
||||
struct dentry *debugfs_file_ro;
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
static int debugfs_codec_open_op(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int debugfs_get_parameters(char *buf, u32 *param1, int num_of_par)
|
||||
{
|
||||
char *token;
|
||||
int base, cnt;
|
||||
|
||||
token = strsep(&buf, " ");
|
||||
for (cnt = 0; cnt < num_of_par; cnt++) {
|
||||
if (token) {
|
||||
if ((token[1] == 'x') || (token[1] == 'X'))
|
||||
base = 16;
|
||||
else
|
||||
base = 10;
|
||||
|
||||
if (kstrtou32(token, base, ¶m1[cnt]) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
token = strsep(&buf, " ");
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t debugfs_codec_write_op(struct file *filp,
|
||||
const char __user *ubuf, size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct csra66x0_priv *csra66x0 =
|
||||
(struct csra66x0_priv *) filp->private_data;
|
||||
struct snd_soc_codec *codec = csra66x0->codec;
|
||||
char lbuf[32];
|
||||
int rc;
|
||||
u32 param[2];
|
||||
|
||||
if (!filp || !ppos || !ubuf)
|
||||
return -EINVAL;
|
||||
if (cnt > sizeof(lbuf) - 1)
|
||||
return -EINVAL;
|
||||
rc = copy_from_user(lbuf, ubuf, cnt);
|
||||
if (rc)
|
||||
return -EFAULT;
|
||||
lbuf[cnt] = '\0';
|
||||
rc = debugfs_get_parameters(lbuf, param, 2);
|
||||
if ((param[0] < CSRA66X0_AUDIO_IF_RX_CONFIG1)
|
||||
|| (param[0] > CSRA66X0_MAX_REGISTER_ADDR)) {
|
||||
dev_err(codec->dev, "%s: register address 0x%04X out of range\n",
|
||||
__func__, param[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((param[1] < 0) || (param[1] > 255)) {
|
||||
dev_err(codec->dev, "%s: register data 0x%02X out of range\n",
|
||||
__func__, param[1]);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (rc == 0)
|
||||
{
|
||||
rc = cnt;
|
||||
dev_info(codec->dev, "%s: reg[0x%04X]=0x%02X\n",
|
||||
__func__, param[0], param[1]);
|
||||
snd_soc_write(codec, param[0], param[1]);
|
||||
} else {
|
||||
dev_err(codec->dev, "%s: write to register addr=0x%04X failed\n",
|
||||
__func__, param[0]);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t debugfs_csra66x0_reg_show(struct snd_soc_codec *codec,
|
||||
char __user *ubuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
int i, reg_val, len;
|
||||
ssize_t total = 0;
|
||||
char tmp_buf[20];
|
||||
|
||||
if (!ubuf || !ppos || !codec || *ppos < 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = ((int) *ppos + CSRA66X0_BASE);
|
||||
i <= CSRA66X0_MAX_REGISTER_ADDR; i++) {
|
||||
reg_val = snd_soc_read(codec, i);
|
||||
len = snprintf(tmp_buf, 20, "0x%04X: 0x%02X\n", i, (reg_val & 0xFF));
|
||||
if ((total + len) >= count - 1)
|
||||
break;
|
||||
if (copy_to_user((ubuf + total), tmp_buf, len)) {
|
||||
dev_err(codec->dev, "%s: fail to copy reg dump\n", __func__);
|
||||
total = -EFAULT;
|
||||
goto copy_err;
|
||||
}
|
||||
*ppos += len;
|
||||
total += len;
|
||||
}
|
||||
|
||||
copy_err:
|
||||
return total;
|
||||
}
|
||||
|
||||
static ssize_t debugfs_codec_read_op(struct file *filp,
|
||||
char __user *ubuf, size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct csra66x0_priv *csra66x0 =
|
||||
(struct csra66x0_priv *) filp->private_data;
|
||||
struct snd_soc_codec *codec = csra66x0->codec;
|
||||
ssize_t ret_cnt;
|
||||
|
||||
if (!filp || !ppos || !ubuf || *ppos < 0)
|
||||
return -EINVAL;
|
||||
ret_cnt = debugfs_csra66x0_reg_show(codec, ubuf, cnt, ppos);
|
||||
return ret_cnt;
|
||||
}
|
||||
|
||||
static const struct file_operations debugfs_codec_ops = {
|
||||
.open = debugfs_codec_open_op,
|
||||
.write = debugfs_codec_write_op,
|
||||
.read = debugfs_codec_read_op,
|
||||
};
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
/*
|
||||
* CSRA66X0 Controls
|
||||
*/
|
||||
|
@ -442,7 +573,7 @@ static int csra66x0_soc_probe(struct snd_soc_codec *codec)
|
|||
|
||||
if (csra66x0->in_cluster) {
|
||||
dapm = snd_soc_codec_get_dapm(codec);
|
||||
dev_dbg(codec->dev, "%s: setting %s to codec %s\n",
|
||||
dev_dbg(codec->dev, "%s: assign prefix %s to codec device %s\n",
|
||||
__func__, codec->component.name_prefix,
|
||||
codec->component.name);
|
||||
snd_soc_write(codec, CSRA66X0_CHIP_STATE_CTRL_FA,
|
||||
|
@ -580,6 +711,31 @@ static irqreturn_t csra66x0_irq(int irq, void *data)
|
|||
|
||||
/* clear fault state and re-init */
|
||||
snd_soc_write(codec, CSRA66X0_FAULT_STATUS_FA, 0x00);
|
||||
snd_soc_write(codec, CSRA66X0_IRQ_OUTPUT_STATUS_FA, 0x00);
|
||||
/* apply reset to CSRA66X0 */
|
||||
val = snd_soc_read(codec, CSRA66X0_MISC_CONTROL_STATUS_1_FA);
|
||||
snd_soc_write(codec, CSRA66X0_MISC_CONTROL_STATUS_1_FA, val | 0x08);
|
||||
/* wait 2s after reset to recover CSRA66X0 */
|
||||
msleep(2000);
|
||||
/* re-init */
|
||||
snd_soc_write(codec, CSRA66X0_CHIP_STATE_CTRL_FA,
|
||||
CONFIG_STATE);
|
||||
/* settle time in HW is min. 500ms before proceeding */
|
||||
msleep(500);
|
||||
snd_soc_write(codec, CSRA66X0_PIO7_SELECT, 0x04);
|
||||
snd_soc_write(codec, CSRA66X0_PIO8_SELECT, 0x04);
|
||||
if (csra66x0->is_master) {
|
||||
/* Master specific config */
|
||||
snd_soc_write(codec, CSRA66X0_PIO_PULL_EN0, 0xFF);
|
||||
snd_soc_write(codec, CSRA66X0_PIO_PULL_DIR0, 0x80);
|
||||
snd_soc_write(codec, CSRA66X0_PIO_PULL_EN1, 0x01);
|
||||
snd_soc_write(codec, CSRA66X0_PIO_PULL_DIR1, 0x01);
|
||||
} else {
|
||||
/* Slave specific config */
|
||||
snd_soc_write(codec, CSRA66X0_PIO_PULL_EN0, 0x7F);
|
||||
snd_soc_write(codec, CSRA66X0_PIO_PULL_EN1, 0x00);
|
||||
}
|
||||
snd_soc_write(codec, CSRA66X0_DCA_CTRL, 0x05);
|
||||
csra66x0_init(codec, csra66x0);
|
||||
} else {
|
||||
return IRQ_NONE;
|
||||
|
@ -599,6 +755,7 @@ static int csra66x0_i2c_probe(struct i2c_client *client_i2c,
|
|||
{
|
||||
struct csra66x0_priv *csra66x0;
|
||||
int ret, irq_trigger;
|
||||
char debugfs_dir_name[32];
|
||||
|
||||
csra66x0 = devm_kzalloc(&client_i2c->dev, sizeof(struct csra66x0_priv),
|
||||
GFP_KERNEL);
|
||||
|
@ -662,7 +819,7 @@ static int csra66x0_i2c_probe(struct i2c_client *client_i2c,
|
|||
}
|
||||
} else {
|
||||
gpio_direction_output(csra66x0->vreg_gpio, 1);
|
||||
gpio_set_value(csra66x0->vreg_gpio, 1);
|
||||
gpio_set_value(csra66x0->vreg_gpio, 0);
|
||||
}
|
||||
|
||||
/* register interrupt handle */
|
||||
|
@ -695,12 +852,45 @@ static int csra66x0_i2c_probe(struct i2c_client *client_i2c,
|
|||
}
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
/* debugfs interface */
|
||||
snprintf(debugfs_dir_name, sizeof(debugfs_dir_name), "%s-%s",
|
||||
client_i2c->name, dev_name(&client_i2c->dev));
|
||||
csra66x0->debugfs_dir = debugfs_create_dir(debugfs_dir_name, NULL);
|
||||
if (!csra66x0->debugfs_dir) {
|
||||
dev_dbg(&client_i2c->dev,
|
||||
"%s: Failed to create /sys/kernel/debug/%s for debugfs\n",
|
||||
__func__, debugfs_dir_name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
csra66x0->debugfs_file_wo = debugfs_create_file(
|
||||
"write_reg_val", S_IFREG | S_IRUGO, csra66x0->debugfs_dir,
|
||||
(void *) csra66x0,
|
||||
&debugfs_codec_ops);
|
||||
if (!csra66x0->debugfs_file_wo) {
|
||||
dev_dbg(&client_i2c->dev,
|
||||
"%s: Failed to create /sys/kernel/debug/%s/write_reg_val\n",
|
||||
__func__, debugfs_dir_name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
csra66x0->debugfs_file_ro = debugfs_create_file(
|
||||
"show_reg_dump", S_IFREG | S_IRUGO, csra66x0->debugfs_dir,
|
||||
(void *) csra66x0,
|
||||
&debugfs_codec_ops);
|
||||
if (!csra66x0->debugfs_file_ro) {
|
||||
dev_dbg(&client_i2c->dev,
|
||||
"%s: Failed to create /sys/kernel/debug/%s/show_reg_dump\n",
|
||||
__func__, debugfs_dir_name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
/* register codec */
|
||||
ret = snd_soc_register_codec(&client_i2c->dev,
|
||||
&soc_codec_drv_csra66x0, NULL, 0);
|
||||
if (ret != 0) {
|
||||
dev_err(&client_i2c->dev, "%s %d: Failed to register CODEC: %d\n",
|
||||
__func__, __LINE__, ret);
|
||||
dev_err(&client_i2c->dev, "%s %d: Failed to register codec: %d\n",
|
||||
__func__, __LINE__, ret);
|
||||
if (gpio_is_valid(csra66x0->vreg_gpio)) {
|
||||
gpio_set_value(csra66x0->vreg_gpio, 0);
|
||||
gpio_free(csra66x0->vreg_gpio);
|
||||
|
@ -719,6 +909,9 @@ static int csra66x0_i2c_remove(struct i2c_client *i2c_client)
|
|||
gpio_set_value(csra66x0->vreg_gpio, 0);
|
||||
gpio_free(csra66x0->vreg_gpio);
|
||||
}
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
debugfs_remove_recursive(csra66x0->debugfs_dir);
|
||||
#endif
|
||||
}
|
||||
snd_soc_unregister_codec(&i2c_client->dev);
|
||||
return 0;
|
||||
|
|
Loading…
Reference in a new issue