Commit fe1544de authored by Takashi Iwai's avatar Takashi Iwai
Browse files

Merge tag 'asoc-fix-v6.14-rc4' of...

Merge tag 'asoc-fix-v6.14-rc4' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus

ASoC: Fixes for v6.14

More driver specific fixes, the firmware change is part of fixing the
race conditions in the Cirrus driver.
parents c6557ccf 9da0ed4a
Loading
Loading
Loading
Loading
+6 −18
Original line number Diff line number Diff line
@@ -1609,7 +1609,7 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
				goto out_fw;
			}

			ret = regmap_raw_write_async(regmap, reg, buf->buf,
			ret = regmap_raw_write(regmap, reg, buf->buf,
					       le32_to_cpu(region->len));
			if (ret != 0) {
				cs_dsp_err(dsp,
@@ -1625,12 +1625,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
		regions++;
	}

	ret = regmap_async_complete(regmap);
	if (ret != 0) {
		cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret);
		goto out_fw;
	}

	if (pos > firmware->size)
		cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
			    file, regions, pos - firmware->size);
@@ -1638,7 +1632,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
	cs_dsp_debugfs_save_wmfwname(dsp, file);

out_fw:
	regmap_async_complete(regmap);
	cs_dsp_buf_free(&buf_list);

	if (ret == -EOVERFLOW)
@@ -2326,7 +2319,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
			cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
				   file, blocks, le32_to_cpu(blk->len),
				   reg);
			ret = regmap_raw_write_async(regmap, reg, buf->buf,
			ret = regmap_raw_write(regmap, reg, buf->buf,
					       le32_to_cpu(blk->len));
			if (ret != 0) {
				cs_dsp_err(dsp,
@@ -2339,10 +2332,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
		blocks++;
	}

	ret = regmap_async_complete(regmap);
	if (ret != 0)
		cs_dsp_err(dsp, "Failed to complete async write: %d\n", ret);

	if (pos > firmware->size)
		cs_dsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
			    file, blocks, pos - firmware->size);
@@ -2350,7 +2339,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
	cs_dsp_debugfs_save_binname(dsp, file);

out_fw:
	regmap_async_complete(regmap);
	cs_dsp_buf_free(&buf_list);

	if (ret == -EOVERFLOW)
@@ -2561,7 +2549,7 @@ static int cs_dsp_adsp2_enable_core(struct cs_dsp *dsp)
{
	int ret;

	ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
				 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
	if (ret != 0)
		return ret;
+31 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <sound/cs-amp-lib.h>

#define CS35L56_DEVID					0x0000000
@@ -61,6 +62,7 @@
#define CS35L56_IRQ1_MASK_8				0x000E0AC
#define CS35L56_IRQ1_MASK_18				0x000E0D4
#define CS35L56_IRQ1_MASK_20				0x000E0DC
#define CS35L56_DSP_MBOX_1_RAW				0x0011000
#define CS35L56_DSP_VIRTUAL1_MBOX_1			0x0011020
#define CS35L56_DSP_VIRTUAL1_MBOX_2			0x0011024
#define CS35L56_DSP_VIRTUAL1_MBOX_3			0x0011028
@@ -224,6 +226,7 @@
#define CS35L56_HALO_STATE_SHUTDOWN			1
#define CS35L56_HALO_STATE_BOOT_DONE			2

#define CS35L56_MBOX_CMD_PING				0x0A000000
#define CS35L56_MBOX_CMD_AUDIO_PLAY			0x0B000001
#define CS35L56_MBOX_CMD_AUDIO_PAUSE			0x0B000002
#define CS35L56_MBOX_CMD_AUDIO_REINIT			0x0B000003
@@ -254,6 +257,16 @@
#define CS35L56_NUM_BULK_SUPPLIES			3
#define CS35L56_NUM_DSP_REGIONS				5

/* Additional margin for SYSTEM_RESET to control port ready on SPI */
#define CS35L56_SPI_RESET_TO_PORT_READY_US (CS35L56_CONTROL_PORT_READY_US + 2500)

struct cs35l56_spi_payload {
	__be32	addr;
	__be16	pad;
	__be32	value;
} __packed;
static_assert(sizeof(struct cs35l56_spi_payload) == 10);

struct cs35l56_base {
	struct device *dev;
	struct regmap *regmap;
@@ -269,6 +282,7 @@ struct cs35l56_base {
	s8 cal_index;
	struct cirrus_amp_cal_data cal_data;
	struct gpio_desc *reset_gpio;
	struct cs35l56_spi_payload *spi_payload_buf;
};

static inline bool cs35l56_is_otp_register(unsigned int reg)
@@ -276,6 +290,23 @@ static inline bool cs35l56_is_otp_register(unsigned int reg)
	return (reg >> 16) == 3;
}

static inline int cs35l56_init_config_for_spi(struct cs35l56_base *cs35l56,
					      struct spi_device *spi)
{
	cs35l56->spi_payload_buf = devm_kzalloc(&spi->dev,
						sizeof(*cs35l56->spi_payload_buf),
						GFP_KERNEL | GFP_DMA);
	if (!cs35l56->spi_payload_buf)
		return -ENOMEM;

	return 0;
}

static inline bool cs35l56_is_spi(struct cs35l56_base *cs35l56)
{
	return IS_ENABLED(CONFIG_SPI_MASTER) && !!cs35l56->spi_payload_buf;
}

extern const struct regmap_config cs35l56_regmap_i2c;
extern const struct regmap_config cs35l56_regmap_spi;
extern const struct regmap_config cs35l56_regmap_sdw;
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi)
		return -ENOMEM;

	cs35l56->base.dev = &spi->dev;
	ret = cs35l56_init_config_for_spi(&cs35l56->base, spi);
	if (ret)
		return ret;

#ifdef CS35L56_WAKE_HOLD_TIME_US
	cs35l56->base.can_hibernate = true;
+80 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <sound/cs-amp-lib.h>

@@ -303,6 +304,79 @@ void cs35l56_wait_min_reset_pulse(void)
}
EXPORT_SYMBOL_NS_GPL(cs35l56_wait_min_reset_pulse, "SND_SOC_CS35L56_SHARED");

static const struct {
	u32 addr;
	u32 value;
} cs35l56_spi_system_reset_stages[] = {
	{ .addr = CS35L56_DSP_VIRTUAL1_MBOX_1, .value = CS35L56_MBOX_CMD_SYSTEM_RESET },
	/* The next write is necessary to delimit the soft reset */
	{ .addr = CS35L56_DSP_MBOX_1_RAW, .value = CS35L56_MBOX_CMD_PING },
};

static void cs35l56_spi_issue_bus_locked_reset(struct cs35l56_base *cs35l56_base,
					       struct spi_device *spi)
{
	struct cs35l56_spi_payload *buf = cs35l56_base->spi_payload_buf;
	struct spi_transfer t = {
		.tx_buf		= buf,
		.len		= sizeof(*buf),
	};
	struct spi_message m;
	int i, ret;

	for (i = 0; i < ARRAY_SIZE(cs35l56_spi_system_reset_stages); i++) {
		buf->addr = cpu_to_be32(cs35l56_spi_system_reset_stages[i].addr);
		buf->value = cpu_to_be32(cs35l56_spi_system_reset_stages[i].value);
		spi_message_init_with_transfers(&m, &t, 1);
		ret = spi_sync_locked(spi, &m);
		if (ret)
			dev_warn(cs35l56_base->dev, "spi_sync failed: %d\n", ret);

		usleep_range(CS35L56_SPI_RESET_TO_PORT_READY_US,
			     2 * CS35L56_SPI_RESET_TO_PORT_READY_US);
	}
}

static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base)
{
	struct spi_device *spi = to_spi_device(cs35l56_base->dev);
	unsigned int val;
	int read_ret, ret;

	/*
	 * There must not be any other SPI bus activity while the amp is
	 * soft-resetting.
	 */
	ret = spi_bus_lock(spi->controller);
	if (ret) {
		dev_warn(cs35l56_base->dev, "spi_bus_lock failed: %d\n", ret);
		return;
	}

	cs35l56_spi_issue_bus_locked_reset(cs35l56_base, spi);
	spi_bus_unlock(spi->controller);

	/*
	 * Check firmware boot by testing for a response in MBOX_2.
	 * HALO_STATE cannot be trusted yet because the reset sequence
	 * can leave it with stale state. But MBOX is reset.
	 * The regmap must remain in cache-only until the chip has
	 * booted, so use a bypassed read.
	 */
	ret = read_poll_timeout(regmap_read_bypassed, read_ret,
				(val > 0) && (val < 0xffffffff),
				CS35L56_HALO_STATE_POLL_US,
				CS35L56_HALO_STATE_TIMEOUT_US,
				false,
				cs35l56_base->regmap,
				CS35L56_DSP_VIRTUAL1_MBOX_2,
				&val);
	if (ret) {
		dev_err(cs35l56_base->dev, "SPI reboot timed out(%d): MBOX2=%#x\n",
			read_ret, val);
	}
}

static const struct reg_sequence cs35l56_system_reset_seq[] = {
	REG_SEQ0(CS35L56_DSP1_HALO_STATE, 0),
	REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
@@ -315,6 +389,12 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
	 * accesses other than the controlled system reset sequence below.
	 */
	regcache_cache_only(cs35l56_base->regmap, true);

	if (cs35l56_is_spi(cs35l56_base)) {
		cs35l56_spi_system_reset(cs35l56_base);
		return;
	}

	regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
					cs35l56_system_reset_seq,
					ARRAY_SIZE(cs35l56_system_reset_seq));
+3 −0
Original line number Diff line number Diff line
@@ -33,6 +33,9 @@ static int cs35l56_spi_probe(struct spi_device *spi)

	cs35l56->base.dev = &spi->dev;
	cs35l56->base.can_hibernate = true;
	ret = cs35l56_init_config_for_spi(&cs35l56->base, spi);
	if (ret)
		return ret;

	ret = cs35l56_common_probe(cs35l56);
	if (ret != 0)
Loading