From: Ben Zhang <[email protected]>
This patch allows headphone plug detect and mic present
detect to be enabled at the same time. This patch implements
an irq_chip with irq_domain directly instead of using
regmap_irq, so that interrupt source line polarities can
be flipped to support irq sharing.
Signed-off-by: Ben Zhang <[email protected]>
Signed-off-by: Fletcher Woodruff <[email protected]>
---
sound/soc/codecs/rt5677.c | 261 ++++++++++++++++++++++++++++----------
sound/soc/codecs/rt5677.h | 14 +-
2 files changed, 208 insertions(+), 67 deletions(-)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 9b7a1833d3316c..33380f82b7008f 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -23,6 +23,10 @@
#include <linux/firmware.h>
#include <linux/of_device.h>
#include <linux/property.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -4620,7 +4624,6 @@ static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
- struct regmap_irq_chip_data *data = rt5677->irq_data;
int irq;
if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) ||
@@ -4646,7 +4649,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
return -ENXIO;
}
- return regmap_irq_get_virq(data, irq);
+ return irq_create_mapping(rt5677->domain, irq);
}
static const struct gpio_chip rt5677_template_chip = {
@@ -4716,37 +4719,13 @@ static int rt5677_probe(struct snd_soc_component *component)
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
- regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ ~RT5677_IRQ_DEBOUNCE_SEL_MASK, 0x0020);
regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
for (i = 0; i < RT5677_GPIO_NUM; i++)
rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]);
- if (rt5677->irq_data) {
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, 0x8000,
- 0x8000);
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x0018,
- 0x0008);
-
- if (rt5677->pdata.jd1_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD1_MASK,
- rt5677->pdata.jd1_gpio <<
- RT5677_SEL_GPIO_JD1_SFT);
-
- if (rt5677->pdata.jd2_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD2_MASK,
- rt5677->pdata.jd2_gpio <<
- RT5677_SEL_GPIO_JD2_SFT);
-
- if (rt5677->pdata.jd3_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD3_MASK,
- rt5677->pdata.jd3_gpio <<
- RT5677_SEL_GPIO_JD3_SFT);
- }
-
mutex_init(&rt5677->dsp_cmd_lock);
mutex_init(&rt5677->dsp_pri_lock);
@@ -5063,65 +5042,215 @@ static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
&rt5677->pdata.jd3_gpio);
}
-static struct regmap_irq rt5677_irqs[] = {
+struct rt5677_irq_desc {
+ unsigned int enable_mask;
+ unsigned int status_mask;
+ unsigned int polarity_mask;
+};
+
+static const struct rt5677_irq_desc rt5677_irq_descs[] = {
[RT5677_IRQ_JD1] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD1,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD1,
+ .status_mask = RT5677_STA_GPIO_JD1,
+ .polarity_mask = RT5677_INV_GPIO_JD1,
},
[RT5677_IRQ_JD2] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD2,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD2,
+ .status_mask = RT5677_STA_GPIO_JD2,
+ .polarity_mask = RT5677_INV_GPIO_JD2,
},
[RT5677_IRQ_JD3] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD3,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD3,
+ .status_mask = RT5677_STA_GPIO_JD3,
+ .polarity_mask = RT5677_INV_GPIO_JD3,
},
};
+static irqreturn_t rt5677_irq(int unused, void *data)
+{
+ struct rt5677_priv *rt5677 = data;
+ int ret = 0, i, loop, reg_irq, virq;
+ bool irq_fired;
+
+ mutex_lock(&rt5677->irq_lock);
+ /*
+ * Loop to handle interrupts until the last i2c read shows no pending
+ * irqs. The interrupt line is shared by multiple interrupt sources.
+ * After the regmap_read() below, a new interrupt source line may
+ * become high before the regmap_write() finishes, so there isn't a
+ * rising edge on the shared interrupt line for the new interrupt. Thus,
+ * the loop is needed to avoid missing irqs.
+ *
+ * A safeguard of 20 loops is used to avoid hanging in the irq handler
+ * if there is something wrong with the interrupt status update. The
+ * interrupt sources here are audio jack plug/unplug events which
+ * shouldn't happen at a high frequency for a long period of time.
+ * Empirically, more than 3 loops have never been seen.
+ */
+ for (loop = 0; loop < 20; loop++) {
+ /* Read interrupt status */
+ ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, ®_irq);
+ if (ret)
+ break;
+ /*
+ * Clear the interrupt by flipping the polarity of the
+ * interrupt source lines that just fired
+ */
+ irq_fired = false;
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (reg_irq & rt5677_irq_descs[i].status_mask) {
+ reg_irq ^= rt5677_irq_descs[i].polarity_mask;
+ irq_fired = true;
+ }
+ }
+ if (!irq_fired)
+ break;
-static struct regmap_irq_chip rt5677_irq_chip = {
- .name = "rt5677",
- .irqs = rt5677_irqs,
- .num_irqs = ARRAY_SIZE(rt5677_irqs),
+ ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
+ if (ret)
+ break;
- .num_regs = 1,
- .status_base = RT5677_IRQ_CTRL1,
- .mask_base = RT5677_IRQ_CTRL1,
- .mask_invert = 1,
-};
+ /* Process interrupts */
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if ((reg_irq & rt5677_irq_descs[i].enable_mask) &&
+ (reg_irq & rt5677_irq_descs[i].status_mask)) {
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+ }
+ }
+ }
+ mutex_unlock(&rt5677->irq_lock);
+ return IRQ_HANDLED;
+}
+static void rt5677_irq_work(struct work_struct *work)
+{
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, irq_work.work);
+
+ rt5677_irq(0, rt5677);
+}
-static int rt5677_init_irq(struct i2c_client *i2c)
+static void rt5677_irq_bus_lock(struct irq_data *data)
{
- int ret;
- struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
- if (!rt5677->pdata.jd1_gpio &&
- !rt5677->pdata.jd2_gpio &&
- !rt5677->pdata.jd3_gpio)
- return 0;
+ mutex_lock(&rt5677->irq_lock);
+}
- if (!i2c->irq) {
- dev_err(&i2c->dev, "No interrupt specified\n");
- return -EINVAL;
- }
+static void rt5677_irq_bus_sync_unlock(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
- ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
- &rt5677_irq_chip, &rt5677->irq_data);
+ regmap_update_bits(rt5677->regmap, RT5677_IRQ_CTRL1,
+ RT5677_EN_IRQ_GPIO_JD1 | RT5677_EN_IRQ_GPIO_JD2 |
+ RT5677_EN_IRQ_GPIO_JD3, rt5677->irq_en);
+ mutex_unlock(&rt5677->irq_lock);
+}
- if (ret != 0) {
- dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret);
- return ret;
- }
+static void rt5677_irq_enable(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ rt5677->irq_en |= rt5677_irq_descs[data->hwirq].enable_mask;
+}
+
+static void rt5677_irq_disable(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ rt5677->irq_en &= ~rt5677_irq_descs[data->hwirq].enable_mask;
+}
+static struct irq_chip rt5677_irq_chip = {
+ .name = "rt5677_irq_chip",
+ .irq_bus_lock = rt5677_irq_bus_lock,
+ .irq_bus_sync_unlock = rt5677_irq_bus_sync_unlock,
+ .irq_disable = rt5677_irq_disable,
+ .irq_enable = rt5677_irq_enable,
+};
+
+static int rt5677_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct rt5677_priv *rt5677 = h->host_data;
+
+ irq_set_chip_data(virq, rt5677);
+ irq_set_chip(virq, &rt5677_irq_chip);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
return 0;
}
-static void rt5677_free_irq(struct i2c_client *i2c)
+
+static const struct irq_domain_ops rt5677_domain_ops = {
+ .map = rt5677_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static void rt5677_init_irq(struct i2c_client *i2c)
+{
+ struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ int ret;
+ unsigned int jd_mask = 0, jd_val = 0;
+
+ /* No irq has been assigned to the codec */
+ if (!i2c->irq)
+ return;
+
+ mutex_init(&rt5677->irq_lock);
+ INIT_DELAYED_WORK(&rt5677->irq_work, rt5677_irq_work);
+
+ /*
+ * Select RC as the debounce clock so that GPIO works even when
+ * MCLK is gated which happens when there is no audio stream
+ * (SND_SOC_BIAS_OFF).
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ RT5677_IRQ_DEBOUNCE_SEL_MASK,
+ RT5677_IRQ_DEBOUNCE_SEL_RC);
+ /* Enable auto power on RC when GPIO states are changed */
+ regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL1, 0xff, 0xff);
+
+ /* Select and enable jack detection sources per platform data */
+ if (rt5677->pdata.jd1_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD1_MASK;
+ jd_val |= rt5677->pdata.jd1_gpio << RT5677_SEL_GPIO_JD1_SFT;
+ }
+ if (rt5677->pdata.jd2_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD2_MASK;
+ jd_val |= rt5677->pdata.jd2_gpio << RT5677_SEL_GPIO_JD2_SFT;
+ }
+ if (rt5677->pdata.jd3_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD3_MASK;
+ jd_val |= rt5677->pdata.jd3_gpio << RT5677_SEL_GPIO_JD3_SFT;
+ }
+ regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1, jd_mask, jd_val);
+
+ /* Set GPIO1 pin to be IRQ output */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
+
+ /* Ready to listen for interrupts */
+ rt5677->domain = irq_domain_add_linear(i2c->dev.of_node,
+ RT5677_IRQ_NUM, &rt5677_domain_ops, rt5677);
+ if (!rt5677->domain) {
+ dev_err(&i2c->dev, "Failed to create IRQ domain\n");
+ return;
+ }
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5677_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "rt5677", rt5677);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ return;
+ }
+}
+
+static void rt5677_irq_exit(struct i2c_client *i2c)
{
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
- if (rt5677->irq_data)
- regmap_del_irq_chip(i2c->irq, rt5677->irq_data);
+ cancel_delayed_work_sync(&rt5677->irq_work);
}
static int rt5677_i2c_probe(struct i2c_client *i2c)
@@ -5259,7 +5388,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
static int rt5677_i2c_remove(struct i2c_client *i2c)
{
- rt5677_free_irq(i2c);
+ rt5677_irq_exit(i2c);
rt5677_free_gpio(i2c);
return 0;
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index 183d92b030459f..9c73bea8635508 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1636,6 +1636,12 @@
#define RT5677_GPIO6_P_NOR (0x0 << 0)
#define RT5677_GPIO6_P_INV (0x1 << 0)
+/* General Control (0xfa) */
+#define RT5677_IRQ_DEBOUNCE_SEL_MASK (0x3 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_MCLK (0x0 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_RC (0x1 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_SLIM (0x2 << 3)
+
/* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */
#define RT5677_DSP_IB_01_H (0x1 << 15)
#define RT5677_DSP_IB_01_H_SFT 15
@@ -1713,6 +1719,7 @@ enum {
RT5677_IRQ_JD1,
RT5677_IRQ_JD2,
RT5677_IRQ_JD3,
+ RT5677_IRQ_NUM,
};
enum rt5677_type {
@@ -1811,9 +1818,14 @@ struct rt5677_priv {
struct gpio_chip gpio_chip;
#endif
bool dsp_vad_en;
- struct regmap_irq_chip_data *irq_data;
bool is_dsp_mode;
bool is_vref_slow;
+
+ /* Interrupt handling */
+ struct irq_domain *domain;
+ struct mutex irq_lock;
+ unsigned int irq_en;
+ struct delayed_work irq_work;
};
int rt5677_sel_asrc_clk_src(struct snd_soc_component *component,
--
2.21.0.392.gf8f6787159e-goog
The rt5677 driver is using the wrong property names to read from ACPI.
Update the property names to match those from _DSD, so that the correct
GPIO pin numbers are read and that plug-detection works.
With this patch, plugging and unplugging the headphone jack switches
between headphones and speakers automatically.
Signed-off-by: Fletcher Woodruff <[email protected]>
---
sound/soc/codecs/rt5677.c | 27 ++++++---------------------
1 file changed, 6 insertions(+), 21 deletions(-)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 33380f82b7008f..1f725086cee39c 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -4998,25 +4998,6 @@ static const struct acpi_device_id rt5677_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match);
-static void rt5677_read_acpi_properties(struct rt5677_priv *rt5677,
- struct device *dev)
-{
- u32 val;
-
- if (!device_property_read_u32(dev, "DCLK", &val))
- rt5677->pdata.dmic2_clk_pin = val;
-
- rt5677->pdata.in1_diff = device_property_read_bool(dev, "IN1");
- rt5677->pdata.in2_diff = device_property_read_bool(dev, "IN2");
- rt5677->pdata.lout1_diff = device_property_read_bool(dev, "OUT1");
- rt5677->pdata.lout2_diff = device_property_read_bool(dev, "OUT2");
- rt5677->pdata.lout3_diff = device_property_read_bool(dev, "OUT3");
-
- device_property_read_u32(dev, "JD1", &rt5677->pdata.jd1_gpio);
- device_property_read_u32(dev, "JD2", &rt5677->pdata.jd2_gpio);
- device_property_read_u32(dev, "JD3", &rt5677->pdata.jd3_gpio);
-}
-
static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
struct device *dev)
{
@@ -5273,19 +5254,23 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
if (match_id)
rt5677->type = (enum rt5677_type)match_id->data;
- rt5677_read_device_properties(rt5677, &i2c->dev);
} else if (ACPI_HANDLE(&i2c->dev)) {
const struct acpi_device_id *acpi_id;
+ u32 val;
acpi_id = acpi_match_device(rt5677_acpi_match, &i2c->dev);
if (acpi_id)
rt5677->type = (enum rt5677_type)acpi_id->driver_data;
- rt5677_read_acpi_properties(rt5677, &i2c->dev);
+ if (!device_property_read_u32(&i2c->dev,
+ "realtek,dmic2_clk_pin", &val))
+ rt5677->pdata.dmic2_clk_pin = val;
} else {
return -EINVAL;
}
+ rt5677_read_device_properties(rt5677, &i2c->dev);
+
/* pow-ldo2 and reset are optional. The codec pins may be statically
* connected on the board without gpios. If the gpio device property
* isn't specified, devm_gpiod_get_optional returns NULL.
--
2.21.0.392.gf8f6787159e-goog
On Mon, Apr 01, 2019 at 02:55:19PM -0600, Fletcher Woodruff wrote:
> The rt5677 driver is using the wrong property names to read from ACPI.
> Update the property names to match those from _DSD, so that the correct
> GPIO pin numbers are read and that plug-detection works.
> With this patch, plugging and unplugging the headphone jack switches
> between headphones and speakers automatically.
What makes you say that these properties are wrong? Are you sure that
this isn't just some other systems using different ACPI properties given
the poor standardization for ACPI? Your new ones look like they're DT
properties pulled into ACPI while the existing ones look more idiomatic
for ACPI. It'd be fine to add your new DT style properties but this
might break existing systems.
On Mon, Apr 01, 2019 at 02:55:18PM -0600, Fletcher Woodruff wrote:
> From: Ben Zhang <[email protected]>
>
> This patch allows headphone plug detect and mic present
> detect to be enabled at the same time. This patch implements
> an irq_chip with irq_domain directly instead of using
> regmap_irq, so that interrupt source line polarities can
> be flipped to support irq sharing.
regmap-irq should support active high/low, and if it doesn't it can't be
a unique thing that only this device wants to implement so the common
code should be improved.
> + mutex_lock(&rt5677->irq_lock);
> + /*
> + * Loop to handle interrupts until the last i2c read shows no pending
> + * irqs. The interrupt line is shared by multiple interrupt sources.
> + * After the regmap_read() below, a new interrupt source line may
> + * become high before the regmap_write() finishes, so there isn't a
> + * rising edge on the shared interrupt line for the new interrupt. Thus,
> + * the loop is needed to avoid missing irqs.
> + *
> + * A safeguard of 20 loops is used to avoid hanging in the irq handler
> + * if there is something wrong with the interrupt status update. The
> + * interrupt sources here are audio jack plug/unplug events which
> + * shouldn't happen at a high frequency for a long period of time.
> + * Empirically, more than 3 loops have never been seen.
> + */
> + for (loop = 0; loop < 20; loop++) {
This looks unrelated to the polarity of the interupt?
On Mon, Apr 1, 2019 at 11:06 PM Mark Brown <[email protected]> wrote:
> On Mon, Apr 01, 2019 at 02:55:19PM -0600, Fletcher Woodruff wrote:
> > The rt5677 driver is using the wrong property names to read from ACPI.
> > Update the property names to match those from _DSD, so that the correct
> > GPIO pin numbers are read and that plug-detection works.
>
> > With this patch, plugging and unplugging the headphone jack switches
> > between headphones and speakers automatically.
>
> What makes you say that these properties are wrong? Are you sure that
> this isn't just some other systems using different ACPI properties given
> the poor standardization for ACPI? Your new ones look like they're DT
> properties pulled into ACPI while the existing ones look more idiomatic
> for ACPI.
The code to read those properties was originally added by commit 89128534f,
specifically to support the device I am trying to fix, the Chromebook
Pixel 2015.
Admittedly, it's possible some other device came along and matched these
property names since then, but I'm inclined to think we might be the only ones.
> It'd be fine to add your new DT style properties but this
> might break existing systems.
That said, I agree it would be safer to use the idiomatic ACPI names, and then
fall back to the DT style names if the idiomatic names fail. Does that sound
reasonable?
On Tue, Apr 02, 2019 at 09:52:56AM -0600, Fletcher Woodruff wrote:
> On Mon, Apr 1, 2019 at 11:06 PM Mark Brown <[email protected]> wrote:
> > It'd be fine to add your new DT style properties but this
> > might break existing systems.
> That said, I agree it would be safer to use the idiomatic ACPI names, and then
> fall back to the DT style names if the idiomatic names fail. Does that sound
> reasonable?
Yes.
On Mon, Apr 1, 2019 at 11:02 PM Mark Brown <[email protected]> wrote:
> regmap-irq should support active high/low, and if it doesn't it can't be
> a unique thing that only this device wants to implement so the common
> code should be improved.
The rt5677 driver needs its own irq regardless for hotword detection.
If we implemented this in regmap-irq, we wouldn't be able to use it.
> This looks unrelated to the polarity of the interupt?
Yes this is separate. If a plug/unplug happens after regmap_read and
before regmap_write, it will not be registered, so we loop to make
sure that it's caught in a later iteration. I can clarify this in the
patch notes.
On Wed, Apr 03, 2019 at 03:32:04PM -0600, Fletcher Woodruff wrote:
> On Mon, Apr 1, 2019 at 11:02 PM Mark Brown <[email protected]> wrote:
> > This looks unrelated to the polarity of the interupt?
> Yes this is separate. If a plug/unplug happens after regmap_read and
> before regmap_write, it will not be registered, so we loop to make
> sure that it's caught in a later iteration. I can clarify this in the
> patch notes.
Please submit one patch per change, each with a clear changelog, as
covered in SubmittingPatches. This makes it much easier to review
things since it's easier to tell if the patch does what it was intended
to do.
Headphone/mic jack detection doesn't work on the Chromebook Pixel 2015.
This patch changes the irq implementation to support polarity flipping
and fixes the configuration code so that correct GPIO pins are read
from ACPI.
With this series, plugging and unplugging the headphone jack switches
between headphones and speakers automatically, and headset microphones
are also detected.
v2:
- Split IRQ change into two patches: adding and fixing potential race
- Change config reading code to try both DT and ACPI style names
Ben Zhang (2):
ASoC: rt5677: allow multiple interrupt sources
ASoC: rt5677: handle concurrent interrupts
Fletcher Woodruff (1):
ASoC: rt5677: fall back to DT prop names on error
sound/soc/codecs/rt5677.c | 347 +++++++++++++++++++++++++++-----------
sound/soc/codecs/rt5677.h | 14 +-
2 files changed, 257 insertions(+), 104 deletions(-)
--
2.21.0.392.gf8f6787159e-goog
From: Ben Zhang <[email protected]>
This patch allows headphone plug detect and mic present
detect to be enabled at the same time. This patch implements
an irq_chip with irq_domain directly instead of using
regmap_irq, so that interrupt source line polarities can
be flipped to support irq sharing.
Signed-off-by: Ben Zhang <[email protected]>
Signed-off-by: Fletcher Woodruff <[email protected]>
---
sound/soc/codecs/rt5677.c | 257 ++++++++++++++++++++++++++++----------
sound/soc/codecs/rt5677.h | 14 ++-
2 files changed, 204 insertions(+), 67 deletions(-)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 9b7a1833d3316c..ef50314ac61eb5 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -23,6 +23,10 @@
#include <linux/firmware.h>
#include <linux/of_device.h>
#include <linux/property.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -4620,7 +4624,6 @@ static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
- struct regmap_irq_chip_data *data = rt5677->irq_data;
int irq;
if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) ||
@@ -4646,7 +4649,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
return -ENXIO;
}
- return regmap_irq_get_virq(data, irq);
+ return irq_create_mapping(rt5677->domain, irq);
}
static const struct gpio_chip rt5677_template_chip = {
@@ -4716,37 +4719,13 @@ static int rt5677_probe(struct snd_soc_component *component)
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
- regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ ~RT5677_IRQ_DEBOUNCE_SEL_MASK, 0x0020);
regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
for (i = 0; i < RT5677_GPIO_NUM; i++)
rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]);
- if (rt5677->irq_data) {
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, 0x8000,
- 0x8000);
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x0018,
- 0x0008);
-
- if (rt5677->pdata.jd1_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD1_MASK,
- rt5677->pdata.jd1_gpio <<
- RT5677_SEL_GPIO_JD1_SFT);
-
- if (rt5677->pdata.jd2_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD2_MASK,
- rt5677->pdata.jd2_gpio <<
- RT5677_SEL_GPIO_JD2_SFT);
-
- if (rt5677->pdata.jd3_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD3_MASK,
- rt5677->pdata.jd3_gpio <<
- RT5677_SEL_GPIO_JD3_SFT);
- }
-
mutex_init(&rt5677->dsp_cmd_lock);
mutex_init(&rt5677->dsp_pri_lock);
@@ -5063,65 +5042,211 @@ static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
&rt5677->pdata.jd3_gpio);
}
-static struct regmap_irq rt5677_irqs[] = {
+struct rt5677_irq_desc {
+ unsigned int enable_mask;
+ unsigned int status_mask;
+ unsigned int polarity_mask;
+};
+
+static const struct rt5677_irq_desc rt5677_irq_descs[] = {
[RT5677_IRQ_JD1] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD1,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD1,
+ .status_mask = RT5677_STA_GPIO_JD1,
+ .polarity_mask = RT5677_INV_GPIO_JD1,
},
[RT5677_IRQ_JD2] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD2,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD2,
+ .status_mask = RT5677_STA_GPIO_JD2,
+ .polarity_mask = RT5677_INV_GPIO_JD2,
},
[RT5677_IRQ_JD3] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD3,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD3,
+ .status_mask = RT5677_STA_GPIO_JD3,
+ .polarity_mask = RT5677_INV_GPIO_JD3,
},
};
-static struct regmap_irq_chip rt5677_irq_chip = {
- .name = "rt5677",
- .irqs = rt5677_irqs,
- .num_irqs = ARRAY_SIZE(rt5677_irqs),
-
- .num_regs = 1,
- .status_base = RT5677_IRQ_CTRL1,
- .mask_base = RT5677_IRQ_CTRL1,
- .mask_invert = 1,
-};
-
-static int rt5677_init_irq(struct i2c_client *i2c)
+static irqreturn_t rt5677_irq(int unused, void *data)
{
- int ret;
+ struct i2c_client *i2c = data;
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ int ret = 0, i, reg_irq, virq;
+ bool irq_fired;
- if (!rt5677->pdata.jd1_gpio &&
- !rt5677->pdata.jd2_gpio &&
- !rt5677->pdata.jd3_gpio)
- return 0;
-
- if (!i2c->irq) {
- dev_err(&i2c->dev, "No interrupt specified\n");
- return -EINVAL;
+ mutex_lock(&rt5677->irq_lock);
+ /* Read interrupt status */
+ ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, ®_irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to read IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
+ /*
+ * Clear the interrupt by flipping the polarity of the
+ * interrupt source lines that just fired
+ */
+ irq_fired = false;
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (reg_irq & rt5677_irq_descs[i].status_mask) {
+ reg_irq ^= rt5677_irq_descs[i].polarity_mask;
+ irq_fired = true;
+ }
}
+ if (!irq_fired)
+ goto exit;
- ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
- &rt5677_irq_chip, &rt5677->irq_data);
+ ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to update IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
- if (ret != 0) {
- dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret);
- return ret;
+ /* Process interrupts */
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if ((reg_irq & rt5677_irq_descs[i].enable_mask) &&
+ (reg_irq & rt5677_irq_descs[i].status_mask)) {
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+ }
}
+exit:
+ mutex_unlock(&rt5677->irq_lock);
+ return IRQ_HANDLED;
+}
+
+static void rt5677_irq_work(struct work_struct *work)
+{
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, irq_work.work);
+
+ rt5677_irq(0, rt5677);
+}
+
+static void rt5677_irq_bus_lock(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&rt5677->irq_lock);
+}
+
+static void rt5677_irq_bus_sync_unlock(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+ regmap_update_bits(rt5677->regmap, RT5677_IRQ_CTRL1,
+ RT5677_EN_IRQ_GPIO_JD1 | RT5677_EN_IRQ_GPIO_JD2 |
+ RT5677_EN_IRQ_GPIO_JD3, rt5677->irq_en);
+ mutex_unlock(&rt5677->irq_lock);
+}
+
+static void rt5677_irq_enable(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ rt5677->irq_en |= rt5677_irq_descs[data->hwirq].enable_mask;
+}
+
+static void rt5677_irq_disable(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ rt5677->irq_en &= ~rt5677_irq_descs[data->hwirq].enable_mask;
+}
+
+static struct irq_chip rt5677_irq_chip = {
+ .name = "rt5677_irq_chip",
+ .irq_bus_lock = rt5677_irq_bus_lock,
+ .irq_bus_sync_unlock = rt5677_irq_bus_sync_unlock,
+ .irq_disable = rt5677_irq_disable,
+ .irq_enable = rt5677_irq_enable,
+};
+
+static int rt5677_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct rt5677_priv *rt5677 = h->host_data;
+
+ irq_set_chip_data(virq, rt5677);
+ irq_set_chip(virq, &rt5677_irq_chip);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
return 0;
}
-static void rt5677_free_irq(struct i2c_client *i2c)
+
+static const struct irq_domain_ops rt5677_domain_ops = {
+ .map = rt5677_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static void rt5677_init_irq(struct i2c_client *i2c)
+{
+ struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ int ret;
+ unsigned int jd_mask = 0, jd_val = 0;
+
+ /* No irq has been assigned to the codec */
+ if (!i2c->irq)
+ return;
+
+ mutex_init(&rt5677->irq_lock);
+ INIT_DELAYED_WORK(&rt5677->irq_work, rt5677_irq_work);
+
+ /*
+ * Select RC as the debounce clock so that GPIO works even when
+ * MCLK is gated which happens when there is no audio stream
+ * (SND_SOC_BIAS_OFF).
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ RT5677_IRQ_DEBOUNCE_SEL_MASK,
+ RT5677_IRQ_DEBOUNCE_SEL_RC);
+ /* Enable auto power on RC when GPIO states are changed */
+ regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL1, 0xff, 0xff);
+
+ /* Select and enable jack detection sources per platform data */
+ if (rt5677->pdata.jd1_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD1_MASK;
+ jd_val |= rt5677->pdata.jd1_gpio << RT5677_SEL_GPIO_JD1_SFT;
+ }
+ if (rt5677->pdata.jd2_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD2_MASK;
+ jd_val |= rt5677->pdata.jd2_gpio << RT5677_SEL_GPIO_JD2_SFT;
+ }
+ if (rt5677->pdata.jd3_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD3_MASK;
+ jd_val |= rt5677->pdata.jd3_gpio << RT5677_SEL_GPIO_JD3_SFT;
+ }
+ regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1, jd_mask, jd_val);
+
+ /* Set GPIO1 pin to be IRQ output */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
+
+ /* Ready to listen for interrupts */
+ rt5677->domain = irq_domain_add_linear(i2c->dev.of_node,
+ RT5677_IRQ_NUM, &rt5677_domain_ops, rt5677);
+ if (!rt5677->domain) {
+ dev_err(&i2c->dev, "Failed to create IRQ domain\n");
+ return;
+ }
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5677_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "rt5677", i2c);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ return;
+ }
+}
+
+static void rt5677_irq_exit(struct i2c_client *i2c)
{
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
- if (rt5677->irq_data)
- regmap_del_irq_chip(i2c->irq, rt5677->irq_data);
+ cancel_delayed_work_sync(&rt5677->irq_work);
}
static int rt5677_i2c_probe(struct i2c_client *i2c)
@@ -5259,7 +5384,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
static int rt5677_i2c_remove(struct i2c_client *i2c)
{
- rt5677_free_irq(i2c);
+ rt5677_irq_exit(i2c);
rt5677_free_gpio(i2c);
return 0;
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index 183d92b030459f..9c73bea8635508 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1636,6 +1636,12 @@
#define RT5677_GPIO6_P_NOR (0x0 << 0)
#define RT5677_GPIO6_P_INV (0x1 << 0)
+/* General Control (0xfa) */
+#define RT5677_IRQ_DEBOUNCE_SEL_MASK (0x3 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_MCLK (0x0 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_RC (0x1 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_SLIM (0x2 << 3)
+
/* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */
#define RT5677_DSP_IB_01_H (0x1 << 15)
#define RT5677_DSP_IB_01_H_SFT 15
@@ -1713,6 +1719,7 @@ enum {
RT5677_IRQ_JD1,
RT5677_IRQ_JD2,
RT5677_IRQ_JD3,
+ RT5677_IRQ_NUM,
};
enum rt5677_type {
@@ -1811,9 +1818,14 @@ struct rt5677_priv {
struct gpio_chip gpio_chip;
#endif
bool dsp_vad_en;
- struct regmap_irq_chip_data *irq_data;
bool is_dsp_mode;
bool is_vref_slow;
+
+ /* Interrupt handling */
+ struct irq_domain *domain;
+ struct mutex irq_lock;
+ unsigned int irq_en;
+ struct delayed_work irq_work;
};
int rt5677_sel_asrc_clk_src(struct snd_soc_component *component,
--
2.21.0.392.gf8f6787159e-goog
From: Ben Zhang <[email protected]>
The rt5677 driver writes to the IRQ control register within the IRQ
handler in order to flip the polarity of the interrupts that have been
signalled. If an interrupt fires in the interval between the
regmap_read and the regmap_write, it will not trigger a new call to
rt5677_irq.
Add a bounded loop to rt5677_irq that keeps checking interrupts until
none are seen, so that any interrupts that are signalled in that
interval are correctly handled.
Signed-off-by: Ben Zhang <[email protected]>
Signed-off-by: Fletcher Woodruff <[email protected]>
---
sound/soc/codecs/rt5677.c | 84 +++++++++++++++++++++++----------------
1 file changed, 50 insertions(+), 34 deletions(-)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index ef50314ac61eb5..6bff0df53c6c6b 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5070,47 +5070,63 @@ static irqreturn_t rt5677_irq(int unused, void *data)
{
struct i2c_client *i2c = data;
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
- int ret = 0, i, reg_irq, virq;
+ int ret = 0, loop, i, reg_irq, virq;
bool irq_fired;
mutex_lock(&rt5677->irq_lock);
- /* Read interrupt status */
- ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, ®_irq);
- if (ret) {
- dev_err(&i2c->dev,
- "Failed to read IRQ status: %d\n",
- ret);
- goto exit;
- }
/*
- * Clear the interrupt by flipping the polarity of the
- * interrupt source lines that just fired
+ * Loop to handle interrupts until the last i2c read shows no pending
+ * irqs. The interrupt line is shared by multiple interrupt sources.
+ * After the regmap_read() below, a new interrupt source line may
+ * become high before the regmap_write() finishes, so there isn't a
+ * rising edge on the shared interrupt line for the new interrupt. Thus,
+ * the loop is needed to avoid missing irqs.
+ *
+ * A safeguard of 20 loops is used to avoid hanging in the irq handler
+ * if there is something wrong with the interrupt status update. The
+ * interrupt sources here are audio jack plug/unplug events which
+ * shouldn't happen at a high frequency for a long period of time.
+ * Empirically, more than 3 loops have never been seen.
*/
- irq_fired = false;
- for (i = 0; i < RT5677_IRQ_NUM; i++) {
- if (reg_irq & rt5677_irq_descs[i].status_mask) {
- reg_irq ^= rt5677_irq_descs[i].polarity_mask;
- irq_fired = true;
+ for (loop = 0; loop < 20; loop++) {
+ /* Read interrupt status */
+ ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, ®_irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to read IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
+ /*
+ * Clear the interrupt by flipping the polarity of the
+ * interrupt source lines that just fired
+ */
+ irq_fired = false;
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (reg_irq & rt5677_irq_descs[i].status_mask) {
+ reg_irq ^= rt5677_irq_descs[i].polarity_mask;
+ irq_fired = true;
+ }
+ }
+ if (!irq_fired)
+ goto exit;
+
+ ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to update IRQ status: %d\n",
+ ret);
+ goto exit;
}
- }
- if (!irq_fired)
- goto exit;
-
- ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
- if (ret) {
- dev_err(&i2c->dev,
- "Failed to update IRQ status: %d\n",
- ret);
- goto exit;
- }
- /* Process interrupts */
- for (i = 0; i < RT5677_IRQ_NUM; i++) {
- if ((reg_irq & rt5677_irq_descs[i].enable_mask) &&
- (reg_irq & rt5677_irq_descs[i].status_mask)) {
- virq = irq_find_mapping(rt5677->domain, i);
- if (virq)
- handle_nested_irq(virq);
+ /* Process interrupts */
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if ((reg_irq & rt5677_irq_descs[i].enable_mask) &&
+ (reg_irq & rt5677_irq_descs[i].status_mask)) {
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+ }
}
}
exit:
--
2.21.0.392.gf8f6787159e-goog
The rt5677 driver uses ACPI-style property names to read from the
device API. However, these do not match the property names in _DSD
used on the Chromebook Pixel 2015, which are closer to the Device Tree
style. Unify the two functions for reading from the device API so that
they try ACPI-style names first and fall back to the DT names on error.
Signed-off-by: Fletcher Woodruff <[email protected]>
---
sound/soc/codecs/rt5677.c | 74 +++++++++++++++++++--------------------
1 file changed, 37 insertions(+), 37 deletions(-)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 6bff0df53c6c6b..38e99580f09acd 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -4998,48 +4998,50 @@ static const struct acpi_device_id rt5677_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match);
-static void rt5677_read_acpi_properties(struct rt5677_priv *rt5677,
+static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
struct device *dev)
{
u32 val;
- if (!device_property_read_u32(dev, "DCLK", &val))
- rt5677->pdata.dmic2_clk_pin = val;
+ rt5677->pdata.in1_diff =
+ device_property_read_bool(dev, "IN1") ||
+ device_property_read_bool(dev, "realtek,in1-differential");
- rt5677->pdata.in1_diff = device_property_read_bool(dev, "IN1");
- rt5677->pdata.in2_diff = device_property_read_bool(dev, "IN2");
- rt5677->pdata.lout1_diff = device_property_read_bool(dev, "OUT1");
- rt5677->pdata.lout2_diff = device_property_read_bool(dev, "OUT2");
- rt5677->pdata.lout3_diff = device_property_read_bool(dev, "OUT3");
+ rt5677->pdata.in2_diff =
+ device_property_read_bool(dev, "IN2") ||
+ device_property_read_bool(dev, "realtek,in2-differential");
- device_property_read_u32(dev, "JD1", &rt5677->pdata.jd1_gpio);
- device_property_read_u32(dev, "JD2", &rt5677->pdata.jd2_gpio);
- device_property_read_u32(dev, "JD3", &rt5677->pdata.jd3_gpio);
-}
+ rt5677->pdata.lout1_diff =
+ device_property_read_bool(dev, "OUT1") ||
+ device_property_read_bool(dev, "realtek,lout1-differential");
-static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
- struct device *dev)
-{
- rt5677->pdata.in1_diff = device_property_read_bool(dev,
- "realtek,in1-differential");
- rt5677->pdata.in2_diff = device_property_read_bool(dev,
- "realtek,in2-differential");
- rt5677->pdata.lout1_diff = device_property_read_bool(dev,
- "realtek,lout1-differential");
- rt5677->pdata.lout2_diff = device_property_read_bool(dev,
- "realtek,lout2-differential");
- rt5677->pdata.lout3_diff = device_property_read_bool(dev,
- "realtek,lout3-differential");
+ rt5677->pdata.lout2_diff =
+ device_property_read_bool(dev, "OUT2") ||
+ device_property_read_bool(dev, "realtek,lout2-differential");
+
+ rt5677->pdata.lout3_diff =
+ device_property_read_bool(dev, "OUT3") ||
+ device_property_read_bool(dev, "realtek,lout3-differential");
device_property_read_u8_array(dev, "realtek,gpio-config",
- rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
-
- device_property_read_u32(dev, "realtek,jd1-gpio",
- &rt5677->pdata.jd1_gpio);
- device_property_read_u32(dev, "realtek,jd2-gpio",
- &rt5677->pdata.jd2_gpio);
- device_property_read_u32(dev, "realtek,jd3-gpio",
- &rt5677->pdata.jd3_gpio);
+ rt5677->pdata.gpio_config,
+ RT5677_GPIO_NUM);
+
+ if (!device_property_read_u32(dev, "DCLK", &val) ||
+ !device_property_read_u32(dev, "realtek,dmic2_clk_pin", &val))
+ rt5677->pdata.dmic2_clk_pin = val;
+
+ if (!device_property_read_u32(dev, "JD1", &val) ||
+ !device_property_read_u32(dev, "realtek,jd1-gpio", &val))
+ rt5677->pdata.jd1_gpio = val;
+
+ if (!device_property_read_u32(dev, "JD2", &val) ||
+ !device_property_read_u32(dev, "realtek,jd2-gpio", &val))
+ rt5677->pdata.jd2_gpio = val;
+
+ if (!device_property_read_u32(dev, "JD3", &val) ||
+ !device_property_read_u32(dev, "realtek,jd3-gpio", &val))
+ rt5677->pdata.jd3_gpio = val;
}
struct rt5677_irq_desc {
@@ -5284,20 +5286,18 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
match_id = of_match_device(rt5677_of_match, &i2c->dev);
if (match_id)
rt5677->type = (enum rt5677_type)match_id->data;
-
- rt5677_read_device_properties(rt5677, &i2c->dev);
} else if (ACPI_HANDLE(&i2c->dev)) {
const struct acpi_device_id *acpi_id;
acpi_id = acpi_match_device(rt5677_acpi_match, &i2c->dev);
if (acpi_id)
rt5677->type = (enum rt5677_type)acpi_id->driver_data;
-
- rt5677_read_acpi_properties(rt5677, &i2c->dev);
} else {
return -EINVAL;
}
+ rt5677_read_device_properties(rt5677, &i2c->dev);
+
/* pow-ldo2 and reset are optional. The codec pins may be statically
* connected on the board without gpios. If the gpio device property
* isn't specified, devm_gpiod_get_optional returns NULL.
--
2.21.0.392.gf8f6787159e-goog
On Fri, Apr 05, 2019 at 02:42:55PM -0600, Fletcher Woodruff wrote:
> This patch allows headphone plug detect and mic present
> detect to be enabled at the same time. This patch implements
> an irq_chip with irq_domain directly instead of using
> regmap_irq, so that interrupt source line polarities can
> be flipped to support irq sharing.
This changelog still doesn't explain why there is a need to open code
this rather than having the irq_chip and irq_domain provided by
regmap-irq support whatever is missing here. You said it "needs its own
irq regardless for hotword detection" but provided no information on why
this is.
On Mon, Apr 8, 2019 at 12:38 AM Mark Brown <[email protected]> wrote:
> On Fri, Apr 05, 2019 at 02:42:55PM -0600, Fletcher Woodruff wrote:
>
> > This patch allows headphone plug detect and mic present
> > detect to be enabled at the same time. This patch implements
> > an irq_chip with irq_domain directly instead of using
> > regmap_irq, so that interrupt source line polarities can
> > be flipped to support irq sharing.
>
> This changelog still doesn't explain why there is a need to open code
> this rather than having the irq_chip and irq_domain provided by
> regmap-irq support whatever is missing here. You said it "needs its own
> irq regardless for hotword detection" but provided no information on why
> this is.
Sorry! I misunderstood your earlier response so I didn't clarify that
portion of the commit message. Thanks for your patience.
How does this updated commit message sound? If you think it's
sufficiently clear, I can send a v3 for this patch.
This patch allows headphone plug detect and mic present
detect to be enabled at the same time. This patch implements
an irq_chip with irq_domain directly instead of using
regmap-irq, so that interrupt source line polarities can
be flipped to support irq sharing.
This patch does not add polarity flipping support within regmap-irq
because there is extra work that must be done within the irq handler
to support hotword detection. On the Chromebook Pixel, the firmware will
disconnect GPIO1 from the jack detection irq when a hotword is detected
and trigger the interrupt handler. Inside the handler, we will need to
detect this, report the hotword event, and re-connect GPIO1 to the jack
detection irq.
From: Ben Zhang <[email protected]>
This patch allows headphone plug detect and mic present
detect to be enabled at the same time. This patch implements
an irq_chip with irq_domain directly instead of using
regmap-irq, so that interrupt source line polarities can
be flipped to support irq sharing.
This patch does not add polarity flipping support within regmap-irq
because there is extra work that must be done within the irq handler
to support hotword detection. On the Chromebook Pixel, the firmware will
disconnect GPIO1 from the jack detection irq when a hotword is detected
and trigger the interrupt handler. Inside the handler, we will need to
detect this, report the hotword event, and re-connect GPIO1 to the jack
detection irq.
Signed-off-by: Ben Zhang <[email protected]>
Signed-off-by: Fletcher Woodruff <[email protected]>
---
sound/soc/codecs/rt5677.c | 257 ++++++++++++++++++++++++++++----------
sound/soc/codecs/rt5677.h | 14 ++-
2 files changed, 204 insertions(+), 67 deletions(-)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 9b7a1833d3316c..ef50314ac61eb5 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -23,6 +23,10 @@
#include <linux/firmware.h>
#include <linux/of_device.h>
#include <linux/property.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -4620,7 +4624,6 @@ static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
- struct regmap_irq_chip_data *data = rt5677->irq_data;
int irq;
if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) ||
@@ -4646,7 +4649,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
return -ENXIO;
}
- return regmap_irq_get_virq(data, irq);
+ return irq_create_mapping(rt5677->domain, irq);
}
static const struct gpio_chip rt5677_template_chip = {
@@ -4716,37 +4719,13 @@ static int rt5677_probe(struct snd_soc_component *component)
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
- regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ ~RT5677_IRQ_DEBOUNCE_SEL_MASK, 0x0020);
regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
for (i = 0; i < RT5677_GPIO_NUM; i++)
rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]);
- if (rt5677->irq_data) {
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, 0x8000,
- 0x8000);
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x0018,
- 0x0008);
-
- if (rt5677->pdata.jd1_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD1_MASK,
- rt5677->pdata.jd1_gpio <<
- RT5677_SEL_GPIO_JD1_SFT);
-
- if (rt5677->pdata.jd2_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD2_MASK,
- rt5677->pdata.jd2_gpio <<
- RT5677_SEL_GPIO_JD2_SFT);
-
- if (rt5677->pdata.jd3_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD3_MASK,
- rt5677->pdata.jd3_gpio <<
- RT5677_SEL_GPIO_JD3_SFT);
- }
-
mutex_init(&rt5677->dsp_cmd_lock);
mutex_init(&rt5677->dsp_pri_lock);
@@ -5063,65 +5042,211 @@ static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
&rt5677->pdata.jd3_gpio);
}
-static struct regmap_irq rt5677_irqs[] = {
+struct rt5677_irq_desc {
+ unsigned int enable_mask;
+ unsigned int status_mask;
+ unsigned int polarity_mask;
+};
+
+static const struct rt5677_irq_desc rt5677_irq_descs[] = {
[RT5677_IRQ_JD1] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD1,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD1,
+ .status_mask = RT5677_STA_GPIO_JD1,
+ .polarity_mask = RT5677_INV_GPIO_JD1,
},
[RT5677_IRQ_JD2] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD2,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD2,
+ .status_mask = RT5677_STA_GPIO_JD2,
+ .polarity_mask = RT5677_INV_GPIO_JD2,
},
[RT5677_IRQ_JD3] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD3,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD3,
+ .status_mask = RT5677_STA_GPIO_JD3,
+ .polarity_mask = RT5677_INV_GPIO_JD3,
},
};
-static struct regmap_irq_chip rt5677_irq_chip = {
- .name = "rt5677",
- .irqs = rt5677_irqs,
- .num_irqs = ARRAY_SIZE(rt5677_irqs),
-
- .num_regs = 1,
- .status_base = RT5677_IRQ_CTRL1,
- .mask_base = RT5677_IRQ_CTRL1,
- .mask_invert = 1,
-};
-
-static int rt5677_init_irq(struct i2c_client *i2c)
+static irqreturn_t rt5677_irq(int unused, void *data)
{
- int ret;
+ struct i2c_client *i2c = data;
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ int ret = 0, i, reg_irq, virq;
+ bool irq_fired;
- if (!rt5677->pdata.jd1_gpio &&
- !rt5677->pdata.jd2_gpio &&
- !rt5677->pdata.jd3_gpio)
- return 0;
-
- if (!i2c->irq) {
- dev_err(&i2c->dev, "No interrupt specified\n");
- return -EINVAL;
+ mutex_lock(&rt5677->irq_lock);
+ /* Read interrupt status */
+ ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, ®_irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to read IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
+ /*
+ * Clear the interrupt by flipping the polarity of the
+ * interrupt source lines that just fired
+ */
+ irq_fired = false;
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (reg_irq & rt5677_irq_descs[i].status_mask) {
+ reg_irq ^= rt5677_irq_descs[i].polarity_mask;
+ irq_fired = true;
+ }
}
+ if (!irq_fired)
+ goto exit;
- ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
- &rt5677_irq_chip, &rt5677->irq_data);
+ ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to update IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
- if (ret != 0) {
- dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret);
- return ret;
+ /* Process interrupts */
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if ((reg_irq & rt5677_irq_descs[i].enable_mask) &&
+ (reg_irq & rt5677_irq_descs[i].status_mask)) {
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+ }
}
+exit:
+ mutex_unlock(&rt5677->irq_lock);
+ return IRQ_HANDLED;
+}
+
+static void rt5677_irq_work(struct work_struct *work)
+{
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, irq_work.work);
+
+ rt5677_irq(0, rt5677);
+}
+
+static void rt5677_irq_bus_lock(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&rt5677->irq_lock);
+}
+
+static void rt5677_irq_bus_sync_unlock(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+ regmap_update_bits(rt5677->regmap, RT5677_IRQ_CTRL1,
+ RT5677_EN_IRQ_GPIO_JD1 | RT5677_EN_IRQ_GPIO_JD2 |
+ RT5677_EN_IRQ_GPIO_JD3, rt5677->irq_en);
+ mutex_unlock(&rt5677->irq_lock);
+}
+
+static void rt5677_irq_enable(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ rt5677->irq_en |= rt5677_irq_descs[data->hwirq].enable_mask;
+}
+
+static void rt5677_irq_disable(struct irq_data *data)
+{
+ struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
+
+ rt5677->irq_en &= ~rt5677_irq_descs[data->hwirq].enable_mask;
+}
+
+static struct irq_chip rt5677_irq_chip = {
+ .name = "rt5677_irq_chip",
+ .irq_bus_lock = rt5677_irq_bus_lock,
+ .irq_bus_sync_unlock = rt5677_irq_bus_sync_unlock,
+ .irq_disable = rt5677_irq_disable,
+ .irq_enable = rt5677_irq_enable,
+};
+
+static int rt5677_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct rt5677_priv *rt5677 = h->host_data;
+
+ irq_set_chip_data(virq, rt5677);
+ irq_set_chip(virq, &rt5677_irq_chip);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
return 0;
}
-static void rt5677_free_irq(struct i2c_client *i2c)
+
+static const struct irq_domain_ops rt5677_domain_ops = {
+ .map = rt5677_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static void rt5677_init_irq(struct i2c_client *i2c)
+{
+ struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ int ret;
+ unsigned int jd_mask = 0, jd_val = 0;
+
+ /* No irq has been assigned to the codec */
+ if (!i2c->irq)
+ return;
+
+ mutex_init(&rt5677->irq_lock);
+ INIT_DELAYED_WORK(&rt5677->irq_work, rt5677_irq_work);
+
+ /*
+ * Select RC as the debounce clock so that GPIO works even when
+ * MCLK is gated which happens when there is no audio stream
+ * (SND_SOC_BIAS_OFF).
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+ RT5677_IRQ_DEBOUNCE_SEL_MASK,
+ RT5677_IRQ_DEBOUNCE_SEL_RC);
+ /* Enable auto power on RC when GPIO states are changed */
+ regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL1, 0xff, 0xff);
+
+ /* Select and enable jack detection sources per platform data */
+ if (rt5677->pdata.jd1_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD1_MASK;
+ jd_val |= rt5677->pdata.jd1_gpio << RT5677_SEL_GPIO_JD1_SFT;
+ }
+ if (rt5677->pdata.jd2_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD2_MASK;
+ jd_val |= rt5677->pdata.jd2_gpio << RT5677_SEL_GPIO_JD2_SFT;
+ }
+ if (rt5677->pdata.jd3_gpio) {
+ jd_mask |= RT5677_SEL_GPIO_JD3_MASK;
+ jd_val |= rt5677->pdata.jd3_gpio << RT5677_SEL_GPIO_JD3_SFT;
+ }
+ regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1, jd_mask, jd_val);
+
+ /* Set GPIO1 pin to be IRQ output */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
+
+ /* Ready to listen for interrupts */
+ rt5677->domain = irq_domain_add_linear(i2c->dev.of_node,
+ RT5677_IRQ_NUM, &rt5677_domain_ops, rt5677);
+ if (!rt5677->domain) {
+ dev_err(&i2c->dev, "Failed to create IRQ domain\n");
+ return;
+ }
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5677_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "rt5677", i2c);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ return;
+ }
+}
+
+static void rt5677_irq_exit(struct i2c_client *i2c)
{
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
- if (rt5677->irq_data)
- regmap_del_irq_chip(i2c->irq, rt5677->irq_data);
+ cancel_delayed_work_sync(&rt5677->irq_work);
}
static int rt5677_i2c_probe(struct i2c_client *i2c)
@@ -5259,7 +5384,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
static int rt5677_i2c_remove(struct i2c_client *i2c)
{
- rt5677_free_irq(i2c);
+ rt5677_irq_exit(i2c);
rt5677_free_gpio(i2c);
return 0;
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index 183d92b030459f..9c73bea8635508 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1636,6 +1636,12 @@
#define RT5677_GPIO6_P_NOR (0x0 << 0)
#define RT5677_GPIO6_P_INV (0x1 << 0)
+/* General Control (0xfa) */
+#define RT5677_IRQ_DEBOUNCE_SEL_MASK (0x3 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_MCLK (0x0 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_RC (0x1 << 3)
+#define RT5677_IRQ_DEBOUNCE_SEL_SLIM (0x2 << 3)
+
/* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */
#define RT5677_DSP_IB_01_H (0x1 << 15)
#define RT5677_DSP_IB_01_H_SFT 15
@@ -1713,6 +1719,7 @@ enum {
RT5677_IRQ_JD1,
RT5677_IRQ_JD2,
RT5677_IRQ_JD3,
+ RT5677_IRQ_NUM,
};
enum rt5677_type {
@@ -1811,9 +1818,14 @@ struct rt5677_priv {
struct gpio_chip gpio_chip;
#endif
bool dsp_vad_en;
- struct regmap_irq_chip_data *irq_data;
bool is_dsp_mode;
bool is_vref_slow;
+
+ /* Interrupt handling */
+ struct irq_domain *domain;
+ struct mutex irq_lock;
+ unsigned int irq_en;
+ struct delayed_work irq_work;
};
int rt5677_sel_asrc_clk_src(struct snd_soc_component *component,
--
2.21.0.392.gf8f6787159e-goog
Headphone/mic jack detection doesn't work on the Chromebook Pixel 2015.
This patch changes the irq implementation to support polarity flipping
and fixes the configuration code so that correct GPIO pins are read
from ACPI.
With this series, plugging and unplugging the headphone jack switches
between headphones and speakers automatically, and headset microphones
are also detected.
v3:
- Update commit message for patch 1/3 to clarify why we implement
our own irq_chip.
v2:
- Split IRQ change into two patches: adding and fixing potential race
- Change config reading code to try both DT and ACPI style names
Ben Zhang (2):
ASoC: rt5677: allow multiple interrupt sources
ASoC: rt5677: handle concurrent interrupts
Fletcher Woodruff (1):
ASoC: rt5677: fall back to DT prop names on error
sound/soc/codecs/rt5677.c | 347 +++++++++++++++++++++++++++-----------
sound/soc/codecs/rt5677.h | 14 +-
2 files changed, 257 insertions(+), 104 deletions(-)
--
2.21.0.392.gf8f6787159e-goog
On Mon, Apr 15, 2019 at 01:45:56PM -0600, Fletcher Woodruff wrote:
> Headphone/mic jack detection doesn't work on the Chromebook Pixel 2015.
>
> This patch changes the irq implementation to support polarity flipping
> and fixes the configuration code so that correct GPIO pins are read
> from ACPI.
I only have patch 1 here...
The rt5677 driver uses ACPI-style property names to read from the
device API. However, these do not match the property names in _DSD
used on the Chromebook Pixel 2015, which are closer to the Device Tree
style. Unify the two functions for reading from the device API so that
they try ACPI-style names first and fall back to the DT names on error.
With this patch, plugging and unplugging the headphone jack switches
between headphones and speakers automatically.
Signed-off-by: Fletcher Woodruff <[email protected]>
---
sound/soc/codecs/rt5677.c | 74 +++++++++++++++++++--------------------
1 file changed, 37 insertions(+), 37 deletions(-)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 6bff0df53c6c6b..38e99580f09acd 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -4998,48 +4998,50 @@ static const struct acpi_device_id rt5677_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match);
-static void rt5677_read_acpi_properties(struct rt5677_priv *rt5677,
+static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
struct device *dev)
{
u32 val;
- if (!device_property_read_u32(dev, "DCLK", &val))
- rt5677->pdata.dmic2_clk_pin = val;
+ rt5677->pdata.in1_diff =
+ device_property_read_bool(dev, "IN1") ||
+ device_property_read_bool(dev, "realtek,in1-differential");
- rt5677->pdata.in1_diff = device_property_read_bool(dev, "IN1");
- rt5677->pdata.in2_diff = device_property_read_bool(dev, "IN2");
- rt5677->pdata.lout1_diff = device_property_read_bool(dev, "OUT1");
- rt5677->pdata.lout2_diff = device_property_read_bool(dev, "OUT2");
- rt5677->pdata.lout3_diff = device_property_read_bool(dev, "OUT3");
+ rt5677->pdata.in2_diff =
+ device_property_read_bool(dev, "IN2") ||
+ device_property_read_bool(dev, "realtek,in2-differential");
- device_property_read_u32(dev, "JD1", &rt5677->pdata.jd1_gpio);
- device_property_read_u32(dev, "JD2", &rt5677->pdata.jd2_gpio);
- device_property_read_u32(dev, "JD3", &rt5677->pdata.jd3_gpio);
-}
+ rt5677->pdata.lout1_diff =
+ device_property_read_bool(dev, "OUT1") ||
+ device_property_read_bool(dev, "realtek,lout1-differential");
-static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
- struct device *dev)
-{
- rt5677->pdata.in1_diff = device_property_read_bool(dev,
- "realtek,in1-differential");
- rt5677->pdata.in2_diff = device_property_read_bool(dev,
- "realtek,in2-differential");
- rt5677->pdata.lout1_diff = device_property_read_bool(dev,
- "realtek,lout1-differential");
- rt5677->pdata.lout2_diff = device_property_read_bool(dev,
- "realtek,lout2-differential");
- rt5677->pdata.lout3_diff = device_property_read_bool(dev,
- "realtek,lout3-differential");
+ rt5677->pdata.lout2_diff =
+ device_property_read_bool(dev, "OUT2") ||
+ device_property_read_bool(dev, "realtek,lout2-differential");
+
+ rt5677->pdata.lout3_diff =
+ device_property_read_bool(dev, "OUT3") ||
+ device_property_read_bool(dev, "realtek,lout3-differential");
device_property_read_u8_array(dev, "realtek,gpio-config",
- rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
-
- device_property_read_u32(dev, "realtek,jd1-gpio",
- &rt5677->pdata.jd1_gpio);
- device_property_read_u32(dev, "realtek,jd2-gpio",
- &rt5677->pdata.jd2_gpio);
- device_property_read_u32(dev, "realtek,jd3-gpio",
- &rt5677->pdata.jd3_gpio);
+ rt5677->pdata.gpio_config,
+ RT5677_GPIO_NUM);
+
+ if (!device_property_read_u32(dev, "DCLK", &val) ||
+ !device_property_read_u32(dev, "realtek,dmic2_clk_pin", &val))
+ rt5677->pdata.dmic2_clk_pin = val;
+
+ if (!device_property_read_u32(dev, "JD1", &val) ||
+ !device_property_read_u32(dev, "realtek,jd1-gpio", &val))
+ rt5677->pdata.jd1_gpio = val;
+
+ if (!device_property_read_u32(dev, "JD2", &val) ||
+ !device_property_read_u32(dev, "realtek,jd2-gpio", &val))
+ rt5677->pdata.jd2_gpio = val;
+
+ if (!device_property_read_u32(dev, "JD3", &val) ||
+ !device_property_read_u32(dev, "realtek,jd3-gpio", &val))
+ rt5677->pdata.jd3_gpio = val;
}
struct rt5677_irq_desc {
@@ -5284,20 +5286,18 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
match_id = of_match_device(rt5677_of_match, &i2c->dev);
if (match_id)
rt5677->type = (enum rt5677_type)match_id->data;
-
- rt5677_read_device_properties(rt5677, &i2c->dev);
} else if (ACPI_HANDLE(&i2c->dev)) {
const struct acpi_device_id *acpi_id;
acpi_id = acpi_match_device(rt5677_acpi_match, &i2c->dev);
if (acpi_id)
rt5677->type = (enum rt5677_type)acpi_id->driver_data;
-
- rt5677_read_acpi_properties(rt5677, &i2c->dev);
} else {
return -EINVAL;
}
+ rt5677_read_device_properties(rt5677, &i2c->dev);
+
/* pow-ldo2 and reset are optional. The codec pins may be statically
* connected on the board without gpios. If the gpio device property
* isn't specified, devm_gpiod_get_optional returns NULL.
--
2.21.0.392.gf8f6787159e-goog
On Thu, Apr 18, 2019 at 3:07 AM Mark Brown <[email protected]> wrote:
>
> I only have patch 1 here...
Apologies, patches 2/3 were unchanged so I did not resend. I'll add
them to the chain.
From: Ben Zhang <[email protected]>
The rt5677 driver writes to the IRQ control register within the IRQ
handler in order to flip the polarity of the interrupts that have been
signalled. If an interrupt fires in the interval between the
regmap_read and the regmap_write, it will not trigger a new call to
rt5677_irq.
Add a bounded loop to rt5677_irq that keeps checking interrupts until
none are seen, so that any interrupts that are signalled in that
interval are correctly handled.
Signed-off-by: Ben Zhang <[email protected]>
Signed-off-by: Fletcher Woodruff <[email protected]>
---
sound/soc/codecs/rt5677.c | 84 +++++++++++++++++++++++----------------
1 file changed, 50 insertions(+), 34 deletions(-)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index ef50314ac61eb5..6bff0df53c6c6b 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5070,47 +5070,63 @@ static irqreturn_t rt5677_irq(int unused, void *data)
{
struct i2c_client *i2c = data;
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
- int ret = 0, i, reg_irq, virq;
+ int ret = 0, loop, i, reg_irq, virq;
bool irq_fired;
mutex_lock(&rt5677->irq_lock);
- /* Read interrupt status */
- ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, ®_irq);
- if (ret) {
- dev_err(&i2c->dev,
- "Failed to read IRQ status: %d\n",
- ret);
- goto exit;
- }
/*
- * Clear the interrupt by flipping the polarity of the
- * interrupt source lines that just fired
+ * Loop to handle interrupts until the last i2c read shows no pending
+ * irqs. The interrupt line is shared by multiple interrupt sources.
+ * After the regmap_read() below, a new interrupt source line may
+ * become high before the regmap_write() finishes, so there isn't a
+ * rising edge on the shared interrupt line for the new interrupt. Thus,
+ * the loop is needed to avoid missing irqs.
+ *
+ * A safeguard of 20 loops is used to avoid hanging in the irq handler
+ * if there is something wrong with the interrupt status update. The
+ * interrupt sources here are audio jack plug/unplug events which
+ * shouldn't happen at a high frequency for a long period of time.
+ * Empirically, more than 3 loops have never been seen.
*/
- irq_fired = false;
- for (i = 0; i < RT5677_IRQ_NUM; i++) {
- if (reg_irq & rt5677_irq_descs[i].status_mask) {
- reg_irq ^= rt5677_irq_descs[i].polarity_mask;
- irq_fired = true;
+ for (loop = 0; loop < 20; loop++) {
+ /* Read interrupt status */
+ ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, ®_irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to read IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
+ /*
+ * Clear the interrupt by flipping the polarity of the
+ * interrupt source lines that just fired
+ */
+ irq_fired = false;
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (reg_irq & rt5677_irq_descs[i].status_mask) {
+ reg_irq ^= rt5677_irq_descs[i].polarity_mask;
+ irq_fired = true;
+ }
+ }
+ if (!irq_fired)
+ goto exit;
+
+ ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to update IRQ status: %d\n",
+ ret);
+ goto exit;
}
- }
- if (!irq_fired)
- goto exit;
-
- ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
- if (ret) {
- dev_err(&i2c->dev,
- "Failed to update IRQ status: %d\n",
- ret);
- goto exit;
- }
- /* Process interrupts */
- for (i = 0; i < RT5677_IRQ_NUM; i++) {
- if ((reg_irq & rt5677_irq_descs[i].enable_mask) &&
- (reg_irq & rt5677_irq_descs[i].status_mask)) {
- virq = irq_find_mapping(rt5677->domain, i);
- if (virq)
- handle_nested_irq(virq);
+ /* Process interrupts */
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if ((reg_irq & rt5677_irq_descs[i].enable_mask) &&
+ (reg_irq & rt5677_irq_descs[i].status_mask)) {
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+ }
}
}
exit:
--
2.21.0.392.gf8f6787159e-goog