Skip to content

Commit

Permalink
ASoC: cs35l56: Move utility functions to shared file
Browse files Browse the repository at this point in the history
Move the cs35l56 utility functions into the shared file so they are
available for use in HDA.

Signed-off-by: Simon Trimmer <simont@opensource.cirrus.com>
Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Acked-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20230721132120.5523-5-rf@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
  • Loading branch information
Simon Trimmer authored and Mark Brown committed Jul 23, 2023
1 parent 0a2e492 commit 8a731fd
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 203 deletions.
7 changes: 7 additions & 0 deletions include/sound/cs35l56.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,13 @@ extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];

int cs35l56_set_patch(struct cs35l56_base *cs35l56_base);
int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command);
int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base);
void cs35l56_wait_min_reset_pulse(void);
void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire);
int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq);
irqreturn_t cs35l56_irq(int irq, void *data);
int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base);
int cs35l56_get_bclk_freq_id(unsigned int freq);
void cs35l56_fill_supply_names(struct regulator_bulk_data *data);

Expand Down
208 changes: 208 additions & 0 deletions sound/soc/codecs/cs35l56-shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,214 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
}
}

int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command)
{
unsigned int val;
int ret;

regmap_write(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command);
ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
val, (val == 0),
CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US);
if (ret) {
dev_warn(cs35l56_base->dev, "MBOX command %#x failed: %d\n", command, ret);
return ret;
}

return 0;
}
EXPORT_SYMBOL_NS_GPL(cs35l56_mbox_send, SND_SOC_CS35L56_SHARED);

int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base)
{
unsigned int reg;
unsigned int val;
int ret;

if (cs35l56_base->rev < CS35L56_REVID_B0)
reg = CS35L56_DSP1_HALO_STATE_A1;
else
reg = CS35L56_DSP1_HALO_STATE;

ret = regmap_read_poll_timeout(cs35l56_base->regmap, reg,
val,
(val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
CS35L56_HALO_STATE_POLL_US,
CS35L56_HALO_STATE_TIMEOUT_US);

if ((ret < 0) && (ret != -ETIMEDOUT)) {
dev_err(cs35l56_base->dev, "Failed to read HALO_STATE: %d\n", ret);
return ret;
}

if ((ret == -ETIMEDOUT) || (val != CS35L56_HALO_STATE_BOOT_DONE)) {
dev_err(cs35l56_base->dev, "Firmware boot fail: HALO_STATE=%#x\n", val);
return -EIO;
}

return 0;
}
EXPORT_SYMBOL_NS_GPL(cs35l56_wait_for_firmware_boot, SND_SOC_CS35L56_SHARED);

void cs35l56_wait_min_reset_pulse(void)
{
/* Satisfy minimum reset pulse width spec */
usleep_range(CS35L56_RESET_PULSE_MIN_US, 2 * CS35L56_RESET_PULSE_MIN_US);
}
EXPORT_SYMBOL_NS_GPL(cs35l56_wait_min_reset_pulse, SND_SOC_CS35L56_SHARED);

static const struct reg_sequence cs35l56_system_reset_seq[] = {
REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
};

void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
{
/*
* Must enter cache-only first so there can't be any more register
* accesses other than the controlled system reset sequence below.
*/
regcache_cache_only(cs35l56_base->regmap, true);
regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
cs35l56_system_reset_seq,
ARRAY_SIZE(cs35l56_system_reset_seq));

/* On SoundWire the registers won't be accessible until it re-enumerates. */
if (is_soundwire)
return;

usleep_range(CS35L56_CONTROL_PORT_READY_US, CS35L56_CONTROL_PORT_READY_US + 400);
regcache_cache_only(cs35l56_base->regmap, false);
}
EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, SND_SOC_CS35L56_SHARED);

int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq)
{
int ret;

if (!irq)
return 0;

ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq,
IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
"cs35l56", cs35l56_base);
if (!ret)
cs35l56_base->irq = irq;
else
dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret);

return ret;
}
EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, SND_SOC_CS35L56_SHARED);

irqreturn_t cs35l56_irq(int irq, void *data)
{
struct cs35l56_base *cs35l56_base = data;
unsigned int status1 = 0, status8 = 0, status20 = 0;
unsigned int mask1, mask8, mask20;
unsigned int val;
int rv;

irqreturn_t ret = IRQ_NONE;

if (!cs35l56_base->init_done)
return IRQ_NONE;

mutex_lock(&cs35l56_base->irq_lock);

rv = pm_runtime_resume_and_get(cs35l56_base->dev);
if (rv < 0) {
dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv);
goto err_unlock;
}

regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
if ((val & CS35L56_IRQ1_STS_MASK) == 0) {
dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n");
goto err;
}

/* Ack interrupts */
regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, &status1);
regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1, &mask1);
status1 &= ~mask1;
regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, status1);

regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, &status8);
regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8, &mask8);
status8 &= ~mask8;
regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, status8);

regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_20, &status20);
regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, &mask20);
status20 &= ~mask20;
/* We don't want EINT20 but they default to unmasked: force mask */
regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);

dev_dbg(cs35l56_base->dev, "%s: %#x %#x\n", __func__, status1, status8);

/* Check to see if unmasked bits are active */
if (!status1 && !status8 && !status20)
goto err;

if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK)
dev_crit(cs35l56_base->dev, "Amp short error\n");

if (status8 & CS35L56_TEMP_ERR_EINT1_MASK)
dev_crit(cs35l56_base->dev, "Overtemp error\n");

ret = IRQ_HANDLED;

err:
pm_runtime_put(cs35l56_base->dev);
err_unlock:
mutex_unlock(&cs35l56_base->irq_lock);

return ret;
}
EXPORT_SYMBOL_NS_GPL(cs35l56_irq, SND_SOC_CS35L56_SHARED);

int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base)
{
unsigned int val;
int ret;

/* Nothing to re-patch if we haven't done any patching yet. */
if (!cs35l56_base->fw_patched)
return false;

/*
* If we have control of RESET we will have asserted it so the firmware
* will need re-patching.
*/
if (cs35l56_base->reset_gpio)
return true;

/*
* In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so
* can't be used here to test for memory retention.
* Assume that tuning must be re-loaded.
*/
if (cs35l56_base->secured)
return true;

ret = pm_runtime_resume_and_get(cs35l56_base->dev);
if (ret) {
dev_err(cs35l56_base->dev, "Failed to runtime_get: %d\n", ret);
return ret;
}

ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &val);
if (ret)
dev_err(cs35l56_base->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
else
ret = !!(val & CS35L56_FIRMWARE_MISSING);

pm_runtime_put_autosuspend(cs35l56_base->dev);

return ret;
}
EXPORT_SYMBOL_NS_GPL(cs35l56_is_fw_reload_needed, SND_SOC_CS35L56_SHARED);

const struct cs_dsp_region cs35l56_dsp1_regions[] = {
{ .type = WMFW_HALO_PM_PACKED, .base = CS35L56_DSP1_PMEM_0 },
{ .type = WMFW_HALO_XM_PACKED, .base = CS35L56_DSP1_XMEM_PACKED_0 },
Expand Down
Loading

0 comments on commit 8a731fd

Please sign in to comment.