Skip to content

Commit f44af96

Browse files
committed
soundwire: SDCA: Generic interrupt support
Change-Id: I39ba6bc96f6d899d0a3b4b3eb7a69bf44203925d Signed-off-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
1 parent b64c933 commit f44af96

File tree

6 files changed

+302
-0
lines changed

6 files changed

+302
-0
lines changed

include/sound/sdca.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
#include <linux/types.h>
1313
#include <linux/kconfig.h>
14+
#include <linux/regmap.h>
15+
#include <linux/interrupt.h>
1416

1517
struct sdw_slave;
1618

include/sound/sdca_function.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
#include <linux/bits.h>
1313
#include <linux/types.h>
14+
#include <linux/interrupt.h>
15+
#include <sound/sdca.h>
16+
#include <sound/sdca_interrupts.h>
1417

1518
struct device;
1619
struct sdca_entity;
@@ -1110,6 +1113,9 @@ struct sdca_function_data {
11101113
int num_entities;
11111114
int num_clusters;
11121115

1116+
struct sdca_interrupt interrupt[SDCA_MAX_INTERRUPTS];
1117+
int interrupt_num;
1118+
11131119
unsigned int busy_max_delay;
11141120
};
11151121

include/sound/sdca_interrupts.h

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
2+
/*
3+
* The MIPI SDCA specification is available for public downloads at
4+
* https://www.mipi.org/mipi-sdca-v1-0-download
5+
*
6+
* Copyright(c) 2024 Intel Corporation
7+
*/
8+
9+
#ifndef __SDCA_INTERRUPTS_H__
10+
#define __SDCA_INTERRUPTS_H__
11+
12+
#include <linux/types.h>
13+
#include <linux/kconfig.h>
14+
#include <linux/regmap.h>
15+
#include <linux/interrupt.h>
16+
17+
struct sdca_function_data;
18+
19+
#define SDCA_MAX_INTERRUPTS 31 /* the last bit is reserved for future extensions */
20+
21+
/**
22+
* struct sdca_interrupt_info - structure containing SDCA interrupt related information
23+
* @parent: Pointer to the parent device.
24+
* @irq_chip: regmap irq chip structure.
25+
* @irq_data: regmap irq chip data structure.
26+
* @irq_dom: Pointer to the irq domain.
27+
* @irqs_lock: Mutex to protect SDCA interrupts.
28+
* @sdca_interrupt_register_list: Bitmask of SDCA interrupt registers.
29+
*/
30+
struct sdca_interrupt_info {
31+
void *parent;
32+
struct device *dev;
33+
struct regmap *regmap;
34+
35+
struct regmap_irq_chip irq_chip;
36+
struct regmap_irq_chip_data *irq_data;
37+
struct irq_domain *irq_dom;
38+
39+
struct mutex irqs_lock; /* protects SDCA interrupts */
40+
};
41+
42+
/**
43+
* struct sdca_interrupt - struct continaing information about an SDCA interrupt
44+
* @regmap: Pointer to the regmap for the device.
45+
* @num: The interrupt number.
46+
* @control: Pointer to the control that the interrupt is associated with.
47+
* @control_reg: The register that the interrupt is associated with.
48+
* @callback: The callback function to be called when the interrupt is triggered.
49+
* @name: The name of the interrupt.
50+
*/
51+
struct sdca_interrupt {
52+
struct regmap *regmap;
53+
54+
int num;
55+
struct sdca_control *control;
56+
unsigned int control_reg;
57+
58+
irqreturn_t (*callback)(int irq, void *context);
59+
char *name;
60+
};
61+
62+
/**
63+
* sdca_request_irq - request an SDCA interrupt
64+
* @interrupt_info: Pointer to the interrupt information structure.
65+
* @irq: Pointer to the interrupt structure.
66+
*/
67+
int sdca_request_irq(struct sdca_interrupt_info *interrupt_info, struct sdca_interrupt *irq);
68+
69+
/**
70+
* sdca_irq_config - configure SDCA interrupts
71+
* @interrupt_info: Pointer to the interrupt information structure.
72+
* @irq: The interrupt number.
73+
* @functions: Pointer to the function data structure.
74+
* @num_functions: The number of functions.
75+
*/
76+
int sdca_irq_config(struct sdca_interrupt_info *interrupt_info, unsigned int irq,
77+
struct sdca_function_data *functions, int num_functions);
78+
79+
#endif

sound/soc/sdca/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@ config SND_SOC_SDCA
99

1010
config SND_SOC_SDCA_OPTIONAL
1111
def_tristate SND_SOC_SDCA || !SND_SOC_SDCA
12+
13+
config SND_SOC_SDCA_IRQ_HANDLER
14+
tristate "SDCA Interrupt support"
15+
depends on SOUNDWIRE
16+
help
17+
This option enables support for the SDCA IRQ handler.

sound/soc/sdca/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o
44

55
obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o
6+
7+
snd-soc-sdca-irq-handler-objs := sdca_interrupts.o
8+
9+
obj-$(CONFIG_SND_SOC_SDCA_IRQ_HANDLER) += snd-soc-sdca-irq-handler.o

sound/soc/sdca/sdca_interrupts.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* SDCA IRQ handler library
4+
*
5+
* Copyright (C) 2022-2023 Cirrus Logic, Inc. and
6+
* Cirrus Logic International Semiconductor Ltd.
7+
*
8+
* The MIPI SDCA specification is available for public downloads at
9+
* https://www.mipi.org/mipi-sdca-v1-0-download
10+
*/
11+
#include <linux/device.h>
12+
#include <linux/soundwire/sdw.h>
13+
#include <linux/soundwire/sdw_registers.h>
14+
#include <sound/sdca.h>
15+
#include <linux/regmap.h>
16+
#include <linux/interrupt.h>
17+
#include <linux/workqueue.h>
18+
#include <sound/sdca_interrupts.h>
19+
#include <sound/sdca_function.h>
20+
21+
static irqreturn_t sdca_base_irq_handler(int irq, void *data)
22+
{
23+
struct sdca_interrupt *priv = data;
24+
25+
pr_err("%s IRQ handled\n", priv->name);
26+
return IRQ_HANDLED;
27+
}
28+
29+
#define IRQ_SDCA_OFFSET(reg_number) ((SDW_SCP_SDCA_INT##reg_number) - SDW_SCP_SDCA_INT1)
30+
#define IRQ_SDCA(number, reg_number) REGMAP_IRQ_REG(SDW_SCP_SDCA_INT_SDCA_##number, \
31+
IRQ_SDCA_OFFSET(reg_number), \
32+
SDW_SCP_SDCA_INTMASK_SDCA_##number)
33+
34+
static const struct regmap_irq regmap_irqs[] = {
35+
IRQ_SDCA(0, 1),
36+
IRQ_SDCA(1, 1),
37+
IRQ_SDCA(2, 1),
38+
IRQ_SDCA(3, 1),
39+
IRQ_SDCA(4, 1),
40+
IRQ_SDCA(5, 1),
41+
IRQ_SDCA(6, 1),
42+
IRQ_SDCA(7, 1),
43+
IRQ_SDCA(8, 2),
44+
IRQ_SDCA(9, 2),
45+
IRQ_SDCA(10, 2),
46+
IRQ_SDCA(11, 2),
47+
IRQ_SDCA(12, 2),
48+
IRQ_SDCA(13, 2),
49+
IRQ_SDCA(14, 2),
50+
IRQ_SDCA(15, 2),
51+
IRQ_SDCA(16, 3),
52+
IRQ_SDCA(17, 3),
53+
IRQ_SDCA(18, 3),
54+
IRQ_SDCA(19, 3),
55+
IRQ_SDCA(20, 3),
56+
IRQ_SDCA(21, 3),
57+
IRQ_SDCA(22, 3),
58+
IRQ_SDCA(23, 3),
59+
IRQ_SDCA(24, 4),
60+
IRQ_SDCA(25, 4),
61+
IRQ_SDCA(26, 4),
62+
IRQ_SDCA(27, 4),
63+
IRQ_SDCA(28, 4),
64+
IRQ_SDCA(29, 4),
65+
};
66+
67+
static const struct regmap_irq_chip sdca_irq_chip = {
68+
.name = "sdca_irq",
69+
70+
.status_base = SDW_SCP_SDCA_INT1,
71+
.mask_base = SDW_SCP_SDCA_INTMASK1,
72+
.num_regs = 4,
73+
74+
.irqs = regmap_irqs,
75+
.num_irqs = SDCA_MAX_INTERRUPTS,
76+
77+
.runtime_pm = true,
78+
};
79+
80+
int sdca_request_irq(struct sdca_interrupt_info *interrupt_info, struct sdca_interrupt *irq)
81+
{
82+
char *name = irq->name;
83+
irq_handler_t thread_fn = irq->callback;
84+
int ret;
85+
86+
87+
ret = irq_create_mapping(interrupt_info->irq_dom, irq->num);
88+
89+
if (ret < 0) {
90+
dev_err(interrupt_info->dev, "Failed to map IRQ %s\n", name);
91+
return 1;
92+
}
93+
94+
dev_dbg(interrupt_info->dev, "Request IRQ %d for %s\n", ret, name);
95+
96+
ret = devm_request_threaded_irq(interrupt_info->dev, ret, NULL, thread_fn,
97+
IRQF_ONESHOT, name, irq);
98+
if (ret) {
99+
dev_err(interrupt_info->dev, "Failed to request IRQ %s\n", name);
100+
return 1;
101+
}
102+
103+
return 0;
104+
}
105+
EXPORT_SYMBOL_GPL(sdca_request_irq);
106+
107+
static int sdca_process_controls_with_interrupts(struct sdca_interrupt_info *interrupt_info,
108+
struct sdca_function_data *functions, int num_functions)
109+
{
110+
int i, j, k;
111+
struct sdca_function_data *function;
112+
struct sdca_control *control;
113+
struct sdca_interrupt *interrupt;
114+
int total_controls = 0;
115+
int local_controls = 0;
116+
117+
for (i = 0; i < num_functions; i++) {
118+
function = &functions[i];
119+
for (j = 0; j < function->num_entities; j++) {
120+
for (k = 0; k < function->entities[j].num_controls; k++) {
121+
control = &function->entities[j].controls[k];
122+
if (control->interrupt_position > 0) {
123+
if (total_controls >= SDCA_MAX_INTERRUPTS)
124+
return -1;
125+
126+
interrupt = &function->interrupt[local_controls++];
127+
interrupt->num = control->interrupt_position;
128+
interrupt->name = devm_kasprintf(interrupt_info->dev, GFP_KERNEL,
129+
"%s: %s", function->desc->name, control->label);
130+
interrupt->callback = &sdca_base_irq_handler;
131+
interrupt->regmap = interrupt_info->regmap;
132+
interrupt->control = control;
133+
interrupt->control_reg = SDW_SDCA_CTL(function->desc->adr,
134+
function->entities[j].id,
135+
control->sel, 0);
136+
137+
total_controls++;
138+
}
139+
}
140+
}
141+
function->interrupt_num = local_controls;
142+
local_controls = 0;
143+
}
144+
145+
return total_controls;
146+
}
147+
148+
int sdca_irq_config(struct sdca_interrupt_info *interrupt_info, unsigned int irq,
149+
struct sdca_function_data *functions, int num_functions)
150+
{
151+
struct irq_data *irq_data;
152+
unsigned long irq_flags;
153+
struct irq_domain *dom;
154+
int ret;
155+
int control_count;
156+
157+
control_count = sdca_process_controls_with_interrupts(interrupt_info, functions, num_functions);
158+
if (control_count < 0) {
159+
dev_info(interrupt_info->dev, "No interrupts to act on\n");
160+
return 0;
161+
}
162+
163+
interrupt_info->irq_chip = sdca_irq_chip;
164+
interrupt_info->irq_chip.irq_drv_data = interrupt_info->parent;
165+
166+
irq_data = irq_get_irq_data(irq);
167+
if (!irq_data) {
168+
dev_err(interrupt_info->dev, "Invalid IRQ: %d\n", irq);
169+
return -EINVAL;
170+
}
171+
172+
irq_flags = irqd_get_trigger_type(irq_data);
173+
switch (irq_flags) {
174+
case IRQF_TRIGGER_LOW:
175+
case IRQF_TRIGGER_HIGH:
176+
case IRQF_TRIGGER_RISING:
177+
case IRQF_TRIGGER_FALLING:
178+
break;
179+
case IRQ_TYPE_NONE:
180+
default:
181+
irq_flags = IRQF_TRIGGER_LOW;
182+
break;
183+
}
184+
185+
irq_flags |= IRQF_ONESHOT;
186+
187+
ret = devm_regmap_add_irq_chip(interrupt_info->dev, interrupt_info->regmap,
188+
irq, irq_flags, 0,
189+
&interrupt_info->irq_chip, &interrupt_info->irq_data);
190+
if (ret) {
191+
dev_err(interrupt_info->dev, "Failed to add IRQ chip: %d\n", ret);
192+
return ret;
193+
}
194+
dev_dbg(interrupt_info->dev, "Configured IRQ %d with flags 0x%lx\n", irq, irq_flags);
195+
196+
interrupt_info->irq_dom = regmap_irq_get_domain(interrupt_info->irq_data);
197+
if (!dom)
198+
return -EINVAL;
199+
200+
return 0;
201+
}
202+
EXPORT_SYMBOL_NS(sdca_irq_config, "SND_SOC_SDCA_IRQ_HANDLER");
203+
204+
MODULE_LICENSE("GPL");
205+
MODULE_DESCRIPTION("SDCA IRQ handler library");

0 commit comments

Comments
 (0)