|
@@ -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);
|