Browse Source

Merge "asoc: codecs: Fix recovery behavior for CSRA66X0"

Linux Build Service Account 6 years ago
parent
commit
b5e037bd5e
1 changed files with 197 additions and 4 deletions
  1. 197 4
      asoc/codecs/csra66x0/csra66x0.c

+ 197 - 4
asoc/codecs/csra66x0/csra66x0.c

@@ -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, &param1[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;