staticconststruct reg_sequence cs35l56_patch_fw[] = { /* These are not reset by a soft-reset, so patch to defaults. */
{ CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 },
{ CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
{ CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
};
staticconststruct reg_sequence cs35l63_patch_fw[] = { /* These are not reset by a soft-reset, so patch to defaults. */
{ CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
{ CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
{ CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 },
};
int cs35l56_set_patch(struct cs35l56_base *cs35l56_base)
{ int ret;
ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch,
ARRAY_SIZE(cs35l56_patch)); if (ret) return ret;
switch (cs35l56_base->type) { case 0x54: case 0x56: case 0x57:
ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch_fw,
ARRAY_SIZE(cs35l56_patch_fw)); break; case 0x63:
ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw,
ARRAY_SIZE(cs35l63_patch_fw)); break; default: break;
}
staticbool cs35l56_is_dsp_memory(unsignedint reg)
{ switch (reg) { case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143: case CS35L56_DSP1_XMEM_UNPACKED32_0 ... CS35L56_DSP1_XMEM_UNPACKED32_4095: case CS35L56_DSP1_XMEM_UNPACKED24_0 ... CS35L56_DSP1_XMEM_UNPACKED24_8191: case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604: case CS35L56_DSP1_YMEM_UNPACKED32_0 ... CS35L56_DSP1_YMEM_UNPACKED32_3070: case CS35L56_DSP1_YMEM_UNPACKED24_0 ... CS35L56_DSP1_YMEM_UNPACKED24_6141: case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114: returntrue; default: returnfalse;
}
}
staticbool cs35l56_readable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case CS35L56_DEVID: case CS35L56_REVID: case CS35L56_RELID: case CS35L56_OTPID: case CS35L56_SFT_RESET: case CS35L56_GLOBAL_ENABLES: case CS35L56_BLOCK_ENABLES: case CS35L56_BLOCK_ENABLES2: case CS35L56_REFCLK_INPUT: case CS35L56_GLOBAL_SAMPLE_RATE: case CS35L56_OTP_MEM_53: case CS35L56_OTP_MEM_54: case CS35L56_OTP_MEM_55: case CS35L56_ASP1_ENABLES1: case CS35L56_ASP1_CONTROL1: case CS35L56_ASP1_CONTROL2: case CS35L56_ASP1_CONTROL3: case CS35L56_ASP1_FRAME_CONTROL1: case CS35L56_ASP1_FRAME_CONTROL5: case CS35L56_ASP1_DATA_CONTROL1: case CS35L56_ASP1_DATA_CONTROL5: case CS35L56_DACPCM1_INPUT: case CS35L56_DACPCM2_INPUT: case CS35L56_ASP1TX1_INPUT: case CS35L56_ASP1TX2_INPUT: case CS35L56_ASP1TX3_INPUT: case CS35L56_ASP1TX4_INPUT: case CS35L56_DSP1RX1_INPUT: case CS35L56_DSP1RX2_INPUT: case CS35L56_SWIRE_DP3_CH1_INPUT: case CS35L56_SWIRE_DP3_CH2_INPUT: case CS35L56_SWIRE_DP3_CH3_INPUT: case CS35L56_SWIRE_DP3_CH4_INPUT: case CS35L56_IRQ1_CFG: case CS35L56_IRQ1_STATUS: case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8: case CS35L56_IRQ1_EINT_18: case CS35L56_IRQ1_EINT_20: case CS35L56_IRQ1_MASK_1: case CS35L56_IRQ1_MASK_2: case CS35L56_IRQ1_MASK_4: case CS35L56_IRQ1_MASK_8: case CS35L56_IRQ1_MASK_18: case CS35L56_IRQ1_MASK_20: case CS35L56_DSP_VIRTUAL1_MBOX_1: case CS35L56_DSP_VIRTUAL1_MBOX_2: case CS35L56_DSP_VIRTUAL1_MBOX_3: case CS35L56_DSP_VIRTUAL1_MBOX_4: case CS35L56_DSP_VIRTUAL1_MBOX_5: case CS35L56_DSP_VIRTUAL1_MBOX_6: case CS35L56_DSP_VIRTUAL1_MBOX_7: case CS35L56_DSP_VIRTUAL1_MBOX_8: case CS35L56_DIE_STS1: case CS35L56_DIE_STS2: case CS35L56_DSP_RESTRICT_STS1: case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END: case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0: case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1: case CS35L56_DSP1_SCRATCH1: case CS35L56_DSP1_SCRATCH2: case CS35L56_DSP1_SCRATCH3: case CS35L56_DSP1_SCRATCH4: returntrue; default: return cs35l56_is_dsp_memory(reg);
}
}
staticbool cs35l56_precious_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143: case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604: case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114: returntrue; default: returnfalse;
}
}
staticbool cs35l56_common_volatile_reg(unsignedint reg)
{ switch (reg) { case CS35L56_DEVID: case CS35L56_REVID: case CS35L56_RELID: case CS35L56_OTPID: case CS35L56_SFT_RESET: case CS35L56_GLOBAL_ENABLES: /* owned by firmware */ case CS35L56_BLOCK_ENABLES: /* owned by firmware */ case CS35L56_BLOCK_ENABLES2: /* owned by firmware */ case CS35L56_REFCLK_INPUT: /* owned by firmware */ case CS35L56_GLOBAL_SAMPLE_RATE: /* owned by firmware */ case CS35L56_DACPCM1_INPUT: /* owned by firmware */ case CS35L56_DACPCM2_INPUT: /* owned by firmware */ case CS35L56_DSP1RX1_INPUT: /* owned by firmware */ case CS35L56_DSP1RX2_INPUT: /* owned by firmware */ case CS35L56_IRQ1_STATUS: case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8: case CS35L56_IRQ1_EINT_18: case CS35L56_IRQ1_EINT_20: case CS35L56_DSP_VIRTUAL1_MBOX_1: case CS35L56_DSP_VIRTUAL1_MBOX_2: case CS35L56_DSP_VIRTUAL1_MBOX_3: case CS35L56_DSP_VIRTUAL1_MBOX_4: case CS35L56_DSP_VIRTUAL1_MBOX_5: case CS35L56_DSP_VIRTUAL1_MBOX_6: case CS35L56_DSP_VIRTUAL1_MBOX_7: case CS35L56_DSP_VIRTUAL1_MBOX_8: case CS35L56_DSP_RESTRICT_STS1: case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END: case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0: case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1: case CS35L56_DSP1_SCRATCH1: case CS35L56_DSP1_SCRATCH2: case CS35L56_DSP1_SCRATCH3: case CS35L56_DSP1_SCRATCH4: returntrue; default: return cs35l56_is_dsp_memory(reg);
}
}
staticbool cs35l56_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case CS35L56_MAIN_RENDER_USER_MUTE: case CS35L56_MAIN_RENDER_USER_VOLUME: case CS35L56_MAIN_POSTURE_NUMBER: returnfalse; default: return cs35l56_common_volatile_reg(reg);
}
}
staticbool cs35l63_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case CS35L63_MAIN_RENDER_USER_MUTE: case CS35L63_MAIN_RENDER_USER_VOLUME: case CS35L63_MAIN_POSTURE_NUMBER: returnfalse; default: return cs35l56_common_volatile_reg(reg);
}
}
int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsignedint command)
{ unsignedint val; int ret;
int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base)
{ int ret; unsignedint val;
ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_SHUTDOWN); if (ret) return ret;
ret = regmap_read_poll_timeout(cs35l56_base->regmap,
cs35l56_base->fw_reg->pm_cur_stat,
val, (val == CS35L56_HALO_STATE_SHUTDOWN),
CS35L56_HALO_STATE_POLL_US,
CS35L56_HALO_STATE_TIMEOUT_US); if (ret < 0)
dev_err(cs35l56_base->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n",
val, ret); return ret;
}
EXPORT_SYMBOL_NS_GPL(cs35l56_firmware_shutdown, "SND_SOC_CS35L56_SHARED");
int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base)
{ unsignedint val = 0; int read_ret, poll_ret;
/* * The regmap must remain in cache-only until the chip has * booted, so use a bypassed read of the status register.
*/
poll_ret = read_poll_timeout(regmap_read_bypassed, read_ret,
(val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
CS35L56_HALO_STATE_POLL_US,
CS35L56_HALO_STATE_TIMEOUT_US, false,
cs35l56_base->regmap,
cs35l56_base->fw_reg->halo_state,
&val);
void cs35l56_wait_control_port_ready(void)
{ /* Wait for control port to be ready (datasheet tIRS). */
usleep_range(CS35L56_CONTROL_PORT_READY_US, 2 * CS35L56_CONTROL_PORT_READY_US);
}
EXPORT_SYMBOL_NS_GPL(cs35l56_wait_control_port_ready, "SND_SOC_CS35L56_SHARED");
/* * 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;
}
/* * 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);
}
}
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);
if (cs35l56_is_spi(cs35l56_base)) {
cs35l56_spi_system_reset(cs35l56_base); return;
}
switch (cs35l56_base->type) { case 0x54: case 0x56: case 0x57:
regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
cs35l56_system_reset_seq,
ARRAY_SIZE(cs35l56_system_reset_seq)); break; case 0x63:
regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
cs35l63_system_reset_seq,
ARRAY_SIZE(cs35l63_system_reset_seq)); break; default: break;
}
/* On SoundWire the registers won't be accessible until it re-enumerates. */ if (is_soundwire) return;
cs35l56_wait_control_port_ready();
/* Leave in cache-only. This will be revoked when the chip has rebooted. */
}
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 < 1) 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);
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);
int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base)
{ unsignedint val; int ret;
/* * 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) returntrue;
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_base->fw_reg->prot_sts,
&val); if (ret)
dev_err(cs35l56_base->dev, "Failed to read PROTECTION_STATUS: %d\n", ret); else
ret = !!(val & CS35L56_FIRMWARE_MISSING);
staticconststruct reg_sequence cs35l56_hibernate_seq[] = { /* This must be the last register access */
REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE),
};
/* * Dummy transactions to trigger I2C/SPI auto-wake. Issue two * transactions to meet the minimum required time from the rising edge * to the last falling edge of wake. * * It uses bypassed read because we must wake the chip before * disabling regmap cache-only.
*/
regmap_read_bypassed(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base)
{ unsignedint val; int ret;
if (!cs35l56_base->init_done) return 0;
/* Firmware must have entered a power-save state */
ret = regmap_read_poll_timeout(cs35l56_base->regmap,
cs35l56_base->fw_reg->transducer_actual_ps,
val, (val >= CS35L56_PS3),
CS35L56_PS3_POLL_US,
CS35L56_PS3_TIMEOUT_US); if (ret)
dev_warn(cs35l56_base->dev, "PS3 wait failed: %d\n", ret);
/* Clear BOOT_DONE so it can be used to detect a reboot */
regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK);
if (!cs35l56_base->can_hibernate) {
regcache_cache_only(cs35l56_base->regmap, true);
dev_dbg(cs35l56_base->dev, "Suspended: no hibernate");
return 0;
}
/* * Must enter cache-only first so there can't be any more register * accesses other than the controlled hibernate sequence below.
*/
regcache_cache_only(cs35l56_base->regmap, true);
int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire)
{ unsignedint val; int ret;
if (!cs35l56_base->init_done) return 0;
if (!cs35l56_base->can_hibernate) goto out_sync;
/* Must be done before releasing cache-only */ if (!is_soundwire)
cs35l56_issue_wake_event(cs35l56_base);
out_sync:
ret = cs35l56_wait_for_firmware_boot(cs35l56_base); if (ret) {
dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); goto err;
}
regcache_cache_only(cs35l56_base->regmap, false);
ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); if (ret) goto err;
/* BOOT_DONE will be 1 if the amp reset */
regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, &val); if (val & CS35L56_OTP_BOOT_DONE_MASK) {
dev_dbg(cs35l56_base->dev, "Registers reset in suspend\n");
regcache_mark_dirty(cs35l56_base->regmap);
}
int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base)
{
u64 silicon_uid = 0; int ret;
/* Driver can't apply calibration to a secured part, so skip */ if (cs35l56_base->secured) return 0;
switch (cs35l56_base->type) { case 0x54: case 0x56: case 0x57:
ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid); break; case 0x63:
ret = cs35l63_read_silicon_uid(cs35l56_base, &silicon_uid); break; default:
ret = -ENODEV; break;
}
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
{ int ret; unsignedint devid, revid, otpid, secured, fw_ver; bool fw_missing;
/* * When the system is not using a reset_gpio ensure the device is * awake, otherwise the device has just been released from reset and * the driver must wait for the control port to become usable.
*/ if (!cs35l56_base->reset_gpio)
cs35l56_issue_wake_event(cs35l56_base); else
cs35l56_wait_control_port_ready();
ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_REVID, &revid); if (ret < 0) {
dev_err(cs35l56_base->dev, "Get Revision ID failed\n"); return ret;
}
cs35l56_base->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK);
ret = cs35l56_wait_for_firmware_boot(cs35l56_base); if (ret) return ret;
ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_DEVID, &devid); if (ret < 0) {
dev_err(cs35l56_base->dev, "Get Device ID failed\n"); return ret;
}
devid &= CS35L56_DEVID_MASK;
switch (devid) { case 0x35A54: case 0x35A56: case 0x35A57:
cs35l56_base->calibration_controls = &cs35l56_calibration_controls; break; case 0x35A630:
cs35l56_base->calibration_controls = &cs35l63_calibration_controls;
devid = devid >> 4; break; default:
dev_err(cs35l56_base->dev, "Unknown device %x\n", devid); return -ENODEV;
}
cs35l56_base->type = devid & 0xFF;
/* Silicon is now identified and booted so exit cache-only */
regcache_cache_only(cs35l56_base->regmap, false);
ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured); if (ret) {
dev_err(cs35l56_base->dev, "Get Secure status failed\n"); return ret;
}
/* When any bus is restricted treat the device as secured */ if (secured & CS35L56_RESTRICTED_MASK)
cs35l56_base->secured = true;
ret = regmap_read(cs35l56_base->regmap, CS35L56_OTPID, &otpid); if (ret < 0) {
dev_err(cs35l56_base->dev, "Get OTP ID failed\n"); return ret;
}
ret = cs35l56_read_prot_status(cs35l56_base, &fw_missing, &fw_ver); if (ret) return ret;
/* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1,
CS35L56_AMP_SHORT_ERR_EINT1_MASK,
0);
regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8,
CS35L56_TEMP_ERR_EINT1_MASK,
0);
int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base)
{ struct gpio_descs *descs;
u32 speaker_id; int i, ret;
/* Attempt to read the speaker type from a device property first */
ret = device_property_read_u32(cs35l56_base->dev, "cirrus,speaker-id", &speaker_id); if (!ret) {
dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id); return speaker_id;
}
/* Read the speaker type qualifier from the motherboard GPIOs */
descs = gpiod_get_array_optional(cs35l56_base->dev, "spk-id", GPIOD_IN); if (!descs) { return -ENOENT;
} elseif (IS_ERR(descs)) {
ret = PTR_ERR(descs); return dev_err_probe(cs35l56_base->dev, ret, "Failed to get spk-id-gpios\n");
}
speaker_id = 0; for (i = 0; i < descs->ndescs; i++) {
ret = gpiod_get_value_cansleep(descs->desc[i]); if (ret < 0) {
dev_err_probe(cs35l56_base->dev, ret, "Failed to read spk-id[%d]\n", i); goto err;
}
speaker_id |= (ret << i);
}
dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
ret = speaker_id;
err:
gpiod_put_array(descs);
int cs35l56_get_bclk_freq_id(unsignedint freq)
{ int i;
if (freq == 0) return -EINVAL;
/* The BCLK frequency must be a valid PLL REFCLK */ for (i = 0; i < ARRAY_SIZE(cs35l56_bclk_valid_for_pll_freq_table); ++i) { if (cs35l56_bclk_valid_for_pll_freq_table[i] == freq) return i;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.