Forráskód Böngészése

asoc: codecs: No I2C activity when EP92 is inactive

Only trigger periodic I2C telegrams when EP92 is in logical power on state.
Rely on EP92 interrupt to notice state change when in power off state.
Fix arc_enable write operation.

Change-Id: Id36f90379280ca5c60984e2ed507844418a612d7
Signed-off-by: Ralf Herz <[email protected]>
Ralf Herz 5 éve
szülő
commit
cf16dedb1f
1 módosított fájl, 148 hozzáadás és 23 törlés
  1. 148 23
      asoc/codecs/ep92/ep92.c

+ 148 - 23
asoc/codecs/ep92/ep92.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/init.h>
@@ -25,6 +25,7 @@
 
 #define EP92_POLL_INTERVAL_OFF_MSEC 200
 #define EP92_POLL_INTERVAL_ON_MSEC  20
+#define EP92_POLL_RUNOUT_MSEC       5000
 #define EP92_SYSFS_ENTRY_MAX_LEN 64
 #define EP92_HYST_CNT 5
 
@@ -68,6 +69,9 @@ struct ep92_pdata {
 	struct timer_list    timer;
 	struct work_struct   read_status_worker;
 	int                  irq;
+	int                  poll_trig;
+	int                  poll_rem;
+	int                  force_inactive;
 
 	int                  hyst_tx_plug;
 	int                  hyst_link_on0;
@@ -648,10 +652,8 @@ static int ep92_probe(struct snd_soc_component *component)
 	ep92_init(component, ep92);
 
 	/* start polling when codec is registered */
-	if (ep92->irq == 0) {
-		mod_timer(&ep92->timer, jiffies +
-			msecs_to_jiffies(EP92_POLL_INTERVAL_OFF_MSEC));
-	}
+	mod_timer(&ep92->timer, jiffies +
+		msecs_to_jiffies(EP92_POLL_INTERVAL_OFF_MSEC));
 
 	return 0;
 }
@@ -690,6 +692,9 @@ void ep92_read_status(struct work_struct *work)
 	if (component == NULL)
 		return;
 
+	if (ep92->force_inactive)
+		return;
+
 	/* check ADO_CHF that is set when audio format has changed */
 	val = snd_soc_component_read32(component, EP92_BI_GENERAL_INFO_1);
 	if (val == 0xff) {
@@ -724,6 +729,10 @@ static irqreturn_t ep92_irq(int irq, void *data)
 
 	dev_dbg(component->dev, "ep92_interrupt\n");
 
+	ep92->poll_trig = 1;
+	mod_timer(&ep92->timer, jiffies +
+		msecs_to_jiffies(EP92_POLL_INTERVAL_ON_MSEC));
+
 	schedule_work(&ep92->read_status_worker);
 
 	return IRQ_HANDLED;
@@ -732,14 +741,45 @@ static irqreturn_t ep92_irq(int irq, void *data)
 void ep92_poll_status(struct timer_list *t)
 {
 	struct ep92_pdata *ep92 = from_timer(ep92, t, timer);
-	u32 poll_msec;
+	struct snd_soc_component *component = ep92->component;
 
-	if ((ep92->gc.ctl & EP92_GC_POWER_MASK) == 0)
-		poll_msec = EP92_POLL_INTERVAL_OFF_MSEC;
-	else
-		poll_msec = EP92_POLL_INTERVAL_ON_MSEC;
+	if (ep92->force_inactive)
+		return;
 
-	mod_timer(&ep92->timer, jiffies + msecs_to_jiffies(poll_msec));
+	/* if no IRQ is configured, always keep on polling */
+	if (ep92->irq == 0)
+		ep92->poll_rem = EP92_POLL_RUNOUT_MSEC;
+
+	/* on interrupt, start polling for some time */
+	if (ep92->poll_trig) {
+		if (ep92->poll_rem == 0)
+			dev_info(component->dev, "status checking activated\n");
+
+		ep92->poll_trig = 0;
+		ep92->poll_rem = EP92_POLL_RUNOUT_MSEC;
+	}
+
+	/*
+	 * If power_on == 0, poll only until poll_rem reaches zero and stop.
+	 * This allows to system to go to low power sleep mode.
+	 * Otherwise (power_on == 1) always re-arm timer to keep on polling.
+	 */
+	if ((ep92->gc.ctl & EP92_GC_POWER_MASK) == 0) {
+		if (ep92->poll_rem) {
+			mod_timer(&ep92->timer, jiffies +
+				msecs_to_jiffies(EP92_POLL_INTERVAL_OFF_MSEC));
+			if (ep92->poll_rem > EP92_POLL_INTERVAL_OFF_MSEC) {
+				ep92->poll_rem -= EP92_POLL_INTERVAL_OFF_MSEC;
+			} else {
+				dev_info(component->dev, "status checking stopped\n");
+				ep92->poll_rem = 0;
+			}
+		}
+	} else {
+		ep92->poll_rem = EP92_POLL_RUNOUT_MSEC;
+		mod_timer(&ep92->timer, jiffies +
+			msecs_to_jiffies(EP92_POLL_INTERVAL_ON_MSEC));
+	}
 
 	schedule_work(&ep92->read_status_worker);
 }
@@ -1162,6 +1202,11 @@ static ssize_t ep92_sysfs_wta_power(struct device *dev,
 	ep92->gc.ctl &= ~EP92_GC_POWER_MASK;
 	ep92->gc.ctl |= (val << EP92_GC_POWER_SHIFT) & EP92_GC_POWER_MASK;
 
+	if (val == 1) {
+		ep92->poll_trig = 1;
+		mod_timer(&ep92->timer, jiffies +
+			msecs_to_jiffies(EP92_POLL_INTERVAL_ON_MSEC));
+	}
 	rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN);
 end:
 	return rc;
@@ -1321,12 +1366,12 @@ static ssize_t ep92_sysfs_wta_arc_enable(struct device *dev,
 	}
 
 	reg = snd_soc_component_read32(ep92->component, EP92_GENERAL_CONTROL_0);
-	reg &= ~EP92_GC_AUDIO_PATH_MASK;
-	reg |= (val << EP92_GC_AUDIO_PATH_SHIFT) & EP92_GC_AUDIO_PATH_MASK;
+	reg &= ~EP92_GC_ARC_EN_MASK;
+	reg |= (val << EP92_GC_ARC_EN_SHIFT) & EP92_GC_ARC_EN_MASK;
 	snd_soc_component_write(ep92->component, EP92_GENERAL_CONTROL_0, reg);
-	ep92->gc.ctl &= ~EP92_GC_AUDIO_PATH_MASK;
-	ep92->gc.ctl |= (val << EP92_GC_AUDIO_PATH_SHIFT) &
-		EP92_GC_AUDIO_PATH_MASK;
+	ep92->gc.ctl &= ~EP92_GC_ARC_EN_MASK;
+	ep92->gc.ctl |= (val << EP92_GC_ARC_EN_SHIFT) &
+		EP92_GC_ARC_EN_MASK;
 
 	rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN);
 end:
@@ -1440,6 +1485,83 @@ end:
 	return rc;
 }
 
+static ssize_t ep92_sysfs_rda_runout(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	int val;
+	struct ep92_pdata *ep92 = dev_get_drvdata(dev);
+
+	if (!ep92 || !ep92->component) {
+		dev_err(dev, "%s: device error\n", __func__);
+		return -ENODEV;
+	}
+
+	val = ep92->poll_rem;
+
+	ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val);
+	dev_dbg(dev, "%s: '%d'\n", __func__, val);
+
+	return ret;
+}
+
+static ssize_t ep92_sysfs_rda_force_inactive(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	int val;
+	struct ep92_pdata *ep92 = dev_get_drvdata(dev);
+
+	if (!ep92 || !ep92->component) {
+		dev_err(dev, "%s: device error\n", __func__);
+		return -ENODEV;
+	}
+
+	val = ep92->force_inactive;
+
+	ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val);
+	dev_dbg(dev, "%s: '%d'\n", __func__, val);
+
+	return ret;
+}
+
+static ssize_t ep92_sysfs_wta_force_inactive(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int val, rc;
+	struct ep92_pdata *ep92 = dev_get_drvdata(dev);
+
+	if (!ep92 || !ep92->component) {
+		dev_err(dev, "%s: device error\n", __func__);
+		return -ENODEV;
+	}
+
+	rc = kstrtoint(buf, 10, &val);
+	if (rc) {
+		dev_err(dev, "%s: kstrtoint failed. rc=%d\n", __func__, rc);
+		goto end;
+	}
+	if ((val < 0) || (val > 1)) {
+		dev_err(dev, "%s: value out of range.\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (val == 0) {
+		ep92->force_inactive = 0;
+		ep92->poll_trig = 1;
+		mod_timer(&ep92->timer, jiffies +
+			msecs_to_jiffies(EP92_POLL_INTERVAL_ON_MSEC));
+	} else {
+		ep92->force_inactive = 1;
+		ep92->poll_rem = 0;
+	}
+
+	rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN);
+end:
+	return rc;
+}
+
 static DEVICE_ATTR(chipid, 0444, ep92_sysfs_rda_chipid, NULL);
 static DEVICE_ATTR(version, 0444, ep92_sysfs_rda_version, NULL);
 static DEVICE_ATTR(audio_state, 0444, ep92_sysfs_rda_audio_state, NULL);
@@ -1467,6 +1589,9 @@ static DEVICE_ATTR(cec_mute, 0644, ep92_sysfs_rda_cec_mute,
 	ep92_sysfs_wta_cec_mute);
 static DEVICE_ATTR(cec_volume, 0644, ep92_sysfs_rda_cec_volume,
 	ep92_sysfs_wta_cec_volume);
+static DEVICE_ATTR(runout, 0444, ep92_sysfs_rda_runout, NULL);
+static DEVICE_ATTR(force_inactive, 0644, ep92_sysfs_rda_force_inactive,
+	ep92_sysfs_wta_force_inactive);
 
 static struct attribute *ep92_fs_attrs[] = {
 	&dev_attr_chipid.attr,
@@ -1490,6 +1615,8 @@ static struct attribute *ep92_fs_attrs[] = {
 	&dev_attr_arc_enable.attr,
 	&dev_attr_cec_mute.attr,
 	&dev_attr_cec_volume.attr,
+	&dev_attr_runout.attr,
+	&dev_attr_force_inactive.attr,
 	NULL,
 };
 
@@ -1552,9 +1679,9 @@ static int ep92_i2c_probe(struct i2c_client *client,
 			ep92->irq = 0;
 		}
 	}
-	/* poll status if IRQ is not configured */
-	if (ep92->irq == 0)
-		timer_setup(&ep92->timer, ep92_poll_status, 0);
+	/* prepare timer */
+	timer_setup(&ep92->timer, ep92_poll_status, 0);
+	ep92->poll_rem = EP92_POLL_RUNOUT_MSEC;
 
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 	/* debugfs interface */
@@ -1610,8 +1737,7 @@ static int ep92_i2c_probe(struct i2c_client *client,
 err_sysfs:
 	snd_soc_unregister_component(&client->dev);
 err_reg:
-	if (ep92->irq == 0)
-		del_timer(&ep92->timer);
+	del_timer(&ep92->timer);
 
 	return ret;
 }
@@ -1622,8 +1748,7 @@ static int ep92_i2c_remove(struct i2c_client *client)
 
 	ep92 = i2c_get_clientdata(client);
 	if (ep92) {
-		if (ep92->irq == 0)
-			del_timer(&ep92->timer);
+		del_timer(&ep92->timer);
 
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 		debugfs_remove_recursive(ep92->debugfs_dir);