forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 142
soundwire: SDCA: Generic interrupt support #5365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0 */ | ||
| /* | ||
| * The MIPI SDCA specification is available for public downloads at | ||
| * https://www.mipi.org/mipi-sdca-v1-0-download | ||
| * | ||
| * Copyright (C) 2025 Cirrus Logic, Inc. and | ||
| * Cirrus Logic International Semiconductor Ltd. | ||
| */ | ||
|
|
||
| #ifndef __SDCA_INTERRUPTS_H__ | ||
| #define __SDCA_INTERRUPTS_H__ | ||
|
|
||
| #include <linux/mutex.h> | ||
| #include <linux/interrupt.h> | ||
| #include <linux/regmap.h> | ||
|
|
||
| struct device; | ||
| struct snd_soc_component; | ||
| struct sdca_function_data; | ||
|
|
||
| #define SDCA_MAX_INTERRUPTS 31 /* the last bit is reserved for future extensions */ | ||
|
|
||
| /** | ||
| * struct sdca_interrupt - contains information about a single SDCA interrupt | ||
| * @name: The name of the interrupt. | ||
| * @component: Pointer to the ASoC component owns the interrupt. | ||
| * @function: Pointer to the Function that the interrupt is associated with. | ||
| * @entity: Pointer to the Entity that the interrupt is associated with. | ||
| * @control: Pointer to the Control that the interrupt is associated with. | ||
| * @externally_requested: Internal flag used to check if something has already | ||
| * requested the interrupt. | ||
| */ | ||
| struct sdca_interrupt { | ||
| const char *name; | ||
|
|
||
| struct snd_soc_component *component; | ||
| struct sdca_function_data *function; | ||
| struct sdca_entity *entity; | ||
| struct sdca_control *control; | ||
|
|
||
| bool externally_requested; | ||
| }; | ||
|
|
||
| /** | ||
| * struct sdca_interrupt_info - contains top-level SDCA interrupt information | ||
| * @irq_chip: regmap irq chip structure. | ||
| * @irq_data: regmap irq chip data structure. | ||
| * @irqs: Array of data for each individual IRQ. | ||
| * @irq_lock: Protects access to the list of sdca_interrupt structures. | ||
| */ | ||
| struct sdca_interrupt_info { | ||
| struct regmap_irq_chip irq_chip; | ||
| struct regmap_irq_chip_data *irq_data; | ||
|
|
||
| struct sdca_interrupt irqs[SDCA_MAX_INTERRUPTS]; | ||
|
|
||
| struct mutex irq_lock; /* Protect accesses to irqs list */ | ||
| }; | ||
|
|
||
| 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); | ||
| int sdca_irq_populate(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); | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| # SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o | ||
| snd-soc-sdca-irq-y := sdca_interrupts.o | ||
|
|
||
| obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o | ||
| obj-$(CONFIG_SND_SOC_SDCA_IRQ) += snd-soc-sdca-irq.o |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,255 @@ | ||
| // SPDX-License-Identifier: GPL-2.0 | ||
| /* | ||
| * SDCA IRQ handler library | ||
| * | ||
| * Copyright (C) 2025 Cirrus Logic, Inc. and | ||
| * Cirrus Logic International Semiconductor Ltd. | ||
| * | ||
| * The MIPI SDCA specification is available for public downloads at | ||
| * https://www.mipi.org/mipi-sdca-v1-0-download | ||
| */ | ||
|
|
||
| #include <linux/bits.h> | ||
| #include <linux/cleanup.h> | ||
| #include <linux/device.h> | ||
| #include <linux/interrupt.h> | ||
| #include <linux/regmap.h> | ||
| #include <linux/soundwire/sdw.h> | ||
| #include <linux/soundwire/sdw_registers.h> | ||
| #include <sound/sdca.h> | ||
| #include <sound/sdca_function.h> | ||
| #include <sound/sdca_interrupts.h> | ||
| #include <sound/soc-component.h> | ||
|
|
||
| #define IRQ_SDCA(number) REGMAP_IRQ_REG(number, ((number) / BITS_PER_BYTE), \ | ||
| SDW_SCP_SDCA_INTMASK_SDCA_##number) | ||
|
|
||
| static const struct regmap_irq regmap_irqs[SDCA_MAX_INTERRUPTS] = { | ||
| IRQ_SDCA(0), | ||
| IRQ_SDCA(1), | ||
| IRQ_SDCA(2), | ||
| IRQ_SDCA(3), | ||
| IRQ_SDCA(4), | ||
| IRQ_SDCA(5), | ||
| IRQ_SDCA(6), | ||
| IRQ_SDCA(7), | ||
| IRQ_SDCA(8), | ||
| IRQ_SDCA(9), | ||
| IRQ_SDCA(10), | ||
| IRQ_SDCA(11), | ||
| IRQ_SDCA(12), | ||
| IRQ_SDCA(13), | ||
| IRQ_SDCA(14), | ||
| IRQ_SDCA(15), | ||
| IRQ_SDCA(16), | ||
| IRQ_SDCA(17), | ||
| IRQ_SDCA(18), | ||
| IRQ_SDCA(19), | ||
| IRQ_SDCA(20), | ||
| IRQ_SDCA(21), | ||
| IRQ_SDCA(22), | ||
| IRQ_SDCA(23), | ||
| IRQ_SDCA(24), | ||
| IRQ_SDCA(25), | ||
| IRQ_SDCA(26), | ||
| IRQ_SDCA(27), | ||
| IRQ_SDCA(28), | ||
| IRQ_SDCA(29), | ||
| IRQ_SDCA(30), | ||
| }; | ||
|
|
||
| static const struct regmap_irq_chip sdca_irq_chip = { | ||
| .name = "sdca_irq", | ||
|
|
||
| .status_base = SDW_SCP_SDCA_INT1, | ||
| .unmask_base = SDW_SCP_SDCA_INTMASK1, | ||
| .ack_base = SDW_SCP_SDCA_INT1, | ||
| .num_regs = 4, | ||
|
|
||
| .irqs = regmap_irqs, | ||
| .num_irqs = SDCA_MAX_INTERRUPTS, | ||
|
|
||
| .runtime_pm = true, | ||
| }; | ||
|
|
||
| static irqreturn_t sdca_base_irq_handler(int irq, void *data) | ||
| { | ||
| return IRQ_HANDLED; | ||
| } | ||
|
|
||
| static int sdca_irq_request_locked(struct device *dev, | ||
| struct sdca_interrupt_info *info, | ||
| int sdca_irq, const char *name, | ||
| irq_handler_t handler, void *data) | ||
| { | ||
| int irq; | ||
| int ret; | ||
|
|
||
| irq = regmap_irq_get_virq(info->irq_data, sdca_irq); | ||
| if (irq < 0) | ||
| return irq; | ||
|
|
||
| ret = devm_request_threaded_irq(dev, irq, NULL, handler, | ||
| IRQF_ONESHOT, name, data); | ||
| if (ret) | ||
| return ret; | ||
|
|
||
| dev_dbg(dev, "Requested IRQ %d for %s\n", irq, name); | ||
|
|
||
mstrozek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return 0; | ||
| } | ||
|
|
||
| /** | ||
| * sdca_request_irq - request an individual SDCA interrupt | ||
| * @dev: Pointer to the struct device against which things should be allocated. | ||
| * @interrupt_info: Pointer to the interrupt information structure. | ||
| * @sdca_irq: SDCA interrupt position. | ||
| * @name: Name to be given to the IRQ. | ||
| * @handler: A callback thread function to be called for the IRQ. | ||
| * @data: Private data pointer that will be passed to the handler. | ||
| * | ||
| * Typically this is handled internally by sdca_irq_populate, however if | ||
| * a device requires custom IRQ handling this can be called manually before | ||
| * calling sdca_irq_populate, which will then skip that IRQ whilst processing. | ||
| * | ||
| * Return: Zero on success, and a negative error code on failure. | ||
| */ | ||
| int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *info, | ||
| int sdca_irq, const char *name, irq_handler_t handler, | ||
| void *data) | ||
| { | ||
| int ret; | ||
|
|
||
| if (sdca_irq < 0 || sdca_irq > SDCA_MAX_INTERRUPTS) { | ||
| dev_err(dev, "Bad SDCA IRQ request: %d\n", sdca_irq); | ||
| return -EINVAL; | ||
| } | ||
|
|
||
| guard(mutex)(&info->irq_lock); | ||
|
|
||
| ret = sdca_irq_request_locked(dev, info, sdca_irq, name, handler, data); | ||
| if (ret) { | ||
| dev_err(dev, "Failed to request IRQ %s: %d\n", name, ret); | ||
| return ret; | ||
| } | ||
|
|
||
| info->irqs[sdca_irq].externally_requested = true; | ||
|
|
||
| return 0; | ||
| } | ||
| EXPORT_SYMBOL_NS_GPL(sdca_irq_request, "SND_SOC_SDCA_IRQ"); | ||
|
|
||
| /** | ||
| * sdca_irq_populate - Request all the individual IRQs for an SDCA Function | ||
| * @function: Pointer to the SDCA Function. | ||
| * @component: Pointer to the 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. | ||
| * | ||
| * Return: Zero on success, and a negative error code on failure. | ||
| */ | ||
| int sdca_irq_populate(struct sdca_function_data *function, | ||
| struct snd_soc_component *component, | ||
| struct sdca_interrupt_info *info) | ||
| { | ||
| struct device *dev = component->dev; | ||
| int i, j; | ||
|
|
||
| guard(mutex)(&info->irq_lock); | ||
|
|
||
| for (i = 0; i < function->num_entities; i++) { | ||
| struct sdca_entity *entity = &function->entities[i]; | ||
|
|
||
| for (j = 0; j < entity->num_controls; j++) { | ||
| struct sdca_control *control = &entity->controls[j]; | ||
| int irq = control->interrupt_position; | ||
| struct sdca_interrupt *interrupt; | ||
| const char *name; | ||
| int ret; | ||
|
|
||
| if (irq == SDCA_NO_INTERRUPT) { | ||
| continue; | ||
| } else if (irq < 0 || irq >= SDCA_MAX_INTERRUPTS) { | ||
| dev_err(dev, "Bad SDCA IRQ position: %d\n", irq); | ||
| return -EINVAL; | ||
| } | ||
|
|
||
| interrupt = &info->irqs[irq]; | ||
|
|
||
| if (interrupt->externally_requested) { | ||
| dev_dbg(dev, | ||
| "Skipping IRQ %d, externally requested\n", | ||
| irq); | ||
| continue; | ||
| } | ||
|
|
||
| name = devm_kasprintf(dev, GFP_KERNEL, "%s %s %s", | ||
| function->desc->name, | ||
| entity->label, control->label); | ||
| if (!name) | ||
| return -ENOMEM; | ||
|
|
||
| interrupt->name = name; | ||
| interrupt->component = component; | ||
| interrupt->function = function; | ||
| interrupt->entity = entity; | ||
| interrupt->control = control; | ||
|
|
||
| ret = sdca_irq_request_locked(dev, info, irq, name, | ||
| sdca_base_irq_handler, | ||
| interrupt); | ||
| if (ret) { | ||
| dev_err(dev, "Failed to request IRQ %s: %d\n", | ||
| name, ret); | ||
| return ret; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
| EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA_IRQ"); | ||
|
|
||
| /** | ||
| * sdca_irq_allocate - allocate an SDCA interrupt structure for a device | ||
| * @dev: Device pointer against which things should be allocated. | ||
| * @regmap: regmap to be used for accessing the SDCA IRQ registers. | ||
| * @irq: The interrupt number. | ||
| * | ||
| * Typically this would be called from the top level driver for the whole | ||
| * SDCA device, as only a single instance is required across all Functions | ||
| * on the device. | ||
| * | ||
| * Return: A pointer to the allocated sdca_interrupt_info struct, or an | ||
| * error code. | ||
| */ | ||
| struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, | ||
| struct regmap *regmap, int irq) | ||
| { | ||
| struct sdca_interrupt_info *info; | ||
| int ret; | ||
|
|
||
| info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); | ||
| if (!info) | ||
| return ERR_PTR(-ENOMEM); | ||
|
|
||
| info->irq_chip = sdca_irq_chip; | ||
|
|
||
| devm_mutex_init(dev, &info->irq_lock); | ||
|
|
||
| ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT, 0, | ||
| &info->irq_chip, &info->irq_data); | ||
| if (ret) { | ||
| dev_err(dev, "Failed to register IRQ chip: %d\n", ret); | ||
| return ERR_PTR(ret); | ||
| } | ||
|
|
||
| dev_dbg(dev, "Registered on IRQ %d\n", irq); | ||
|
|
||
| return info; | ||
| } | ||
| EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA_IRQ"); | ||
|
|
||
| MODULE_LICENSE("GPL"); | ||
| MODULE_DESCRIPTION("SDCA IRQ handler library"); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.