int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *interrupt_info,
int sdca_irq, const char *name, irq_handler_t handler,
void *data);
+void sdca_irq_free(struct device *dev, struct sdca_interrupt_info *interrupt_info,
+ int sdca_irq, const char *name, void *data);
int sdca_irq_data_populate(struct device *dev, struct regmap *function_regmap,
struct snd_soc_component *component,
struct sdca_function_data *function,
int sdca_irq_populate(struct sdca_function_data *function,
struct snd_soc_component *component,
struct sdca_interrupt_info *info);
+void sdca_irq_cleanup(struct sdca_function_data *function,
+ struct snd_soc_component *component,
+ struct sdca_interrupt_info *info);
struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev,
struct regmap *regmap, int irq);
if (irq < 0)
return irq;
- ret = devm_request_threaded_irq(dev, irq, NULL, handler,
- IRQF_ONESHOT, name, data);
+ ret = request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, name, data);
if (ret)
return ret;
return 0;
}
+static void sdca_irq_free_locked(struct device *dev, struct sdca_interrupt_info *info,
+ int sdca_irq, const char *name, void *data)
+{
+ int irq;
+
+ irq = regmap_irq_get_virq(info->irq_data, sdca_irq);
+ if (irq < 0)
+ return;
+
+ free_irq(irq, data);
+
+ info->irqs[sdca_irq].irq = 0;
+
+ dev_dbg(dev, "freed irq %d for %s\n", irq, name);
+}
+
/**
* sdca_irq_request - request an individual SDCA interrupt
* @dev: Pointer to the struct device against which things should be allocated.
}
EXPORT_SYMBOL_NS_GPL(sdca_irq_request, "SND_SOC_SDCA");
+/**
+ * sdca_irq_free - free an individual SDCA interrupt
+ * @dev: Pointer to the struct device.
+ * @info: Pointer to the interrupt information structure.
+ * @sdca_irq: SDCA interrupt position.
+ * @name: Name to be given to the IRQ.
+ * @data: Private data pointer that will be passed to the handler.
+ *
+ * Typically this is handled internally by sdca_irq_cleanup, however if
+ * a device requires custom IRQ handling this can be called manually before
+ * calling sdca_irq_cleanup, which will then skip that IRQ whilst processing.
+ */
+void sdca_irq_free(struct device *dev, struct sdca_interrupt_info *info,
+ int sdca_irq, const char *name, void *data)
+{
+ if (sdca_irq < 0 || sdca_irq >= SDCA_MAX_INTERRUPTS)
+ return;
+
+ guard(mutex)(&info->irq_lock);
+
+ sdca_irq_free_locked(dev, info, sdca_irq, name, data);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_free, "SND_SOC_SDCA");
+
/**
* sdca_irq_data_populate - Populate common interrupt data
* @dev: Pointer to the Function device.
if (!dev)
return -ENODEV;
- name = devm_kasprintf(dev, GFP_KERNEL, "%s %s %s", function->desc->name,
- entity->label, control->label);
+ name = kasprintf(GFP_KERNEL, "%s %s %s", function->desc->name,
+ entity->label, control->label);
if (!name)
return -ENOMEM;
}
EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA");
+/**
+ * sdca_irq_cleanup - Free all the individual IRQs for an SDCA Function
+ * @function: Pointer to the SDCA Function.
+ * @component: Pointer to the ASoC component for the Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * Typically this would be called from the driver for a single SDCA Function.
+ */
+void sdca_irq_cleanup(struct sdca_function_data *function,
+ struct snd_soc_component *component,
+ struct sdca_interrupt_info *info)
+{
+ struct device *dev = component->dev;
+ int i;
+
+ guard(mutex)(&info->irq_lock);
+
+ for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
+ struct sdca_interrupt *interrupt = &info->irqs[i];
+
+ if (interrupt->function != function || !interrupt->irq)
+ continue;
+
+ sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt);
+
+ kfree(interrupt->name);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA");
+
/**
* sdca_irq_allocate - allocate an SDCA interrupt structure for a device
* @sdev: Device pointer against which things should be allocated.