ASoC: cs35l45: IRQ support
authorVlad.Karpovich <vkarpovi@opensource.cirrus.com>
Wed, 15 Mar 2023 15:47:20 +0000 (10:47 -0500)
committerMark Brown <broonie@kernel.org>
Tue, 21 Mar 2023 12:46:41 +0000 (12:46 +0000)
Adds IRQ handlers

Signed-off-by: Vlad Karpovich <vkarpovi@opensource.cirrus.com>
Link: https://lore.kernel.org/r/167933510218.26.11092784685990338045@mailman-core.alsa-project.org
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/cs35l45-i2c.c
sound/soc/codecs/cs35l45-spi.c
sound/soc/codecs/cs35l45-tables.c
sound/soc/codecs/cs35l45.c
sound/soc/codecs/cs35l45.h

index 1117df4..33fa6d5 100644 (file)
@@ -32,6 +32,7 @@ static int cs35l45_i2c_probe(struct i2c_client *client)
        }
 
        cs35l45->dev = dev;
+       cs35l45->irq = client->irq;
 
        return cs35l45_probe(cs35l45);
 }
index ffaca07..6166cff 100644 (file)
@@ -32,6 +32,7 @@ static int cs35l45_spi_probe(struct spi_device *spi)
        }
 
        cs35l45->dev = dev;
+       cs35l45->irq = spi->irq;
 
        return cs35l45_probe(cs35l45);
 }
index 997ea41..eb9fb07 100644 (file)
@@ -64,6 +64,25 @@ static const struct reg_default cs35l45_defaults[] = {
        { CS35L45_ASPTX4_INPUT,                 0x00000028 },
        { CS35L45_ASPTX5_INPUT,                 0x00000048 },
        { CS35L45_AMP_PCM_CONTROL,              0x00100000 },
+       { CS35L45_IRQ1_CFG,                     0x00000000 },
+       { CS35L45_IRQ1_MASK_1,                  0xBFEFFFBF },
+       { CS35L45_IRQ1_MASK_2,                  0xFFFFFFFF },
+       { CS35L45_IRQ1_MASK_3,                  0xFFFF87FF },
+       { CS35L45_IRQ1_MASK_4,                  0xF8FFFFFF },
+       { CS35L45_IRQ1_MASK_5,                  0x0EF80000 },
+       { CS35L45_IRQ1_MASK_6,                  0x00000000 },
+       { CS35L45_IRQ1_MASK_7,                  0xFFFFFF78 },
+       { CS35L45_IRQ1_MASK_8,                  0x00003FFF },
+       { CS35L45_IRQ1_MASK_9,                  0x00000000 },
+       { CS35L45_IRQ1_MASK_10,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_11,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_12,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_13,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_14,                 0x00000001 },
+       { CS35L45_IRQ1_MASK_15,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_16,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_17,                 0x00000000 },
+       { CS35L45_IRQ1_MASK_18,                 0x3FE5D0FF },
        { CS35L45_GPIO1_CTRL1,                  0x81000001 },
        { CS35L45_GPIO2_CTRL1,                  0x81000001 },
        { CS35L45_GPIO3_CTRL1,                  0x81000001 },
@@ -100,7 +119,11 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
        case CS35L45_ASPTX5_INPUT:
        case CS35L45_AMP_PCM_CONTROL:
        case CS35L45_AMP_PCM_HPF_TST:
-       case CS35L45_IRQ1_EINT_4:
+       case CS35L45_IRQ1_CFG:
+       case CS35L45_IRQ1_STATUS:
+       case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+       case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
+       case CS35L45_IRQ1_MASK_1 ... CS35L45_IRQ1_MASK_18:
        case CS35L45_GPIO_STATUS1:
        case CS35L45_GPIO1_CTRL1:
        case CS35L45_GPIO2_CTRL1:
@@ -119,7 +142,9 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
        case CS35L45_GLOBAL_ENABLES:
        case CS35L45_ERROR_RELEASE:
        case CS35L45_AMP_PCM_HPF_TST:   /* not cachable */
-       case CS35L45_IRQ1_EINT_4:
+       case CS35L45_IRQ1_STATUS:
+       case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+       case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
        case CS35L45_GPIO_STATUS1:
                return true;
        default:
index c87dccb..a7095da 100644 (file)
@@ -586,10 +586,13 @@ static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
                                           val << CS35L45_GPIO_CTRL_SHIFT);
 
                ret = of_property_read_u32(child, "gpio-invert", &val);
-               if (!ret)
+               if (!ret) {
                        regmap_update_bits(cs35l45->regmap, pad_regs[i],
                                           CS35L45_GPIO_INVERT_MASK,
                                           val << CS35L45_GPIO_INVERT_SHIFT);
+                       if (i == 1)
+                               cs35l45->irq_invert = val;
+               }
 
                of_node_put(child);
        }
@@ -604,6 +607,78 @@ static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
        return 0;
 }
 
+static irqreturn_t cs35l45_pll_unlock(int irq, void *data)
+{
+       struct cs35l45_private *cs35l45 = data;
+
+       dev_dbg(cs35l45->dev, "PLL unlock detected!");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_pll_lock(int irq, void *data)
+{
+       struct cs35l45_private *cs35l45 = data;
+
+       dev_dbg(cs35l45->dev, "PLL lock detected!");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data);
+
+static const struct cs35l45_irq cs35l45_irqs[] = {
+       CS35L45_IRQ(AMP_SHORT_ERR, "Amplifier short error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(UVLO_VDDBATT_ERR, "VDDBATT undervoltage error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(BST_SHORT_ERR, "Boost inductor error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(BST_UVP_ERR, "Boost undervoltage error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(TEMP_ERR, "Overtemperature error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(AMP_CAL_ERR, "Amplifier calibration error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(UVLO_VDDLV_ERR, "LV threshold detector error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(GLOBAL_ERROR, "Global error", cs35l45_spk_safe_err),
+       CS35L45_IRQ(DSP_WDT_EXPIRE, "DSP Watchdog Timer", cs35l45_spk_safe_err),
+       CS35L45_IRQ(PLL_UNLOCK_FLAG_RISE, "PLL unlock", cs35l45_pll_unlock),
+       CS35L45_IRQ(PLL_LOCK_FLAG, "PLL lock", cs35l45_pll_lock),
+};
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data)
+{
+       struct cs35l45_private *cs35l45 = data;
+       int i;
+
+       i = irq - regmap_irq_get_virq(cs35l45->irq_data, 0);
+
+       dev_err(cs35l45->dev, "%s condition detected!\n", cs35l45_irqs[i].name);
+
+       return IRQ_HANDLED;
+}
+
+static const struct regmap_irq cs35l45_reg_irqs[] = {
+       CS35L45_REG_IRQ(IRQ1_EINT_1, AMP_SHORT_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_1, UVLO_VDDBATT_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_1, BST_SHORT_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_1, BST_UVP_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_1, TEMP_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_3, AMP_CAL_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_18, UVLO_VDDLV_ERR),
+       CS35L45_REG_IRQ(IRQ1_EINT_18, GLOBAL_ERROR),
+       CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_WDT_EXPIRE),
+       CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_UNLOCK_FLAG_RISE),
+       CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_LOCK_FLAG),
+};
+
+static const struct regmap_irq_chip cs35l45_regmap_irq_chip = {
+       .name = "cs35l45 IRQ1 Controller",
+       .main_status = CS35L45_IRQ1_STATUS,
+       .status_base = CS35L45_IRQ1_EINT_1,
+       .mask_base = CS35L45_IRQ1_MASK_1,
+       .ack_base = CS35L45_IRQ1_EINT_1,
+       .num_regs = 18,
+       .irqs = cs35l45_reg_irqs,
+       .num_irqs = ARRAY_SIZE(cs35l45_reg_irqs),
+       .runtime_pm = true,
+};
+
 static int cs35l45_initialize(struct cs35l45_private *cs35l45)
 {
        struct device *dev = cs35l45->dev;
@@ -660,7 +735,8 @@ static int cs35l45_initialize(struct cs35l45_private *cs35l45)
 int cs35l45_probe(struct cs35l45_private *cs35l45)
 {
        struct device *dev = cs35l45->dev;
-       int ret;
+       unsigned long irq_pol = IRQF_ONESHOT | IRQF_SHARED;
+       int ret, i, irq;
 
        cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt");
        if (IS_ERR(cs35l45->vdd_batt))
@@ -705,6 +781,37 @@ int cs35l45_probe(struct cs35l45_private *cs35l45)
        if (ret < 0)
                goto err_reset;
 
+       if (cs35l45->irq) {
+               if (cs35l45->irq_invert)
+                       irq_pol |= IRQF_TRIGGER_HIGH;
+               else
+                       irq_pol |= IRQF_TRIGGER_LOW;
+
+               ret = devm_regmap_add_irq_chip(dev, cs35l45->regmap, cs35l45->irq, irq_pol, 0,
+                                              &cs35l45_regmap_irq_chip, &cs35l45->irq_data);
+               if (ret) {
+                       dev_err(dev, "Failed to register IRQ chip: %d\n", ret);
+                       goto err_reset;
+               }
+
+               for (i = 0; i < ARRAY_SIZE(cs35l45_irqs); i++) {
+                       irq = regmap_irq_get_virq(cs35l45->irq_data, cs35l45_irqs[i].irq);
+                       if (irq < 0) {
+                               dev_err(dev, "Failed to get %s\n", cs35l45_irqs[i].name);
+                               ret = irq;
+                               goto err_reset;
+                       }
+
+                       ret = devm_request_threaded_irq(dev, irq, NULL, cs35l45_irqs[i].handler,
+                                                       irq_pol, cs35l45_irqs[i].name, cs35l45);
+                       if (ret) {
+                               dev_err(dev, "Failed to request IRQ %s: %d\n",
+                                       cs35l45_irqs[i].name, ret);
+                               goto err_reset;
+                       }
+               }
+       }
+
        ret = devm_snd_soc_register_component(dev, &cs35l45_component,
                                              cs35l45_dai,
                                              ARRAY_SIZE(cs35l45_dai));
index f3a54fc..ce92f50 100644 (file)
 #define CS35L45_LDPM_CONFIG                    0x00006404
 #define CS35L45_AMP_PCM_CONTROL                        0x00007000
 #define CS35L45_AMP_PCM_HPF_TST                        0x00007004
+#define CS35L45_IRQ1_CFG                       0x0000E000
+#define CS35L45_IRQ1_STATUS                    0x0000E004
+#define CS35L45_IRQ1_EINT_1                    0x0000E010
+#define CS35L45_IRQ1_EINT_2                    0x0000E014
+#define CS35L45_IRQ1_EINT_3                    0x0000E018
 #define CS35L45_IRQ1_EINT_4                    0x0000E01C
+#define CS35L45_IRQ1_EINT_5                    0x0000E020
+#define CS35L45_IRQ1_EINT_7                    0x0000E028
+#define CS35L45_IRQ1_EINT_8                    0x0000E02C
+#define CS35L45_IRQ1_EINT_18                   0x0000E054
+#define CS35L45_IRQ1_STS_1                     0x0000E090
+#define CS35L45_IRQ1_STS_2                     0x0000E094
+#define CS35L45_IRQ1_STS_3                     0x0000E098
+#define CS35L45_IRQ1_STS_4                     0x0000E09C
+#define CS35L45_IRQ1_STS_5                     0x0000E0A0
+#define CS35L45_IRQ1_STS_7                     0x0000E0A8
+#define CS35L45_IRQ1_STS_8                     0x0000E0AC
+#define CS35L45_IRQ1_STS_18                    0x0000E0D4
+#define CS35L45_IRQ1_MASK_1                    0x0000E110
+#define CS35L45_IRQ1_MASK_2                    0x0000E114
+#define CS35L45_IRQ1_MASK_3                    0x0000E118
+#define CS35L45_IRQ1_MASK_4                    0x0000E11C
+#define CS35L45_IRQ1_MASK_5                    0x0000E120
+#define CS35L45_IRQ1_MASK_6                    0x0000E124
+#define CS35L45_IRQ1_MASK_7                    0x0000E128
+#define CS35L45_IRQ1_MASK_8                    0x0000E12C
+#define CS35L45_IRQ1_MASK_9                    0x0000E130
+#define CS35L45_IRQ1_MASK_10                   0x0000E134
+#define CS35L45_IRQ1_MASK_11                   0x0000E138
+#define CS35L45_IRQ1_MASK_12                   0x0000E13C
+#define CS35L45_IRQ1_MASK_13                   0x0000E140
+#define CS35L45_IRQ1_MASK_14                   0x0000E144
+#define CS35L45_IRQ1_MASK_15                   0x0000E148
+#define CS35L45_IRQ1_MASK_16                   0x0000E14C
+#define CS35L45_IRQ1_MASK_17                   0x0000E150
+#define CS35L45_IRQ1_MASK_18                   0x0000E154
 #define CS35L45_GPIO_STATUS1                   0x0000F000
 #define CS35L45_GPIO1_CTRL1                    0x0000F008
 #define CS35L45_GPIO2_CTRL1                    0x0000F00C
 #define CS35L45_GPIO_INVERT_SHIFT              19
 #define CS35L45_GPIO_INVERT_MASK               BIT(19)
 
+/* CS35L45_IRQ1_EINT_1 */
+#define CS35L45_BST_UVP_ERR_SHIFT              7
+#define CS35L45_BST_UVP_ERR_MASK               BIT(7)
+#define CS35L45_BST_SHORT_ERR_SHIFT            8
+#define CS35L45_BST_SHORT_ERR_MASK             BIT(8)
+#define CS35L45_TEMP_ERR_SHIFT                 17
+#define CS35L45_TEMP_ERR_MASK                  BIT(17)
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_SHIFT     22
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_MASK      BIT(22)
+#define CS35L45_UVLO_VDDBATT_ERR_SHIFT 29
+#define CS35L45_UVLO_VDDBATT_ERR_MASK          BIT(29)
+#define CS35L45_AMP_SHORT_ERR_SHIFT            31
+#define CS35L45_AMP_SHORT_ERR_MASK             BIT(31)
+
+/* CS35L45_IRQ1_EINT_2 */
+#define CS35L45_DSP_WDT_EXPIRE_SHIFT           4
+#define CS35L45_DSP_WDT_EXPIRE_MASK            BIT(4)
+
+/* CS35L45_IRQ1_EINT_3 */
+#define CS35L45_PLL_LOCK_FLAG_SHIFT            1
+#define CS35L45_PLL_LOCK_FLAG_MASK             BIT(1)
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_SHIFT     4
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_MASK      BIT(4)
+#define CS35L45_AMP_CAL_ERR_SHIFT              25
+#define CS35L45_AMP_CAL_ERR_MASK               BIT(25)
+
+/* CS35L45_IRQ1_EINT_18 */
+#define CS35L45_GLOBAL_ERROR_SHIFT             15
+#define CS35L45_GLOBAL_ERROR_MASK              BIT(15)
+#define CS35L45_UVLO_VDDLV_ERR_SHIFT           16
+#define CS35L45_UVLO_VDDLV_ERR_MASK            BIT(16)
+
 /* Mixer sources */
 #define CS35L45_PCM_SRC_MASK                   0x7F
 #define CS35L45_PCM_SRC_ZERO                   0x00
                       SNDRV_PCM_RATE_88200 | \
                       SNDRV_PCM_RATE_96000)
 
+/*
+ * IRQs
+ */
+#define CS35L45_IRQ(_irq, _name, _hand)                \
+       {                                       \
+               .irq = CS35L45_ ## _irq ## _IRQ,\
+               .name = _name,                  \
+               .handler = _hand,               \
+       }
+
+struct cs35l45_irq {
+       int irq;
+       const char *name;
+       irqreturn_t (*handler)(int irq, void *data);
+};
+
+#define CS35L45_REG_IRQ(_reg, _irq)                                    \
+       [CS35L45_ ## _irq ## _IRQ] = {                                  \
+               .reg_offset = (CS35L45_ ## _reg) - CS35L45_IRQ1_EINT_1, \
+               .mask = CS35L45_ ## _irq ## _MASK                       \
+       }
+
+enum cs35l45_irq_list {
+       CS35L45_AMP_SHORT_ERR_IRQ,
+       CS35L45_UVLO_VDDBATT_ERR_IRQ,
+       CS35L45_BST_SHORT_ERR_IRQ,
+       CS35L45_BST_UVP_ERR_IRQ,
+       CS35L45_TEMP_ERR_IRQ,
+       CS35L45_AMP_CAL_ERR_IRQ,
+       CS35L45_UVLO_VDDLV_ERR_IRQ,
+       CS35L45_GLOBAL_ERROR_IRQ,
+       CS35L45_DSP_WDT_EXPIRE_IRQ,
+       CS35L45_PLL_UNLOCK_FLAG_RISE_IRQ,
+       CS35L45_PLL_LOCK_FLAG_IRQ,
+       CS35L45_NUM_IRQ
+};
+
 struct cs35l45_private {
        struct device *dev;
        struct regmap *regmap;
@@ -227,6 +331,9 @@ struct cs35l45_private {
        bool sysclk_set;
        u8 slot_width;
        u8 slot_count;
+       int irq_invert;
+       int irq;
+       struct regmap_irq_chip_data *irq_data;
 };
 
 extern const struct dev_pm_ops cs35l45_pm_ops;