switch (event) { case SND_SOC_DAPM_PRE_PMU: /* Don't wait for ACK, we check in POST_PMU that it completed */ return regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
CS35L56_MBOX_CMD_AUDIO_PLAY); case SND_SOC_DAPM_POST_PMU: /* Wait for firmware to enter PS0 power state */
ret = regmap_read_poll_timeout(cs35l56->base.regmap,
cs35l56->base.fw_reg->transducer_actual_ps,
val, (val == CS35L56_PS0),
CS35L56_PS0_POLL_US,
CS35L56_PS0_TIMEOUT_US); if (ret)
dev_err(cs35l56->base.dev, "PS0 wait failed: %d\n", ret); return ret; case SND_SOC_DAPM_POST_PMD: return cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE); default: return 0;
}
}
/* Hi-Z DOUT in unused slots and when all TX are disabled */
regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL3,
CS35L56_ASP1_DOUT_HIZ_CTRL_MASK,
CS35L56_ASP_UNUSED_HIZ_OFF_HIZ);
return 0;
}
staticunsignedint cs35l56_make_tdm_config_word(unsignedint reg_val, unsignedlong mask)
{ unsignedint channel_shift; int bit_num;
/* Enable consecutive TX1..TXn for each of the slots set in mask */
channel_shift = 0;
for_each_set_bit(bit_num, &mask, 32) {
reg_val &= ~(0x3f << channel_shift);
reg_val |= bit_num << channel_shift;
channel_shift += 8;
}
return reg_val;
}
staticint cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsignedint tx_mask, unsignedint rx_mask, int slots, int slot_width)
{ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
/* More than 32 slots would give an unsupportable BCLK frequency */ if (slots > 32) {
dev_err(cs35l56->base.dev, "tdm invalid slot count %d\n", slots); return -EINVAL;
}
staticint cs35l56_write_cal(struct cs35l56_private *cs35l56)
{ int ret;
if (cs35l56->base.secured || !cs35l56->base.cal_data_valid) return -ENODATA;
ret = wm_adsp_run(&cs35l56->dsp); if (ret) return ret;
ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp,
cs35l56->base.calibration_controls,
&cs35l56->base.cal_data);
wm_adsp_stop(&cs35l56->dsp);
if (ret == 0)
dev_info(cs35l56->base.dev, "Calibration applied\n");
return ret;
}
staticint cs35l56_dsp_download_and_power_up(struct cs35l56_private *cs35l56, bool load_firmware)
{ int ret;
/* * Abort the first load if it didn't find the suffixed bins and * we have an alternate fallback suffix.
*/
cs35l56->dsp.bin_mandatory = (load_firmware && cs35l56->fallback_fw_suffix);
ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware); if ((ret == -ENOENT) && cs35l56->dsp.bin_mandatory) {
cs35l56->dsp.fwf_suffix = cs35l56->fallback_fw_suffix;
cs35l56->fallback_fw_suffix = NULL;
cs35l56->dsp.bin_mandatory = false;
ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware);
}
if (ret) {
dev_dbg(cs35l56->base.dev, "wm_adsp_power_up ret %d\n", ret); return ret;
}
return 0;
}
staticvoid cs35l56_reinit_patch(struct cs35l56_private *cs35l56)
{ int ret;
ret = cs35l56_dsp_download_and_power_up(cs35l56, true); if (ret) return;
cs35l56_write_cal(cs35l56);
/* Always REINIT after applying patch or coefficients */
cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
}
staticvoid cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing)
{ int ret;
/* * Disable SoundWire interrupts to prevent race with IRQ work. * Setting sdw_irq_no_unmask prevents the handler re-enabling * the SoundWire interrupt.
*/ if (cs35l56->sdw_peripheral) {
cs35l56->sdw_irq_no_unmask = true;
flush_work(&cs35l56->sdw_irq_work);
sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
flush_work(&cs35l56->sdw_irq_work);
}
ret = cs35l56_firmware_shutdown(&cs35l56->base); if (ret) goto err;
/* * Use wm_adsp to load and apply the firmware patch and coefficient files, * but only if firmware is missing. If firmware is already patched just * power-up wm_adsp without downloading firmware.
*/
ret = cs35l56_dsp_download_and_power_up(cs35l56, firmware_missing); if (ret) goto err;
if (cs35l56->sdw_peripheral) { /* * The system-reset causes the CS35L56 to detach from the bus. * Wait for the manager to re-enumerate the CS35L56 and * cs35l56_init() to run again.
*/ if (!wait_for_completion_timeout(&cs35l56->init_completion,
msecs_to_jiffies(5000))) {
dev_err(cs35l56->base.dev, "%s: init_completion timed out (SDW)\n",
__func__); goto err_unlock;
}
} elseif (cs35l56_init(cs35l56)) { goto err_unlock;
}
ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &firmware_version); if (ret) goto err;
/* Populate fw file qualifier with the revision and security state */
kfree(cs35l56->dsp.fwf_name); if (firmware_missing) {
cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x-dsp1", cs35l56->base.rev);
} else { /* Firmware files must match the running firmware version */
cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x%s-%06x-dsp1",
cs35l56->base.rev,
cs35l56->base.secured ? "-s" : "",
firmware_version);
}
if (!cs35l56->dsp.fwf_name) goto err;
dev_dbg(cs35l56->base.dev, "DSP fwf name: '%s' system name: '%s'\n",
cs35l56->dsp.fwf_name, cs35l56->dsp.system_name);
/* * The firmware cannot be patched if it is already running from * patch RAM. In this case the firmware files are versioned to * match the running firmware version and will only contain * tunings. We do not need to shutdown the firmware to apply * tunings so can use the lower cost reinit sequence instead.
*/ if (!firmware_missing)
cs35l56_reinit_patch(cs35l56); else
cs35l56_patch(cs35l56, firmware_missing);
/* * There are published firmware files for L56 B0 silicon using * the ALSA prefix as the filename suffix. Default to trying these * first, with the new name as an alternate.
*/ if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
}
switch (cs35l56->base.type) { case 0x54: case 0x56: case 0x57:
ret = snd_soc_add_component_controls(component, cs35l56_controls,
ARRAY_SIZE(cs35l56_controls)); break; case 0x63:
ret = snd_soc_add_component_controls(component, cs35l63_controls,
ARRAY_SIZE(cs35l63_controls)); break; default:
ret = -ENODEV; break;
}
if (ret) return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n");
switch (level) { case SND_SOC_BIAS_STANDBY: /* * Wait for patching to complete when transitioning from * BIAS_OFF to BIAS_STANDBY
*/ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
cs35l56_wait_dsp_ready(cs35l56);
int cs35l56_system_suspend(struct device *dev)
{ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
dev_dbg(dev, "system_suspend\n");
if (cs35l56->component)
flush_work(&cs35l56->dsp_work);
/* * The interrupt line is normally shared, but after we start suspending * we can't check if our device is the source of an interrupt, and can't * clear it. Prevent this race by temporarily disabling the parent irq * until we reach _no_irq.
*/ if (cs35l56->base.irq)
disable_irq(cs35l56->base.irq);
int cs35l56_system_suspend_late(struct device *dev)
{ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
dev_dbg(dev, "system_suspend_late\n");
/* * Assert RESET before removing supplies. * RESET is usually shared by all amps so it must not be asserted until * all driver instances have done their suspend() stage.
*/ if (cs35l56->base.reset_gpio) {
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
cs35l56_wait_min_reset_pulse();
}
int cs35l56_system_resume_no_irq(struct device *dev)
{ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
dev_dbg(dev, "system_resume_no_irq\n");
/* * WAKE interrupts unmask if the CS35L56 hibernates, which can cause * spurious interrupts, and the interrupt line is normally shared. * We can't check if our device is the source of an interrupt, and can't * clear it, until it has fully resumed. Prevent this race by temporarily * disabling the parent irq until we complete resume().
*/ if (cs35l56->base.irq)
disable_irq(cs35l56->base.irq);
int cs35l56_system_resume(struct device *dev)
{ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); int ret;
dev_dbg(dev, "system_resume\n");
/* * We might have done a hard reset or the CS35L56 was power-cycled * so wait for control port to be ready.
*/
cs35l56_wait_control_port_ready();
/* Undo pm_runtime_force_suspend() before re-enabling the irq */
ret = pm_runtime_force_resume(dev); if (cs35l56->base.irq)
enable_irq(cs35l56->base.irq);
if (ret) return ret;
/* Firmware won't have been loaded if the component hasn't probed */ if (!cs35l56->component) return 0;
ret = cs35l56_is_fw_reload_needed(&cs35l56->base);
dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret); if (ret < 1) return ret;
/* * dsp->part is filled in later as it is based on the DEVID. In a * SoundWire system that cannot be read until enumeration has occurred * and the device has attached.
*/
dsp->fw = 12;
dsp->wmfw_optional = true;
/* * None of the firmware controls need to be exported so add a no-op * callback that suppresses creating an ALSA control.
*/
dsp->control_add = &cs35l56_control_add_nop;
dev_dbg(cs35l56->base.dev, "DSP system name: '%s'\n", dsp->system_name);
ret = wm_halo_init(dsp); if (ret != 0) {
dev_err(cs35l56->base.dev, "wm_halo_init failed\n"); return ret;
}
ret = device_property_read_string(dev, "cirrus,firmware-uid", &prop); /* If bad sw node property, return 0 and fallback to legacy firmware path */ if (ret < 0) return 0;
/* Append a speaker qualifier if there is a speaker ID */ if (cs35l56->speaker_id >= 0)
cs35l56->dsp.system_name = devm_kasprintf(dev, GFP_KERNEL, "%s-spkid%d",
prop, cs35l56->speaker_id); else
cs35l56->dsp.system_name = devm_kstrdup(dev, prop, GFP_KERNEL);
if (cs35l56->dsp.system_name == NULL) return -ENOMEM;
/* * Some SoundWire laptops have a spk-id-gpios property but it points to * the wrong ACPI Device node so can't be used to get the GPIO. Try to * find the SDCA node containing the GpioIo resource and add a GPIO * mapping to it.
*/ staticconststruct acpi_gpio_params cs35l56_af01_first_gpio = { 0, 0, false }; staticconststruct acpi_gpio_mapping cs35l56_af01_spkid_gpios_mapping[] = {
{ "spk-id-gpios", &cs35l56_af01_first_gpio, 1 },
{ }
};
/* Find the SDCA node containing the GpioIo */
af01_fwnode = device_get_named_child_node(cs35l56->base.dev, "AF01"); if (!af01_fwnode) {
dev_dbg(cs35l56->base.dev, "No AF01 node\n"); return -ENOENT;
}
ret = acpi_dev_get_property(ACPI_COMPANION(cs35l56->base.dev), "spk-id-gpios", ACPI_TYPE_PACKAGE, &obj); if (ret) {
dev_dbg(cs35l56->base.dev, "Could not get spk-id-gpios package: %d\n", ret);
fwnode_handle_put(af01_fwnode); return -ENOENT;
}
/* The broken properties we can handle are a 4-element package (one GPIO) */ if (obj->package.count != 4) {
dev_warn(cs35l56->base.dev, "Unexpected spk-id element count %d\n",
obj->package.count);
fwnode_handle_put(af01_fwnode); return -ENOENT;
}
/* Add a GPIO mapping if it doesn't already have one */ if (!fwnode_property_present(af01_fwnode, "spk-id-gpios")) { struct acpi_device *adev = to_acpi_device_node(af01_fwnode);
/* * Can't use devm_acpi_dev_add_driver_gpios() because the * mapping isn't being added to the node pointed to by * ACPI_COMPANION().
*/
ret = acpi_dev_add_driver_gpios(adev, cs35l56_af01_spkid_gpios_mapping); if (ret) {
fwnode_handle_put(af01_fwnode); return dev_err_probe(cs35l56->base.dev, ret, "Failed to add gpio mapping to AF01\n");
}
ret = devm_add_action_or_reset(cs35l56->base.dev,
cs35l56_acpi_dev_release_driver_gpios,
adev); if (ret) {
fwnode_handle_put(af01_fwnode); return ret;
}
dev_dbg(cs35l56->base.dev, "Added spk-id-gpios mapping to AF01\n");
}
desc = fwnode_gpiod_get_index(af01_fwnode, "spk-id", 0, GPIOD_IN, NULL); if (IS_ERR(desc)) {
fwnode_handle_put(af01_fwnode);
ret = PTR_ERR(desc); return dev_err_probe(cs35l56->base.dev, ret, "Get GPIO from AF01 failed\n");
}
ret = gpiod_get_value_cansleep(desc);
gpiod_put(desc);
cs35l56_fill_supply_names(cs35l56->supplies);
ret = devm_regulator_bulk_get(cs35l56->base.dev, ARRAY_SIZE(cs35l56->supplies),
cs35l56->supplies); if (ret != 0) return dev_err_probe(cs35l56->base.dev, ret, "Failed to request supplies\n");
/* Reset could be controlled by the BIOS or shared by multiple amps */
cs35l56->base.reset_gpio = devm_gpiod_get_optional(cs35l56->base.dev, "reset",
GPIOD_OUT_LOW); if (IS_ERR(cs35l56->base.reset_gpio)) {
ret = PTR_ERR(cs35l56->base.reset_gpio); /* * If RESET is shared the first amp to probe will grab the reset * line and reset all the amps
*/ if (ret != -EBUSY) return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n");
ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); if (ret != 0) return dev_err_probe(cs35l56->base.dev, ret, "Failed to enable supplies\n");
if (cs35l56->base.reset_gpio) { /* ACPI can override GPIOD_OUT_LOW flag so force it to start low */
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
cs35l56_wait_min_reset_pulse();
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
}
ret = cs35l56_get_speaker_id(&cs35l56->base); if (ACPI_COMPANION(cs35l56->base.dev) && cs35l56->sdw_peripheral && (ret == -ENOENT))
ret = cs35l56_try_get_broken_sdca_spkid_gpio(cs35l56);
if ((ret < 0) && (ret != -ENOENT)) goto err;
cs35l56->speaker_id = ret;
ret = cs35l56_get_firmware_uid(cs35l56); if (ret != 0) goto err;
ret = cs35l56_dsp_init(cs35l56); if (ret < 0) {
dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n"); goto err;
}
ret = devm_snd_soc_register_component(cs35l56->base.dev,
&soc_component_dev_cs35l56,
cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); if (ret < 0) {
dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); goto err;
}
int cs35l56_init(struct cs35l56_private *cs35l56)
{ int ret;
/* * Check whether the actions associated with soft reset or one time * init need to be performed.
*/ if (cs35l56->soft_resetting) goto post_soft_reset;
ret = cs35l56_hw_init(&cs35l56->base); if (ret < 0) return ret;
ret = cs35l56_set_patch(&cs35l56->base); if (ret) return ret;
ret = cs35l56_get_calibration(&cs35l56->base); if (ret) return ret;
if (!cs35l56->base.reset_gpio) {
dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n");
cs35l56->soft_resetting = true;
cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral); if (cs35l56->sdw_peripheral) { /* Keep alive while we wait for re-enumeration */
pm_runtime_get_noresume(cs35l56->base.dev); return 0;
}
}
post_soft_reset: if (cs35l56->soft_resetting) {
cs35l56->soft_resetting = false;
/* Done re-enumerating after one-time init so release the keep-alive */ if (cs35l56->sdw_peripheral && !cs35l56->base.init_done)
pm_runtime_put_noidle(cs35l56->base.dev);
regcache_mark_dirty(cs35l56->base.regmap);
ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); if (ret) return ret;
dev_dbg(cs35l56->base.dev, "Firmware rebooted after soft reset\n");
/* Disable auto-hibernate so that runtime_pm has control */
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); if (ret) return ret;
/* Registers could be dirty after soft reset or SoundWire enumeration */
regcache_sync(cs35l56->base.regmap);
/* Set ASP1 DOUT to high-impedance when it is not transmitting audio data. */
ret = regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL3,
CS35L56_ASP1_DOUT_HIZ_CTRL_MASK); if (ret) return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n");
/* * WAKE IRQs unmask if CS35L56 hibernates so free the handler to * prevent it racing with remove().
*/ if (cs35l56->base.irq)
devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base);
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.