[PATCH v2 09/21] ASoC: apple: Add macaudio machine driver
Cezary Rojewski
cezary.rojewski at intel.com
Fri Oct 9 04:52:09 PDT 2026
On 10/4/2026 8:03 AM, James Calligeros wrote:
> From: Martin Povišer <povik+lin at cutebit.org>
>
> Apple Silicon Macs have a complex audio subsystem consisting
> of an I2S peripheral (MCA) and multiple codecs of various
> models and capabilities. Some machines have a basic mono
> speaker with hardware downmix, while others have a very
> intricate stereo system consisting of multiple codecs and
> drivers per L/R channel. Some machines report voice coil
> voltage and current information back to the SoC, and others
> do not. All machines have a headset jack.
>
> Add an ASoC machine driver for this platform.
> +struct ma_codec_idle {
> + int idle_mode;
> + u32 idle_mask;
> +};
> +
> +struct macaudio_snd_data {
> + struct snd_soc_card card;
> + struct snd_soc_jack jack;
Hi,
Typically storing both card and jack heralds that the design is off.
Especially when stored as fields. Looks to me as if you've made a one
structure to catch them all.
Let's start with the card. Below are the users - I'll skip dev_xxx()s as
that's an easy fix:
macaudio_dpcm_hw_params()
macaudio_be_hw_free()
both can access the card from the substream/dai without
relying on the ma-context
macaudio_parse_of()
macaudio_parse_of_be_dai_link()
called in context which creates the very card
macaudio_vlimit_enable_timeout()
macaudio_vlimit_disable_timeout()
macaudio_vlimit_update()
called in context of delayed part of the trigger() callback.
I'd need more information on the trigger() implementation of
yours to present a solution. See the comments below.
Skipped macaudio_vlimit_unlock() as it's called in context of
macaudio_vlimit_update() and macaudio_late_probe(). The latter has
direct access to the card while the former is mentioned above.
> + int jack_plugin_state;
> +
> + const struct macaudio_platform_cfg *cfg;
> + bool has_speakers;
> + bool has_sense;
> + bool has_safety;
> + unsigned int max_channels;
> +
> + struct macaudio_link_props {
> + /* frontend props */
> + unsigned int bclk_ratio;
> + bool is_sense;
> +
> + /* backend props */
> + bool is_speakers;
> + bool is_headphones;
> + unsigned int tdm_mask;
> + struct ma_codec_idle *codecs;
> + } *link_props;
> +
> + int speaker_sample_rate;
> + struct snd_kcontrol *speaker_sample_rate_kctl;
> +
> + struct mutex be_link_mutex;
> +
> + struct mutex volume_lock_mutex;
> + bool speaker_volume_unlocked;
> + bool speaker_volume_was_locked;
> + struct snd_kcontrol *speaker_lock_kctl;
> + u64 bes_active;
> + bool speaker_lock_timeout_enabled;
> + ktime_t speaker_lock_timeout;
> + ktime_t speaker_lock_remain;
> + struct delayed_work lock_timeout_work;
> + struct work_struct lock_update_work;
> +
> +};
> +static int macaudio_be_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(rtd->card);
> + struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
> +
> + guard(mutex)(&ma->be_link_mutex);
As already pointed out by Ajay, there is no 'nonatomic' flag set and
without such, you will hit a problem, eventually.
> + if (props->is_speakers && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + ma->bes_active |= BIT(rtd->dai_link->id);
> + break;
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + case SNDRV_PCM_TRIGGER_STOP:
> + ma->bes_active &= ~BIT(rtd->dai_link->id);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + schedule_work(&ma->lock_update_work);
trigger() should enable DMA work and/or signal other components
participating in the streaming to start their tasks too.
Please correct me if I'm wrong but it seems the scheduled work does
speaker-volume operations only. Moreover, it seems that
->lock_update_work may schedule a follow up work in form of
->lock_timeout_work. Are you sure trigger() is the right place to do
volume-control in delayed manner?
Once we clear the mist around the main structure and the trigger()
callback, I can happily review the "easier" parts of the patch.
> + }
> +
> + return 0;
> +}
More information about the Linux-mediatek
mailing list