ret = cs35l56_runtime_resume_common(&cs35l56->base, false); if (ret < 0) return ret;
if (cs35l56->cs_dsp.booted) {
ret = cs_dsp_run(&cs35l56->cs_dsp); if (ret) {
dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret); goto err;
}
}
/* * Make sure that filename is lower-case and any non alpha-numeric * characters except full stop and forward slash are replaced with * hyphens.
*/
s = *filename; while (*s) {
c = *s; if (isalnum(c))
*s = tolower(c); elseif (c != '.' && c != '/')
*s = '-';
s++;
}
ret = firmware_request_nowarn(firmware, *filename, cs35l56->base.dev); if (ret) {
dev_dbg(cs35l56->base.dev, "Failed to request '%s'\n", *filename);
kfree(*filename);
*filename = NULL; return ret;
}
if (system_name) { if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
base_name, system_name, NULL, "wmfw")) { if (amp_name)
cs35l56_hda_request_firmware_file(cs35l56,
coeff_firmware, coeff_filename,
base_name, system_name,
amp_name, "bin"); if (!*coeff_firmware)
cs35l56_hda_request_firmware_file(cs35l56,
coeff_firmware, coeff_filename,
base_name, system_name,
NULL, "bin"); return;
}
/* * Check for system-specific bin files without wmfw before * falling back to generic firmware
*/ if (amp_name)
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
base_name, system_name, amp_name, "bin"); if (!*coeff_firmware)
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
base_name, system_name, NULL, "bin");
/* * Prepare for a new DSP power-up. If the DSP has had firmware * downloaded previously then it needs to be powered down so that it * can be updated.
*/ if (cs35l56->base.fw_patched)
cs_dsp_power_down(&cs35l56->cs_dsp);
cs35l56->base.fw_patched = false;
ret = pm_runtime_resume_and_get(cs35l56->base.dev); if (ret < 0) {
dev_err(cs35l56->base.dev, "Failed to resume and get %d\n", ret); return;
}
/* * The firmware can only be upgraded if it is currently running * from the built-in ROM. If not, the wmfw/bin must be for the * version of firmware that is running on the chip.
*/
ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &preloaded_fw_ver); if (ret) goto err_pm_put;
/* * If the BIOS didn't patch the firmware a bin file is mandatory to * enable the ASP·
*/ if (!coeff_firmware && firmware_missing) {
dev_err(cs35l56->base.dev, ".bin file required but not found\n"); goto err_fw_release;
}
mutex_lock(&cs35l56->base.irq_lock);
/* * If the firmware hasn't been patched it must be shutdown before * doing a full patch and reset afterwards. If it is already * running a patched version the firmware files only contain * tunings and we can use the lower cost reinit sequence instead.
*/ if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
ret = cs35l56_firmware_shutdown(&cs35l56->base); if (ret) goto err;
}
ret = cs_dsp_power_up(&cs35l56->cs_dsp, wmfw_firmware, wmfw_filename,
coeff_firmware, coeff_filename, "misc"); if (ret) {
dev_dbg(cs35l56->base.dev, "%s: cs_dsp_power_up ret %d\n", __func__, ret); goto err;
}
if (wmfw_filename)
dev_dbg(cs35l56->base.dev, "Loaded WMFW Firmware: %s\n", wmfw_filename);
if (coeff_filename)
dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
/* If we downloaded firmware, reset the device and wait for it to boot */ if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
cs35l56_system_reset(&cs35l56->base, false);
regcache_mark_dirty(cs35l56->base.regmap);
ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); if (ret) goto err_powered_up;
/* Disable auto-hibernate so that runtime_pm has control */
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); if (ret) goto err_powered_up;
ret = cs_dsp_run(&cs35l56->cs_dsp); if (ret)
dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
cs35l56_hda_apply_calibration(cs35l56);
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); if (ret)
cs_dsp_stop(&cs35l56->cs_dsp);
/* * 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);
/* * 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();
}
/* * 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);
/* 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;
cs35l56->suspended = false;
if (!cs35l56->codec) return 0;
ret = cs35l56_is_fw_reload_needed(&cs35l56->base);
dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret); if (ret > 0)
queue_work(system_long_wq, &cs35l56->dsp_work);
if (cs35l56->playing)
cs35l56_hda_play(cs35l56);
return 0;
}
staticint cs35l56_hda_fixup_yoga9(struct cs35l56_hda *cs35l56, int *bus_addr)
{ /* The cirrus,dev-index property has the wrong values */ switch (*bus_addr) { case 0x30:
cs35l56->index = 1; return 0; case 0x31:
cs35l56->index = 0; return 0; default: /* There is a pseudo-address for broadcast to both amps - ignore it */
dev_dbg(cs35l56->base.dev, "Ignoring I2C address %#x\n", *bus_addr); return 0;
}
}
staticconststruct { constchar *sub; int (*fixup_fn)(struct cs35l56_hda *cs35l56, int *bus_addr);
} cs35l56_hda_fixups[] = {
{
.sub = "17AA390B", /* Lenovo Yoga Book 9i GenX */
.fixup_fn = cs35l56_hda_fixup_yoga9,
},
};
staticint cs35l56_hda_apply_platform_fixups(struct cs35l56_hda *cs35l56, constchar *sub, int *bus_addr)
{ int i;
if (IS_ERR(sub)) return 0;
for (i = 0; i < ARRAY_SIZE(cs35l56_hda_fixups); i++) { if (strcasecmp(cs35l56_hda_fixups[i].sub, sub) == 0) {
dev_dbg(cs35l56->base.dev, "Applying fixup for %s\n",
cs35l56_hda_fixups[i].sub); return (cs35l56_hda_fixups[i].fixup_fn)(cs35l56, bus_addr);
}
}
return 0;
}
staticint cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id)
{
u32 values[HDA_MAX_COMPONENTS]; char hid_string[8]; struct acpi_device *adev; constchar *property, *sub;
size_t nval; int i, ret;
/* * ACPI_COMPANION isn't available when this driver was instantiated by * the serial-multi-instantiate driver, so lookup the node by HID
*/ if (!ACPI_COMPANION(cs35l56->base.dev)) {
snprintf(hid_string, sizeof(hid_string), "CSC%04X", hid);
adev = acpi_dev_get_first_match_dev(hid_string, NULL, -1); if (!adev) {
dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n",
dev_name(cs35l56->base.dev)); return -ENODEV;
}
ACPI_COMPANION_SET(cs35l56->base.dev, adev);
}
/* Initialize things that could be overwritten by a fixup */
cs35l56->index = -1;
sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev));
ret = cs35l56_hda_apply_platform_fixups(cs35l56, sub, &id); if (ret) return ret;
if (cs35l56->index == -1) {
property = "cirrus,dev-index";
ret = device_property_count_u32(cs35l56->base.dev, property); if (ret <= 0) goto err;
if (ret > ARRAY_SIZE(values)) {
ret = -EINVAL; goto err;
}
nval = ret;
ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval); if (ret) goto err;
for (i = 0; i < nval; i++) { if (values[i] == id) {
cs35l56->index = i; break;
}
}
/* * It's not an error for the ID to be missing: for I2C there can be * an alias address that is not a real device. So reject silently.
*/ if (cs35l56->index == -1) {
dev_dbg(cs35l56->base.dev, "No index found in %s\n", property);
ret = -ENODEV; goto err;
}
}
cs35l56->base.reset_gpio = devm_gpiod_get_index_optional(cs35l56->base.dev, "reset",
cs35l56->index,
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");
if (cs35l56->base.reset_gpio) {
dev_dbg(cs35l56->base.dev, "Hard reset\n");
/* * The GPIOD_OUT_LOW to *_gpiod_get_*() will be ignored if the * ACPI defines a different default state. So explicitly set 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_hw_init(&cs35l56->base); if (ret < 0) goto err;
/* Reset the device and wait for it to boot */
cs35l56_system_reset(&cs35l56->base, false);
ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); if (ret) goto err;
regcache_cache_only(cs35l56->base.regmap, false);
ret = cs35l56_set_patch(&cs35l56->base); if (ret) goto err;
/* Disable auto-hibernate so that runtime_pm has control */
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); if (ret) goto err;
ret = cs35l56_get_calibration(&cs35l56->base); if (ret) goto err;
ret = cs_dsp_halo_init(&cs35l56->cs_dsp); if (ret) {
dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n"); goto err;
}
dev_info(cs35l56->base.dev, "DSP system name: '%s', amp name: '%s'\n",
cs35l56->system_name, cs35l56->amp_name);
/* * By default only enable one ASP1TXn, where n=amplifier index, * This prevents multiple amps trying to drive the same slot.
*/
cs35l56->asp_tx_mask = BIT(cs35l56->index);
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.