diff --git a/asoc/codecs/ep92/ep92.c b/asoc/codecs/ep92/ep92.c index 06d29ff8207f..4d481039ffda 100644 --- a/asoc/codecs/ep92/ep92.c +++ b/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 @@ -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);