Skip to content

Commit 2e411e9

Browse files
committed
ASoC: cs35l56: Fixes to handling of ASP1 config
Merge series from Richard Fitzgerald <[email protected]>: This chain fixes some problems with some previous patches for handling the ASP1 config registers. The root of the problem is that the ownership of these registers can be either with the firmware or the driver, and that the chip has to be soft-reset after downloading the firmware. This chain adds and uses a regmap_read_bypassed() function so that the driver can leave the regmap in cache-only until the chip has rebooted, but still poll a register to detect when the chip has rebooted. Richard Fitzgerald (4): regmap: Add regmap_read_bypassed() ALSA: hda: cs35l56: Exit cache-only after cs35l56_wait_for_firmware_boot() ASoC: cs35l56: Fix unintended bus access while resetting amp ASoC: cs35l56: Prevent overwriting firmware ASP config drivers/base/regmap/regmap.c | 37 ++++++++++++++ include/linux/regmap.h | 8 +++ include/sound/cs35l56.h | 2 + sound/pci/hda/cs35l56_hda.c | 4 ++ sound/soc/codecs/cs35l56-sdw.c | 2 - sound/soc/codecs/cs35l56-shared.c | 83 ++++++++++++++++++++----------- sound/soc/codecs/cs35l56.c | 26 +++++++++- 7 files changed, 130 insertions(+), 32 deletions(-) -- 2.39.2
2 parents f23e8f3 + dfd2ffb commit 2e411e9

File tree

7 files changed

+130
-32
lines changed

7 files changed

+130
-32
lines changed

drivers/base/regmap/regmap.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2838,6 +2838,43 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
28382838
}
28392839
EXPORT_SYMBOL_GPL(regmap_read);
28402840

2841+
/**
2842+
* regmap_read_bypassed() - Read a value from a single register direct
2843+
* from the device, bypassing the cache
2844+
*
2845+
* @map: Register map to read from
2846+
* @reg: Register to be read from
2847+
* @val: Pointer to store read value
2848+
*
2849+
* A value of zero will be returned on success, a negative errno will
2850+
* be returned in error cases.
2851+
*/
2852+
int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val)
2853+
{
2854+
int ret;
2855+
bool bypass, cache_only;
2856+
2857+
if (!IS_ALIGNED(reg, map->reg_stride))
2858+
return -EINVAL;
2859+
2860+
map->lock(map->lock_arg);
2861+
2862+
bypass = map->cache_bypass;
2863+
cache_only = map->cache_only;
2864+
map->cache_bypass = true;
2865+
map->cache_only = false;
2866+
2867+
ret = _regmap_read(map, reg, val);
2868+
2869+
map->cache_bypass = bypass;
2870+
map->cache_only = cache_only;
2871+
2872+
map->unlock(map->lock_arg);
2873+
2874+
return ret;
2875+
}
2876+
EXPORT_SYMBOL_GPL(regmap_read_bypassed);
2877+
28412878
/**
28422879
* regmap_raw_read() - Read raw data from the device
28432880
*

include/linux/regmap.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,7 @@ int regmap_multi_reg_write_bypassed(struct regmap *map,
12301230
int regmap_raw_write_async(struct regmap *map, unsigned int reg,
12311231
const void *val, size_t val_len);
12321232
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
1233+
int regmap_read_bypassed(struct regmap *map, unsigned int reg, unsigned int *val);
12331234
int regmap_raw_read(struct regmap *map, unsigned int reg,
12341235
void *val, size_t val_len);
12351236
int regmap_noinc_read(struct regmap *map, unsigned int reg,
@@ -1739,6 +1740,13 @@ static inline int regmap_read(struct regmap *map, unsigned int reg,
17391740
return -EINVAL;
17401741
}
17411742

1743+
static inline int regmap_read_bypassed(struct regmap *map, unsigned int reg,
1744+
unsigned int *val)
1745+
{
1746+
WARN_ONCE(1, "regmap API is disabled");
1747+
return -EINVAL;
1748+
}
1749+
17421750
static inline int regmap_raw_read(struct regmap *map, unsigned int reg,
17431751
void *val, size_t val_len)
17441752
{

include/sound/cs35l56.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ struct cs35l56_base {
267267
bool fw_patched;
268268
bool secured;
269269
bool can_hibernate;
270+
bool fw_owns_asp1;
270271
bool cal_data_valid;
271272
s8 cal_index;
272273
struct cirrus_amp_cal_data cal_data;
@@ -283,6 +284,7 @@ extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
283284
extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];
284285

285286
int cs35l56_set_patch(struct cs35l56_base *cs35l56_base);
287+
int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base);
286288
int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base);
287289
int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command);
288290
int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base);

sound/pci/hda/cs35l56_hda.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,8 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
644644
ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
645645
if (ret)
646646
goto err_powered_up;
647+
648+
regcache_cache_only(cs35l56->base.regmap, false);
647649
}
648650

649651
/* Disable auto-hibernate so that runtime_pm has control */
@@ -1002,6 +1004,8 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id)
10021004
if (ret)
10031005
goto err;
10041006

1007+
regcache_cache_only(cs35l56->base.regmap, false);
1008+
10051009
ret = cs35l56_set_patch(&cs35l56->base);
10061010
if (ret)
10071011
goto err;

sound/soc/codecs/cs35l56-sdw.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,6 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral)
188188
goto out;
189189
}
190190

191-
regcache_cache_only(cs35l56->base.regmap, false);
192-
193191
ret = cs35l56_init(cs35l56);
194192
if (ret < 0) {
195193
regcache_cache_only(cs35l56->base.regmap, true);

sound/soc/codecs/cs35l56-shared.c

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,11 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED);
4040
static const struct reg_default cs35l56_reg_defaults[] = {
4141
/* no defaults for OTP_MEM - first read populates cache */
4242

43-
{ CS35L56_ASP1_ENABLES1, 0x00000000 },
44-
{ CS35L56_ASP1_CONTROL1, 0x00000028 },
45-
{ CS35L56_ASP1_CONTROL2, 0x18180200 },
46-
{ CS35L56_ASP1_CONTROL3, 0x00000002 },
47-
{ CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
48-
{ CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
49-
{ CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
50-
{ CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
51-
52-
/* no defaults for ASP1TX mixer */
43+
/*
44+
* No defaults for ASP1 control or ASP1TX mixer. See
45+
* cs35l56_populate_asp1_register_defaults() and
46+
* cs35l56_sync_asp1_mixer_widgets_with_firmware().
47+
*/
5348

5449
{ CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
5550
{ CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
@@ -210,26 +205,52 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
210205
}
211206
}
212207

208+
static const struct reg_sequence cs35l56_asp1_defaults[] = {
209+
REG_SEQ0(CS35L56_ASP1_ENABLES1, 0x00000000),
210+
REG_SEQ0(CS35L56_ASP1_CONTROL1, 0x00000028),
211+
REG_SEQ0(CS35L56_ASP1_CONTROL2, 0x18180200),
212+
REG_SEQ0(CS35L56_ASP1_CONTROL3, 0x00000002),
213+
REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL1, 0x03020100),
214+
REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL5, 0x00020100),
215+
REG_SEQ0(CS35L56_ASP1_DATA_CONTROL1, 0x00000018),
216+
REG_SEQ0(CS35L56_ASP1_DATA_CONTROL5, 0x00000018),
217+
};
218+
219+
/*
220+
* The firmware can have control of the ASP so we don't provide regmap
221+
* with defaults for these registers, to prevent a regcache_sync() from
222+
* overwriting the firmware settings. But if the machine driver hooks up
223+
* the ASP it means the driver is taking control of the ASP, so then the
224+
* registers are populated with the defaults.
225+
*/
226+
int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base)
227+
{
228+
if (!cs35l56_base->fw_owns_asp1)
229+
return 0;
230+
231+
cs35l56_base->fw_owns_asp1 = false;
232+
233+
return regmap_multi_reg_write(cs35l56_base->regmap, cs35l56_asp1_defaults,
234+
ARRAY_SIZE(cs35l56_asp1_defaults));
235+
}
236+
EXPORT_SYMBOL_NS_GPL(cs35l56_init_asp1_regs_for_driver_control, SND_SOC_CS35L56_SHARED);
237+
213238
/*
214239
* The firmware boot sequence can overwrite the ASP1 config registers so that
215240
* they don't match regmap's view of their values. Rewrite the values from the
216241
* regmap cache into the hardware registers.
217242
*/
218243
int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base)
219244
{
220-
struct reg_sequence asp1_regs[] = {
221-
{ .reg = CS35L56_ASP1_ENABLES1 },
222-
{ .reg = CS35L56_ASP1_CONTROL1 },
223-
{ .reg = CS35L56_ASP1_CONTROL2 },
224-
{ .reg = CS35L56_ASP1_CONTROL3 },
225-
{ .reg = CS35L56_ASP1_FRAME_CONTROL1 },
226-
{ .reg = CS35L56_ASP1_FRAME_CONTROL5 },
227-
{ .reg = CS35L56_ASP1_DATA_CONTROL1 },
228-
{ .reg = CS35L56_ASP1_DATA_CONTROL5 },
229-
};
245+
struct reg_sequence asp1_regs[ARRAY_SIZE(cs35l56_asp1_defaults)];
230246
int i, ret;
231247

232-
/* Read values from regmap cache into a write sequence */
248+
if (cs35l56_base->fw_owns_asp1)
249+
return 0;
250+
251+
memcpy(asp1_regs, cs35l56_asp1_defaults, sizeof(asp1_regs));
252+
253+
/* Read current values from regmap cache into the write sequence */
233254
for (i = 0; i < ARRAY_SIZE(asp1_regs); ++i) {
234255
ret = regmap_read(cs35l56_base->regmap, asp1_regs[i].reg, &asp1_regs[i].def);
235256
if (ret)
@@ -307,10 +328,10 @@ int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base)
307328
reg = CS35L56_DSP1_HALO_STATE;
308329

309330
/*
310-
* This can't be a regmap_read_poll_timeout() because cs35l56 will NAK
311-
* I2C until it has booted which would terminate the poll
331+
* The regmap must remain in cache-only until the chip has
332+
* booted, so use a bypassed read of the status register.
312333
*/
313-
poll_ret = read_poll_timeout(regmap_read, read_ret,
334+
poll_ret = read_poll_timeout(regmap_read_bypassed, read_ret,
314335
(val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
315336
CS35L56_HALO_STATE_POLL_US,
316337
CS35L56_HALO_STATE_TIMEOUT_US,
@@ -362,7 +383,8 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
362383
return;
363384

364385
cs35l56_wait_control_port_ready();
365-
regcache_cache_only(cs35l56_base->regmap, false);
386+
387+
/* Leave in cache-only. This will be revoked when the chip has rebooted. */
366388
}
367389
EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, SND_SOC_CS35L56_SHARED);
368390

@@ -577,14 +599,14 @@ int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_sou
577599
cs35l56_issue_wake_event(cs35l56_base);
578600

579601
out_sync:
580-
regcache_cache_only(cs35l56_base->regmap, false);
581-
582602
ret = cs35l56_wait_for_firmware_boot(cs35l56_base);
583603
if (ret) {
584604
dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret);
585605
goto err;
586606
}
587607

608+
regcache_cache_only(cs35l56_base->regmap, false);
609+
588610
ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
589611
if (ret)
590612
goto err;
@@ -757,7 +779,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
757779
* devices so the REVID needs to be determined before waiting for the
758780
* firmware to boot.
759781
*/
760-
ret = regmap_read(cs35l56_base->regmap, CS35L56_REVID, &revid);
782+
ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_REVID, &revid);
761783
if (ret < 0) {
762784
dev_err(cs35l56_base->dev, "Get Revision ID failed\n");
763785
return ret;
@@ -768,7 +790,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
768790
if (ret)
769791
return ret;
770792

771-
ret = regmap_read(cs35l56_base->regmap, CS35L56_DEVID, &devid);
793+
ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_DEVID, &devid);
772794
if (ret < 0) {
773795
dev_err(cs35l56_base->dev, "Get Device ID failed\n");
774796
return ret;
@@ -787,6 +809,9 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
787809

788810
cs35l56_base->type = devid & 0xFF;
789811

812+
/* Silicon is now identified and booted so exit cache-only */
813+
regcache_cache_only(cs35l56_base->regmap, false);
814+
790815
ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured);
791816
if (ret) {
792817
dev_err(cs35l56_base->dev, "Get Secure status failed\n");

sound/soc/codecs/cs35l56.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,9 +454,14 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f
454454
{
455455
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component);
456456
unsigned int val;
457+
int ret;
457458

458459
dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt);
459460

461+
ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base);
462+
if (ret)
463+
return ret;
464+
460465
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
461466
case SND_SOC_DAIFMT_CBC_CFC:
462467
break;
@@ -530,6 +535,11 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx
530535
unsigned int rx_mask, int slots, int slot_width)
531536
{
532537
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
538+
int ret;
539+
540+
ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base);
541+
if (ret)
542+
return ret;
533543

534544
if ((slots == 0) || (slot_width == 0)) {
535545
dev_dbg(cs35l56->base.dev, "tdm config cleared\n");
@@ -578,6 +588,11 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream,
578588
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
579589
unsigned int rate = params_rate(params);
580590
u8 asp_width, asp_wl;
591+
int ret;
592+
593+
ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base);
594+
if (ret)
595+
return ret;
581596

582597
asp_wl = params_width(params);
583598
if (cs35l56->asp_slot_width)
@@ -634,7 +649,11 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai,
634649
int clk_id, unsigned int freq, int dir)
635650
{
636651
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
637-
int freq_id;
652+
int freq_id, ret;
653+
654+
ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base);
655+
if (ret)
656+
return ret;
638657

639658
if (freq == 0) {
640659
cs35l56->sysclk_set = false;
@@ -1403,6 +1422,9 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
14031422
cs35l56->base.cal_index = -1;
14041423
cs35l56->speaker_id = -ENOENT;
14051424

1425+
/* Assume that the firmware owns ASP1 until we know different */
1426+
cs35l56->base.fw_owns_asp1 = true;
1427+
14061428
dev_set_drvdata(cs35l56->base.dev, cs35l56);
14071429

14081430
cs35l56_fill_supply_names(cs35l56->supplies);
@@ -1531,6 +1553,8 @@ int cs35l56_init(struct cs35l56_private *cs35l56)
15311553
return ret;
15321554

15331555
dev_dbg(cs35l56->base.dev, "Firmware rebooted after soft reset\n");
1556+
1557+
regcache_cache_only(cs35l56->base.regmap, false);
15341558
}
15351559

15361560
/* Disable auto-hibernate so that runtime_pm has control */

0 commit comments

Comments
 (0)