static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_multi_out *mout = (void *)kcontrol->private_value;
+
+ guard(mutex)(&codec->spdif_mutex);
ucontrol->value.integer.value[0] = mout->share_spdif;
return 0;
}
static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
- mout->share_spdif = !!ucontrol->value.integer.value[0];
- return 0;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_multi_out *mout = (void *)kcontrol->private_value;
+ bool val = !!ucontrol->value.integer.value[0];
+ int change;
+
+ guard(mutex)(&codec->spdif_mutex);
+ change = mout->share_spdif != val;
+ mout->share_spdif = val;
+ return change;
}
static const struct snd_kcontrol_new spdif_share_sw = {
.put = spdif_share_sw_put,
};
+static void notify_spdif_share_sw(struct hda_codec *codec,
+ struct hda_multi_out *mout)
+{
+ if (mout->share_spdif_kctl)
+ snd_ctl_notify_one(codec->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ mout->share_spdif_kctl, 0);
+}
+
/**
* snd_hda_create_spdif_share_sw - create Default PCM switch
* @codec: the HDA codec
struct hda_multi_out *mout)
{
struct snd_kcontrol *kctl;
+ int err;
if (!mout->dig_out_nid)
return 0;
- kctl = snd_ctl_new1(&spdif_share_sw, mout);
+ kctl = snd_ctl_new1(&spdif_share_sw, codec);
if (!kctl)
return -ENOMEM;
- /* ATTENTION: here mout is passed as private_data, instead of codec */
- return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl);
+ /* snd_ctl_new1() stores @codec in private_data; stash @mout in
+ * private_value for the share-switch callbacks and cache the
+ * assigned control for forced-disable notifications.
+ */
+ kctl->private_value = (unsigned long)mout;
+ err = snd_hda_ctl_add(codec, mout->dig_out_nid, kctl);
+ if (err < 0)
+ return err;
+ mout->share_spdif_kctl = kctl;
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw);
struct hda_pcm_stream *hinfo)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ bool notify_share_sw = false;
+
runtime->hw.channels_max = mout->max_channels;
if (mout->dig_out_nid) {
if (!mout->analog_rates) {
hinfo->maxbps = mout->spdif_maxbps;
} else {
mout->share_spdif = 0;
- /* FIXME: need notify? */
+ notify_share_sw = true;
}
}
}
+ if (notify_share_sw)
+ notify_spdif_share_sw(codec, mout);
return snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
}