Re: [RFC PATCH v2 5/5] ASoC: apple: Add macaudio machine driver

From: Martin Povišer
Date: Mon Jun 06 2022 - 17:33:37 EST



> On 6. 6. 2022, at 23:22, Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxxxxxxxx> wrote:
>
> On 6/6/22 15:46, Martin Povišer wrote:
>> (I am having trouble delivering mail to linux.intel.com, so I reply to the list
>> and CC at least...)
>>
>>> On 6. 6. 2022, at 22:02, Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxxxxxxxx> wrote:
>>>
>>>
>>>> + * Virtual FE/BE Playback Topology
>>>> + * -------------------------------
>>>> + *
>>>> + * The platform driver has independent frontend and backend DAIs with the
>>>> + * option of routing backends to any of the frontends. The platform
>>>> + * driver configures the routing based on DPCM couplings in ASoC runtime
>>>> + * structures, which in turn is determined from DAPM paths by ASoC. But the
>>>> + * platform driver doesn't supply relevant DAPM paths and leaves that up for
>>>> + * the machine driver to fill in. The filled-in virtual topology can be
>>>> + * anything as long as a particular backend isn't connected to more than one
>>>> + * frontend at any given time. (The limitation is due to the unsupported case
>>>> + * of reparenting of live BEs.)
>>>> + *
>>>> + * The DAPM routing that this machine-level driver makes up has two use-cases
>>>> + * in mind:
>>>> + *
>>>> + * - Using a single PCM for playback such that it conditionally sinks to either
>>>> + * speakers or headphones based on the plug-in state of the headphones jack.
>>>> + * All the while making the switch transparent to userspace. This has the
>>>> + * drawback of requiring a sample stream suited for both speakers and
>>>> + * headphones, which is hard to come by on machines where tailored DSP for
>>>> + * speakers in userspace is desirable or required.
>>>> + *
>>>> + * - Driving the headphones and speakers from distinct PCMs, having userspace
>>>> + * bridge the difference and apply different signal processing to the two.
>>>> + *
>>>> + * In the end the topology supplied by this driver looks like this:
>>>> + *
>>>> + * PCMs (frontends) I2S Port Groups (backends)
>>>> + * ──────────────── ──────────────────────────
>>>> + *
>>>> + * ┌──────────┐ ┌───────────────► ┌─────┐ ┌──────────┐
>>>> + * │ Primary ├───────┤ │ Mux │ ──► │ Speakers │
>>>> + * └──────────┘ │ ┌──────────► └─────┘ └──────────┘
>>>> + * ┌─── │ ───┘ ▲
>>>> + * ┌──────────┐ │ │ │
>>>> + * │Secondary ├──┘ │ ┌────────────┴┐
>>>> + * └──────────┘ ├────►│Plug-in Demux│
>>>> + * │ └────────────┬┘
>>>> + * │ │
>>>> + * │ ▼
>>>> + * │ ┌─────┐ ┌──────────┐
>>>> + * └───────────────► │ Mux │ ──► │Headphones│
>>>> + * └─────┘ └──────────┘
>>>> + */
>>>
>>> In Patch2, the 'clusters' are described as front-ends, with I2S as
>>> back-ends. Here the PCMs are described as front-ends, but there's no
>>> mention of clusters. Either one of the two descriptions is outdated, or
>>> there's something missing to help reconcile the two pieces of information?
>>
>> Both descriptions should be in sync. Maybe I don’t know the proper
>> terminology. In both cases the frontend is meant to be the actual I2S
>> transceiver unit, and backend the I2S port on the SoC’s periphery,
>> which can be routed to any of transceiver units. (Multiple ports can
>> be routed to the same unit, which means the ports will have the same
>> clocks and data line -- that's a configuration we need to support to
>> drive some of the speaker arrays, hence the backend/frontend
>> distinction).
>>
>> Maybe I am using 'PCM' in a confusing way here? What I meant is a
>> subdevice that’s visible from userspace, because I have seen it used
>> that way in ALSA codebase.
>
> Yes, I think most people familiar with DPCM would take the 'PCM
> frontend' as some sort of generic DMA transfer from system memory, while
> the 'back end' is more the actual serial link. In your case, the
> front-end is already very low-level and tied to I2S already. I think
> that's fine, it's just that using different terms for 'cluster' and
> 'PCM' in different patches could lead to confusions.

OK, will take this into account then.

>>>> +static int macaudio_get_runtime_mclk_fs(struct snd_pcm_substream *substream)
>>>> +{
>>>> + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
>>>> + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(rtd->card);
>>>> + struct snd_soc_dpcm *dpcm;
>>>> +
>>>> + /*
>>>> + * If this is a FE, look it up in link_props directly.
>>>> + * If this is a BE, look it up in the respective FE.
>>>> + */
>>>> + if (!rtd->dai_link->no_pcm)
>>>> + return ma->link_props[rtd->dai_link->id].mclk_fs;
>>>> +
>>>> + for_each_dpcm_fe(rtd, substream->stream, dpcm) {
>>>> + int fe_id = dpcm->fe->dai_link->id;
>>>> +
>>>> + return ma->link_props[fe_id].mclk_fs;
>>>> + }
>>>
>>> I am not sure what the concept of mclk would mean for a front-end? This
>>> is typically very I2S-specific, i.e. a back-end property, no?
>>
>> Right, that’s a result of the confusion from above. Hope I cleared it up
>> somehow. The frontend already decides the clocks and data serialization,
>> hence mclk/fs is a frontend-prop here.
>
> What confuses me in this code is that it looks possible that the front-
> and back-end could have different mclk values? I think a comment is
> missing that the values are identical, it's just that there's a
> different way to access it depending on the link type?

Well, that’s exactly what I am trying to convey by the comment above
in macaudio_get_runtime_mclk_fs. Maybe I should do something to make
it more obvious.

>>>> +static int macaudio_be_init(struct snd_soc_pcm_runtime *rtd)
>>>> +{
>>>> + struct snd_soc_card *card = rtd->card;
>>>> + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
>>>> + struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
>>>> + struct snd_soc_dai *dai;
>>>> + int i, ret;
>>>> +
>>>> + ret = macaudio_be_assign_tdm(rtd);
>>>> + if (ret < 0)
>>>> + return ret;
>>>> +
>>>> + if (props->is_headphones) {
>>>> + for_each_rtd_codec_dais(rtd, i, dai)
>>>> + snd_soc_component_set_jack(dai->component, &ma->jack, NULL);
>>>> + }
>>>
>>> this is weird, set_jack() is invoked by the ASoC core. You shouldn't
>>> need to do this?
>>
>> That’s interesting. Where would it be invoked? How does ASoC know which codec
>> it attaches to?
>
> sorry, my comment was partly invalid.
>
> set_jack() is invoked in the machine driver indeed, what I found strange
> is that you may have different codecs handling the jack? What is the
> purpose of that loop?

There’s no need for the loop, there’s a single codec. OK, will remove the loop
to make it less confusing.

>>>> +static int macaudio_jack_event(struct notifier_block *nb, unsigned long event,
>>>> + void *data);
>>>> +
>>>> +static struct notifier_block macaudio_jack_nb = {
>>>> + .notifier_call = macaudio_jack_event,
>>>> +};
>>>
>>> why is this needed? we have already many ways of dealing with the jack
>>> events (dare I say too many ways?).
>>
>> Because I want to update the DAPM paths based on the jack status,
>> specifically I want to set macaudio_plugin_demux. I don’t know how
>> else it could be done.
>
> I don't know either but I have never seen notifier blocks being used. I
> would think there are already ways to do this with DAPM events.
>
>
>>>> +static int macaudio_jack_event(struct notifier_block *nb, unsigned long event,
>>>> + void *data)
>>>> +{
>>>> + struct snd_soc_jack *jack = data;
>>>> + struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(jack->card);
>>>> +
>>>> + ma->jack_plugin_state = !!event;
>>>> +
>>>> + if (!ma->plugin_demux_kcontrol)
>>>> + return 0;
>>>> +
>>>> + snd_soc_dapm_mux_update_power(&ma->card.dapm, ma->plugin_demux_kcontrol,
>>>> + ma->jack_plugin_state,
>>>> + (struct soc_enum *) &macaudio_plugin_demux_enum, NULL);
>>>
>>> the term 'plugin' can be understood in many ways by different audio
>>> folks. 'plugin' is usually the term used for processing libraries (VST,
>>> LADSPA, etc). I think here you meant 'jack control'?
>>
>> So ‘jack control’ would be understood as the jack plugged/unplugged status?
>
> The 'Headphone Jack' or 'Headset Mic Jack' kcontrols typically track the
> status. Other terms are 'jack detection'. "plugin" is not a very common
> term here.

OK