]> git.baikalelectronics.ru Git - kernel.git/commitdiff
ASoC: hdmi-codec: Add a prepare hook
authorMaxime Ripard <maxime@cerno.tech>
Tue, 25 May 2021 13:23:47 +0000 (15:23 +0200)
committerMaxime Ripard <maxime@cerno.tech>
Thu, 10 Jun 2021 09:48:56 +0000 (11:48 +0200)
The IEC958 status bit is usually set by the userspace after hw_params
has been called, so in order to use whatever is set by the userspace, we
need to implement the prepare hook. Let's add it to the hdmi_codec_ops,
and mandate that either prepare or hw_params is implemented.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20210525132354.297468-6-maxime@cerno.tech
include/sound/hdmi-codec.h
sound/soc/codecs/hdmi-codec.c

index 4b3a1d374b9042d3172b94e51f4bdc54b750ec74..4fc733c8c570e3fa74fb17916996dbb64341283d 100644 (file)
@@ -65,12 +65,22 @@ struct hdmi_codec_ops {
 
        /*
         * Configures HDMI-encoder for audio stream.
-        * Mandatory
+        * Having either prepare or hw_params is mandatory.
         */
        int (*hw_params)(struct device *dev, void *data,
                         struct hdmi_codec_daifmt *fmt,
                         struct hdmi_codec_params *hparms);
 
+       /*
+        * Configures HDMI-encoder for audio stream. Can be called
+        * multiple times for each setup.
+        *
+        * Having either prepare or hw_params is mandatory.
+        */
+       int (*prepare)(struct device *dev, void *data,
+                      struct hdmi_codec_daifmt *fmt,
+                      struct hdmi_codec_params *hparms);
+
        /*
         * Shuts down the audio stream.
         * Mandatory
index a0834b7848141085f5766b306a75c4701de8c5fc..a67c92032e114567629c6f72dad844eea912caba 100644 (file)
@@ -481,6 +481,42 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
        mutex_unlock(&hcp->lock);
 }
 
+static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai,
+                                       unsigned int sample_width,
+                                       unsigned int sample_rate,
+                                       unsigned int channels,
+                                       struct hdmi_codec_params *hp)
+{
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+       int idx;
+
+       /* Select a channel allocation that matches with ELD and pcm channels */
+       idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
+       if (idx < 0) {
+               dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
+                       idx);
+               hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+               return idx;
+       }
+
+       memset(hp, 0, sizeof(*hp));
+
+       hdmi_audio_infoframe_init(&hp->cea);
+       hp->cea.channels = channels;
+       hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+       hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+       hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+       hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
+
+       hp->sample_width = sample_width;
+       hp->sample_rate = sample_rate;
+       hp->channels = channels;
+
+       hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
+
+       return 0;
+}
+
 static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
@@ -495,12 +531,23 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
                        .dig_subframe = { 0 },
                }
        };
-       int ret, idx;
+       int ret;
+
+       if (!hcp->hcd.ops->hw_params)
+               return 0;
 
        dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
                params_width(params), params_rate(params),
                params_channels(params));
 
+       ret = hdmi_codec_fill_codec_params(dai,
+                                          params_width(params),
+                                          params_rate(params),
+                                          params_channels(params),
+                                          &hp);
+       if (ret < 0)
+               return ret;
+
        memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
        ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status,
                                                     sizeof(hp.iec.status));
@@ -510,32 +557,47 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
-       hdmi_audio_infoframe_init(&hp.cea);
-       hp.cea.channels = params_channels(params);
-       hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
-       hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
-       hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
-
-       /* Select a channel allocation that matches with ELD and pcm channels */
-       idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels);
-       if (idx < 0) {
-               dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
-                       idx);
-               hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
-               return idx;
-       }
-       hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
-       hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
-
-       hp.sample_width = params_width(params);
-       hp.sample_rate = params_rate(params);
-       hp.channels = params_channels(params);
-
        cf->bit_fmt = params_format(params);
        return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
                                       cf, &hp);
 }
 
+static int hdmi_codec_prepare(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+       struct hdmi_codec_daifmt *cf = dai->playback_dma_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int channels = runtime->channels;
+       unsigned int width = snd_pcm_format_width(runtime->format);
+       unsigned int rate = runtime->rate;
+       struct hdmi_codec_params hp;
+       int ret;
+
+       if (!hcp->hcd.ops->prepare)
+               return 0;
+
+       dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
+               width, rate, channels);
+
+       ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp);
+       if (ret < 0)
+               return ret;
+
+       memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
+       ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status,
+                                          sizeof(hp.iec.status));
+       if (ret < 0) {
+               dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
+                       ret);
+               return ret;
+       }
+
+       cf->bit_fmt = runtime->format;
+       return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data,
+                                    cf, &hp);
+}
+
 static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
                                  unsigned int fmt)
 {
@@ -627,6 +689,7 @@ static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
        .startup        = hdmi_codec_startup,
        .shutdown       = hdmi_codec_shutdown,
        .hw_params      = hdmi_codec_hw_params,
+       .prepare        = hdmi_codec_prepare,
        .set_fmt        = hdmi_codec_i2s_set_fmt,
        .mute_stream    = hdmi_codec_mute,
 };
@@ -917,7 +980,8 @@ static int hdmi_codec_probe(struct platform_device *pdev)
        }
 
        dai_count = hcd->i2s + hcd->spdif;
-       if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params ||
+       if (dai_count < 1 || !hcd->ops ||
+           (!hcd->ops->hw_params && !hcd->ops->prepare) ||
            !hcd->ops->audio_shutdown) {
                dev_err(dev, "%s: Invalid parameters\n", __func__);
                return -EINVAL;