Skip to content

Commit aa4a38a

Browse files
Stefan Bindingtiwai
authored andcommitted
ALSA: hda: cs35l41: Add Support for Interrupts
The CS35L41 can produce interrupts on error. When the interrupts occur, the driver will report the error, but errors will only be fixed after playback finishes. Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com> Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com> Link: https://lore.kernel.org/r/20220509214703.4482-5-vitalyr@opensource.cirrus.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent 14e42ce commit aa4a38a

4 files changed

Lines changed: 191 additions & 1 deletion

File tree

include/sound/cs35l41.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,13 @@
691691
#define CS35L41_TEMP_WARN_ERR_RLS 0x20
692692
#define CS35L41_TEMP_ERR_RLS 0x40
693693

694+
#define CS35L41_AMP_SHORT_ERR_RLS_SHIFT 1
695+
#define CS35L41_BST_SHORT_ERR_RLS_SHIFT 2
696+
#define CS35L41_BST_OVP_ERR_RLS_SHIFT 3
697+
#define CS35L41_BST_UVP_ERR_RLS_SHIFT 4
698+
#define CS35L41_TEMP_WARN_ERR_RLS_SHIFT 5
699+
#define CS35L41_TEMP_ERR_RLS_SHIFT 6
700+
694701
#define CS35L41_INT1_MASK_DEFAULT 0x7FFCFE3F
695702
#define CS35L41_INT1_UNMASK_PUP 0xFEFFFFFF
696703
#define CS35L41_INT1_UNMASK_PDN 0xFF7FFFFF
@@ -794,6 +801,53 @@ struct cs35l41_otp_map_element_t {
794801
u32 word_offset;
795802
};
796803

804+
/*
805+
* IRQs
806+
*/
807+
#define CS35L41_IRQ(_irq, _name, _hand) \
808+
{ \
809+
.irq = CS35L41_ ## _irq ## _IRQ,\
810+
.name = _name, \
811+
.handler = _hand, \
812+
}
813+
814+
struct cs35l41_irq {
815+
int irq;
816+
const char *name;
817+
irqreturn_t (*handler)(int irq, void *data);
818+
};
819+
820+
#define CS35L41_REG_IRQ(_reg, _irq) \
821+
[CS35L41_ ## _irq ## _IRQ] = { \
822+
.reg_offset = (CS35L41_ ## _reg) - CS35L41_IRQ1_STATUS1,\
823+
.mask = CS35L41_ ## _irq ## _MASK \
824+
}
825+
826+
/* (0x0000E010) CS35L41_IRQ1_STATUS1 */
827+
#define CS35L41_BST_OVP_ERR_SHIFT 6
828+
#define CS35L41_BST_OVP_ERR_MASK BIT(CS35L41_BST_OVP_ERR_SHIFT)
829+
#define CS35L41_BST_DCM_UVP_ERR_SHIFT 7
830+
#define CS35L41_BST_DCM_UVP_ERR_MASK BIT(CS35L41_BST_DCM_UVP_ERR_SHIFT)
831+
#define CS35L41_BST_SHORT_ERR_SHIFT 8
832+
#define CS35L41_BST_SHORT_ERR_MASK BIT(CS35L41_BST_SHORT_ERR_SHIFT)
833+
#define CS35L41_TEMP_WARN_SHIFT 15
834+
#define CS35L41_TEMP_WARN_MASK BIT(CS35L41_TEMP_WARN_SHIFT)
835+
#define CS35L41_TEMP_ERR_SHIFT 17
836+
#define CS35L41_TEMP_ERR_MASK BIT(CS35L41_TEMP_ERR_SHIFT)
837+
#define CS35L41_AMP_SHORT_ERR_SHIFT 31
838+
#define CS35L41_AMP_SHORT_ERR_MASK BIT(CS35L41_AMP_SHORT_ERR_SHIFT)
839+
840+
enum cs35l41_irq_list {
841+
CS35L41_BST_OVP_ERR_IRQ,
842+
CS35L41_BST_DCM_UVP_ERR_IRQ,
843+
CS35L41_BST_SHORT_ERR_IRQ,
844+
CS35L41_TEMP_WARN_IRQ,
845+
CS35L41_TEMP_ERR_IRQ,
846+
CS35L41_AMP_SHORT_ERR_IRQ,
847+
848+
CS35L41_NUM_IRQ
849+
};
850+
797851
extern struct regmap_config cs35l41_regmap_i2c;
798852
extern struct regmap_config cs35l41_regmap_spi;
799853

sound/pci/hda/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ config SND_HDA_SCODEC_CS35L41_I2C
102102
select SND_HDA_GENERIC
103103
select SND_SOC_CS35L41_LIB
104104
select SND_HDA_SCODEC_CS35L41
105+
select REGMAP_IRQ
105106
help
106107
Say Y or M here to include CS35L41 I2C HD-audio side codec support
107108
in snd-hda-intel driver, such as ALC287.
@@ -117,6 +118,7 @@ config SND_HDA_SCODEC_CS35L41_SPI
117118
select SND_HDA_GENERIC
118119
select SND_SOC_CS35L41_LIB
119120
select SND_HDA_SCODEC_CS35L41
121+
select REGMAP_IRQ
120122
help
121123
Say Y or M here to include CS35L41 SPI HD-audio side codec support
122124
in snd-hda-intel driver, such as ALC287.

sound/pci/hda/cs35l41_hda.c

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,21 @@ static const struct reg_sequence cs35l41_hda_mute[] = {
3232
{ CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_VOL_PCM Mute
3333
};
3434

35+
/* Protection release cycle to get the speaker out of Safe-Mode */
36+
static void cs35l41_error_release(struct device *dev, struct regmap *regmap, unsigned int mask)
37+
{
38+
regmap_write(regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
39+
regmap_set_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
40+
regmap_clear_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
41+
}
42+
43+
/* Clear all errors to release safe mode. Global Enable must be cleared first. */
44+
static void cs35l41_irq_release(struct cs35l41_hda *cs35l41)
45+
{
46+
cs35l41_error_release(cs35l41->dev, cs35l41->regmap, cs35l41->irq_errors);
47+
cs35l41->irq_errors = 0;
48+
}
49+
3550
static void cs35l41_hda_playback_hook(struct device *dev, int action)
3651
{
3752
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
@@ -58,6 +73,7 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action)
5873
CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
5974
if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
6075
regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001);
76+
cs35l41_irq_release(cs35l41);
6177
break;
6278
default:
6379
dev_warn(cs35l41->dev, "Playback action not supported: %d\n", action);
@@ -110,10 +126,101 @@ static const struct component_ops cs35l41_hda_comp_ops = {
110126
.unbind = cs35l41_hda_unbind,
111127
};
112128

129+
static irqreturn_t cs35l41_bst_short_err(int irq, void *data)
130+
{
131+
struct cs35l41_hda *cs35l41 = data;
132+
133+
dev_crit_ratelimited(cs35l41->dev, "LBST Error\n");
134+
set_bit(CS35L41_BST_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
135+
136+
return IRQ_HANDLED;
137+
}
138+
139+
static irqreturn_t cs35l41_bst_dcm_uvp_err(int irq, void *data)
140+
{
141+
struct cs35l41_hda *cs35l41 = data;
142+
143+
dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
144+
set_bit(CS35L41_BST_UVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
145+
146+
return IRQ_HANDLED;
147+
}
148+
149+
static irqreturn_t cs35l41_bst_ovp_err(int irq, void *data)
150+
{
151+
struct cs35l41_hda *cs35l41 = data;
152+
153+
dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
154+
set_bit(CS35L41_BST_OVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
155+
156+
return IRQ_HANDLED;
157+
}
158+
159+
static irqreturn_t cs35l41_temp_err(int irq, void *data)
160+
{
161+
struct cs35l41_hda *cs35l41 = data;
162+
163+
dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
164+
set_bit(CS35L41_TEMP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
165+
166+
return IRQ_HANDLED;
167+
}
168+
169+
static irqreturn_t cs35l41_temp_warn(int irq, void *data)
170+
{
171+
struct cs35l41_hda *cs35l41 = data;
172+
173+
dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
174+
set_bit(CS35L41_TEMP_WARN_ERR_RLS_SHIFT, &cs35l41->irq_errors);
175+
176+
return IRQ_HANDLED;
177+
}
178+
179+
static irqreturn_t cs35l41_amp_short(int irq, void *data)
180+
{
181+
struct cs35l41_hda *cs35l41 = data;
182+
183+
dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
184+
set_bit(CS35L41_AMP_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
185+
186+
return IRQ_HANDLED;
187+
}
188+
189+
static const struct cs35l41_irq cs35l41_irqs[] = {
190+
CS35L41_IRQ(BST_OVP_ERR, "Boost Overvoltage Error", cs35l41_bst_ovp_err),
191+
CS35L41_IRQ(BST_DCM_UVP_ERR, "Boost Undervoltage Error", cs35l41_bst_dcm_uvp_err),
192+
CS35L41_IRQ(BST_SHORT_ERR, "Boost Inductor Short Error", cs35l41_bst_short_err),
193+
CS35L41_IRQ(TEMP_WARN, "Temperature Warning", cs35l41_temp_warn),
194+
CS35L41_IRQ(TEMP_ERR, "Temperature Error", cs35l41_temp_err),
195+
CS35L41_IRQ(AMP_SHORT_ERR, "Amp Short", cs35l41_amp_short),
196+
};
197+
198+
static const struct regmap_irq cs35l41_reg_irqs[] = {
199+
CS35L41_REG_IRQ(IRQ1_STATUS1, BST_OVP_ERR),
200+
CS35L41_REG_IRQ(IRQ1_STATUS1, BST_DCM_UVP_ERR),
201+
CS35L41_REG_IRQ(IRQ1_STATUS1, BST_SHORT_ERR),
202+
CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_WARN),
203+
CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_ERR),
204+
CS35L41_REG_IRQ(IRQ1_STATUS1, AMP_SHORT_ERR),
205+
};
206+
207+
static const struct regmap_irq_chip cs35l41_regmap_irq_chip = {
208+
.name = "cs35l41 IRQ1 Controller",
209+
.status_base = CS35L41_IRQ1_STATUS1,
210+
.mask_base = CS35L41_IRQ1_MASK1,
211+
.ack_base = CS35L41_IRQ1_STATUS1,
212+
.num_regs = 4,
213+
.irqs = cs35l41_reg_irqs,
214+
.num_irqs = ARRAY_SIZE(cs35l41_reg_irqs),
215+
};
216+
113217
static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41)
114218
{
115219
struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
220+
bool using_irq = false;
221+
int irq, irq_pol;
116222
int ret;
223+
int i;
117224

118225
if (!cs35l41->hw_cfg.valid)
119226
return -EINVAL;
@@ -145,14 +252,36 @@ static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41)
145252
case CS35L41_NOT_USED:
146253
break;
147254
case CS35L41_INTERRUPT:
255+
using_irq = true;
148256
break;
149257
default:
150258
dev_err(cs35l41->dev, "Invalid GPIO2 function %d\n", hw_cfg->gpio2.func);
151259
return -EINVAL;
152260
}
153261
}
154262

155-
cs35l41_gpio_config(cs35l41->regmap, hw_cfg);
263+
irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg);
264+
265+
if (cs35l41->irq && using_irq) {
266+
ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq,
267+
IRQF_ONESHOT | IRQF_SHARED | irq_pol,
268+
0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data);
269+
if (ret)
270+
return ret;
271+
272+
for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) {
273+
irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq);
274+
if (irq < 0)
275+
return irq;
276+
277+
ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL,
278+
cs35l41_irqs[i].handler,
279+
IRQF_ONESHOT | IRQF_SHARED | irq_pol,
280+
cs35l41_irqs[i].name, cs35l41);
281+
if (ret)
282+
return ret;
283+
}
284+
}
156285

157286
return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos);
158287
}
@@ -296,6 +425,9 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
296425
struct cs35l41_hda *cs35l41;
297426
int ret;
298427

428+
BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != ARRAY_SIZE(cs35l41_reg_irqs));
429+
BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != CS35L41_NUM_IRQ);
430+
299431
if (IS_ERR(regmap))
300432
return PTR_ERR(regmap);
301433

sound/pci/hda/cs35l41_hda.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ struct cs35l41_hda {
3535

3636
int irq;
3737
int index;
38+
unsigned volatile long irq_errors;
39+
struct regmap_irq_chip_data *irq_data;
3840
};
3941

4042
int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,

0 commit comments

Comments
 (0)