Commit dca45efb authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull sound fixes from Takashi Iwai:
 "Here are a few remaining patches for 6.1-rc1.

  The major changes are the hibernation fixes for HD-audio CS35L41 codec
  and the USB-audio small fixes against the last change. In addition, a
  couple of HD-audio regression fixes and a couple of potential
  mutex-deadlock fixes with OSS emulation in ALSA core side are seen"

* tag 'sound-fix-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound:
  ALSA: hda: cs35l41: Support System Suspend
  ALSA: hda: cs35l41: Remove suspend/resume hda hooks
  ALSA: hda/cs_dsp_ctl: Fix mutex inversion when creating controls
  ALSA: hda: hda_cs_dsp_ctl: Ensure pwr_lock is held before reading/writing controls
  ALSA: hda: hda_cs_dsp_ctl: Minor clean and redundant code removal
  ALSA: oss: Fix potential deadlock at unregistration
  ALSA: rawmidi: Drop register_mutex in snd_rawmidi_free()
  ALSA: hda/realtek: Add Intel Reference SSID to support headset keys
  ALSA: hda/realtek: Add quirk for ASUS GV601R laptop
  ALSA: hda/realtek: Correct pin configs for ASUS G533Z
  ALSA: usb-audio: Avoid superfluous endpoint setup
  ALSA: usb-audio: Correct the return code from snd_usb_endpoint_set_params()
  ALSA: usb-audio: Apply mutex around snd_usb_endpoint_set_params()
  ALSA: usb-audio: Avoid unnecessary interface change at EP close
  ALSA: hda: Update register polling macros
  ALSA: hda/realtek: remove ALC289_FIXUP_DUAL_SPK for Dell 5530
parents 5964c927 88672826
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -592,11 +592,11 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
#define snd_hdac_stream_readb(dev, reg) \
	snd_hdac_reg_readb((dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_readb_poll(dev, reg, val, cond, delay_us, timeout_us) \
	readb_poll_timeout((dev)->sd_addr + AZX_REG_ ## reg, val, cond, \
			   delay_us, timeout_us)
	read_poll_timeout_atomic(snd_hdac_reg_readb, val, cond, delay_us, timeout_us, \
				 false, (dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_readl_poll(dev, reg, val, cond, delay_us, timeout_us) \
	readl_poll_timeout((dev)->sd_addr + AZX_REG_ ## reg, val, cond, \
			   delay_us, timeout_us)
	read_poll_timeout_atomic(snd_hdac_reg_readl, val, cond, delay_us, timeout_us, \
				 false, (dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)

/* update a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_updatel(dev, reg, mask, val) \
+0 −2
Original line number Diff line number Diff line
@@ -1899,10 +1899,8 @@ static int snd_rawmidi_free(struct snd_rawmidi *rmidi)

	snd_info_free_entry(rmidi->proc_entry);
	rmidi->proc_entry = NULL;
	mutex_lock(&register_mutex);
	if (rmidi->ops && rmidi->ops->dev_unregister)
		rmidi->ops->dev_unregister(rmidi);
	mutex_unlock(&register_mutex);

	snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
	snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
+9 −4
Original line number Diff line number Diff line
@@ -162,7 +162,6 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
		mutex_unlock(&sound_oss_mutex);
		return -ENOENT;
	}
	unregister_sound_special(minor);
	switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
	case SNDRV_MINOR_OSS_PCM:
		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
@@ -174,12 +173,18 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
		track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
		break;
	}
	if (track2 >= 0) {
		unregister_sound_special(track2);
	if (track2 >= 0)
		snd_oss_minors[track2] = NULL;
	}
	snd_oss_minors[minor] = NULL;
	mutex_unlock(&sound_oss_mutex);

	/* call unregister_sound_special() outside sound_oss_mutex;
	 * otherwise may deadlock, as it can trigger the release of a card
	 */
	unregister_sound_special(minor);
	if (track2 >= 0)
		unregister_sound_special(track2);

	kfree(mptr);
	return 0;
}
+151 −46
Original line number Diff line number Diff line
@@ -91,20 +91,18 @@ static const struct reg_sequence cs35l41_hda_mute[] = {
	{ CS35L41_AMP_DIG_VOL_CTRL,	0x0000A678 }, // AMP_VOL_PCM Mute
};

static int cs35l41_control_add(struct cs_dsp_coeff_ctl *cs_ctl)
static void cs35l41_add_controls(struct cs35l41_hda *cs35l41)
{
	struct cs35l41_hda *cs35l41 = container_of(cs_ctl->dsp, struct cs35l41_hda, cs_dsp);
	struct hda_cs_dsp_ctl_info info;

	info.device_name = cs35l41->amp_name;
	info.fw_type = cs35l41->firmware_type;
	info.card = cs35l41->codec->card;

	return hda_cs_dsp_control_add(cs_ctl, &info);
	hda_cs_dsp_add_controls(&cs35l41->cs_dsp, &info);
}

static const struct cs_dsp_client_ops client_ops = {
	.control_add = cs35l41_control_add,
	.control_remove = hda_cs_dsp_control_remove,
};

@@ -435,6 +433,8 @@ static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41)
	if (ret)
		goto err_release;

	cs35l41_add_controls(cs35l41);

	ret = cs35l41_save_calibration(cs35l41);

err_release:
@@ -461,9 +461,12 @@ static void cs35l41_remove_dsp(struct cs35l41_hda *cs35l41)
	struct cs_dsp *dsp = &cs35l41->cs_dsp;

	cancel_work_sync(&cs35l41->fw_load_work);

	mutex_lock(&cs35l41->fw_mutex);
	cs35l41_shutdown_dsp(cs35l41);
	cs_dsp_remove(dsp);
	cs35l41->halo_initialized = false;
	mutex_unlock(&cs35l41->fw_mutex);
}

/* Protection release cycle to get the speaker out of Safe-Mode */
@@ -487,10 +490,10 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action)
	struct regmap *reg = cs35l41->regmap;
	int ret = 0;

	mutex_lock(&cs35l41->fw_mutex);

	switch (action) {
	case HDA_GEN_PCM_ACT_OPEN:
		pm_runtime_get_sync(dev);
		mutex_lock(&cs35l41->fw_mutex);
		cs35l41->playback_started = true;
		if (cs35l41->firmware_running) {
			regmap_multi_reg_write(reg, cs35l41_hda_config_dsp,
@@ -508,15 +511,21 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action)
					 CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT);
		if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
			regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001);
		mutex_unlock(&cs35l41->fw_mutex);
		break;
	case HDA_GEN_PCM_ACT_PREPARE:
		mutex_lock(&cs35l41->fw_mutex);
		ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1);
		mutex_unlock(&cs35l41->fw_mutex);
		break;
	case HDA_GEN_PCM_ACT_CLEANUP:
		mutex_lock(&cs35l41->fw_mutex);
		regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
		ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0);
		mutex_unlock(&cs35l41->fw_mutex);
		break;
	case HDA_GEN_PCM_ACT_CLOSE:
		mutex_lock(&cs35l41->fw_mutex);
		ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2,
					 CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
		if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
@@ -530,14 +539,16 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action)
		}
		cs35l41_irq_release(cs35l41);
		cs35l41->playback_started = false;
		mutex_unlock(&cs35l41->fw_mutex);

		pm_runtime_mark_last_busy(dev);
		pm_runtime_put_autosuspend(dev);
		break;
	default:
		dev_warn(cs35l41->dev, "Playback action not supported: %d\n", action);
		break;
	}

	mutex_unlock(&cs35l41->fw_mutex);

	if (ret)
		dev_err(cs35l41->dev, "Regmap access fail: %d\n", ret);
}
@@ -562,45 +573,148 @@ static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsi
				    rx_slot);
}

static int cs35l41_runtime_suspend(struct device *dev)
static void cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41)
{
	mutex_lock(&cs35l41->fw_mutex);
	if (cs35l41->firmware_running) {

		regcache_cache_only(cs35l41->regmap, false);

		cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
		cs35l41_shutdown_dsp(cs35l41);
		cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);

		regcache_cache_only(cs35l41->regmap, true);
		regcache_mark_dirty(cs35l41->regmap);
	}
	mutex_unlock(&cs35l41->fw_mutex);
}

static int cs35l41_system_suspend(struct device *dev)
{
	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
	int ret;

	dev_dbg(cs35l41->dev, "Suspend\n");
	dev_dbg(cs35l41->dev, "System Suspend\n");

	if (!cs35l41->firmware_running)
	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
		dev_err(cs35l41->dev, "System Suspend not supported\n");
		return -EINVAL;
	}

	ret = pm_runtime_force_suspend(dev);
	if (ret)
		return ret;

	/* Shutdown DSP before system suspend */
	cs35l41_ready_for_reset(cs35l41);

	/*
	 * Reset GPIO may be shared, so cannot reset here.
	 * However beyond this point, amps may be powered down.
	 */
	return 0;
}

static int cs35l41_system_resume(struct device *dev)
{
	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
	int ret;

	dev_dbg(cs35l41->dev, "System Resume\n");

	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
		dev_err(cs35l41->dev, "System Resume not supported\n");
		return -EINVAL;
	}

	if (cs35l41->reset_gpio) {
		usleep_range(2000, 2100);
		gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
	}

	usleep_range(2000, 2100);

	ret = pm_runtime_force_resume(dev);

	mutex_lock(&cs35l41->fw_mutex);
	if (!ret && cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) {
		cs35l41->fw_request_ongoing = true;
		schedule_work(&cs35l41->fw_load_work);
	}
	mutex_unlock(&cs35l41->fw_mutex);

	return ret;
}

	if (cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type) < 0)
static int cs35l41_runtime_suspend(struct device *dev)
{
	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
	int ret = 0;

	dev_dbg(cs35l41->dev, "Runtime Suspend\n");

	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
		dev_dbg(cs35l41->dev, "Runtime Suspend not supported\n");
		return 0;
	}

	mutex_lock(&cs35l41->fw_mutex);

	if (cs35l41->playback_started) {
		regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute,
				       ARRAY_SIZE(cs35l41_hda_mute));
		cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0);
		regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
				   CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
		if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
			regmap_write(cs35l41->regmap, CS35L41_GPIO1_CTRL1, 0x00000001);
		regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
				   CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
				   0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT);
		cs35l41->playback_started = false;
	}

	if (cs35l41->firmware_running) {
		ret = cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap,
					      cs35l41->hw_cfg.bst_type);
		if (ret)
			goto err;
	} else {
		cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
	}

	regcache_cache_only(cs35l41->regmap, true);
	regcache_mark_dirty(cs35l41->regmap);

	return 0;
err:
	mutex_unlock(&cs35l41->fw_mutex);

	return ret;
}

static int cs35l41_runtime_resume(struct device *dev)
{
	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
	int ret;
	int ret = 0;

	dev_dbg(cs35l41->dev, "Resume.\n");
	dev_dbg(cs35l41->dev, "Runtime Resume\n");

	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
		dev_dbg(cs35l41->dev, "System does not support Resume\n");
		dev_dbg(cs35l41->dev, "Runtime Resume not supported\n");
		return 0;
	}

	if (!cs35l41->firmware_running)
		return 0;
	mutex_lock(&cs35l41->fw_mutex);

	regcache_cache_only(cs35l41->regmap, false);

	if (cs35l41->firmware_running)	{
		ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
		if (ret) {
		regcache_cache_only(cs35l41->regmap, true);
		return ret;
			dev_warn(cs35l41->dev, "Unable to exit Hibernate.");
			goto err;
		}
	}

	/* Test key needs to be unlocked to allow the OTP settings to re-apply */
@@ -609,26 +723,16 @@ static int cs35l41_runtime_resume(struct device *dev)
	cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
	if (ret) {
		dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
		return ret;
		goto err;
	}

	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
		cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);

	return 0;
}

static int cs35l41_hda_suspend_hook(struct device *dev)
{
	dev_dbg(dev, "Request Suspend\n");
	pm_runtime_mark_last_busy(dev);
	return pm_runtime_put_autosuspend(dev);
}
err:
	mutex_unlock(&cs35l41->fw_mutex);

static int cs35l41_hda_resume_hook(struct device *dev)
{
	dev_dbg(dev, "Request Resume\n");
	return pm_runtime_get_sync(dev);
	return ret;
}

static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
@@ -678,8 +782,6 @@ static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)

static void cs35l41_load_firmware(struct cs35l41_hda *cs35l41, bool load)
{
	pm_runtime_get_sync(cs35l41->dev);

	if (cs35l41->firmware_running && !load) {
		dev_dbg(cs35l41->dev, "Unloading Firmware\n");
		cs35l41_shutdown_dsp(cs35l41);
@@ -689,9 +791,6 @@ static void cs35l41_load_firmware(struct cs35l41_hda *cs35l41, bool load)
	} else {
		dev_dbg(cs35l41->dev, "Unable to Load firmware.\n");
	}

	pm_runtime_mark_last_busy(cs35l41->dev);
	pm_runtime_put_autosuspend(cs35l41->dev);
}

static int cs35l41_fw_load_ctl_get(struct snd_kcontrol *kcontrol,
@@ -707,16 +806,21 @@ static void cs35l41_fw_load_work(struct work_struct *work)
{
	struct cs35l41_hda *cs35l41 = container_of(work, struct cs35l41_hda, fw_load_work);

	pm_runtime_get_sync(cs35l41->dev);

	mutex_lock(&cs35l41->fw_mutex);

	/* Recheck if playback is ongoing, mutex will block playback during firmware loading */
	if (cs35l41->playback_started)
		dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback\n");
		dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback. Retrying...\n");
	else
		cs35l41_load_firmware(cs35l41, cs35l41->request_fw_load);

	cs35l41->fw_request_ongoing = false;
	mutex_unlock(&cs35l41->fw_mutex);

	pm_runtime_mark_last_busy(cs35l41->dev);
	pm_runtime_put_autosuspend(cs35l41->dev);
}

static int cs35l41_fw_load_ctl_put(struct snd_kcontrol *kcontrol,
@@ -840,6 +944,8 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas

	pm_runtime_get_sync(dev);

	mutex_lock(&cs35l41->fw_mutex);

	comps->dev = dev;
	if (!cs35l41->acpi_subsystem_id)
		cs35l41->acpi_subsystem_id = kasprintf(GFP_KERNEL, "%.8x",
@@ -852,10 +958,8 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
	if (firmware_autostart) {
		dev_dbg(cs35l41->dev, "Firmware Autostart.\n");
		cs35l41->request_fw_load = true;
		mutex_lock(&cs35l41->fw_mutex);
		if (cs35l41_smart_amp(cs35l41) < 0)
			dev_warn(cs35l41->dev, "Cannot Run Firmware, reverting to dsp bypass...\n");
		mutex_unlock(&cs35l41->fw_mutex);
	} else {
		dev_dbg(cs35l41->dev, "Firmware Autostart is disabled.\n");
	}
@@ -863,8 +967,8 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas
	ret = cs35l41_create_controls(cs35l41);

	comps->playback_hook = cs35l41_hda_playback_hook;
	comps->suspend_hook = cs35l41_hda_suspend_hook;
	comps->resume_hook = cs35l41_hda_resume_hook;

	mutex_unlock(&cs35l41->fw_mutex);

	pm_runtime_mark_last_busy(dev);
	pm_runtime_put_autosuspend(dev);
@@ -1433,6 +1537,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, SND_HDA_SCODEC_CS35L41);

const struct dev_pm_ops cs35l41_hda_pm_ops = {
	RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL)
	SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume)
};
EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41);

+0 −2
Original line number Diff line number Diff line
@@ -16,6 +16,4 @@ struct hda_component {
	char name[HDA_MAX_NAME_SIZE];
	struct hda_codec *codec;
	void (*playback_hook)(struct device *dev, int action);
	int (*suspend_hook)(struct device *dev);
	int (*resume_hook)(struct device *dev);
};
Loading