Update Linux to v5.10.109

Sourced from [1]

[1] https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.109.tar.xz

Change-Id: I19bca9fc6762d4e63bcf3e4cba88bbe560d9c76c
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/sound/soc/meson/aiu-encoder-spdif.c b/sound/soc/meson/aiu-encoder-spdif.c
new file mode 100644
index 0000000..de85091
--- /dev/null
+++ b/sound/soc/meson/aiu-encoder-spdif.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2020 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm_iec958.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "aiu.h"
+
+#define AIU_958_MISC_NON_PCM		BIT(0)
+#define AIU_958_MISC_MODE_16BITS	BIT(1)
+#define AIU_958_MISC_16BITS_ALIGN	GENMASK(6, 5)
+#define AIU_958_MISC_MODE_32BITS	BIT(7)
+#define AIU_958_MISC_U_FROM_STREAM	BIT(12)
+#define AIU_958_MISC_FORCE_LR		BIT(13)
+#define AIU_958_CTRL_HOLD_EN		BIT(0)
+#define AIU_CLK_CTRL_958_DIV_EN		BIT(1)
+#define AIU_CLK_CTRL_958_DIV		GENMASK(5, 4)
+#define AIU_CLK_CTRL_958_DIV_MORE	BIT(12)
+
+#define AIU_CS_WORD_LEN			4
+#define AIU_958_INTERNAL_DIV		2
+
+static void
+aiu_encoder_spdif_divider_enable(struct snd_soc_component *component,
+				 bool enable)
+{
+	snd_soc_component_update_bits(component, AIU_CLK_CTRL,
+				      AIU_CLK_CTRL_958_DIV_EN,
+				      enable ? AIU_CLK_CTRL_958_DIV_EN : 0);
+}
+
+static void aiu_encoder_spdif_hold(struct snd_soc_component *component,
+				   bool enable)
+{
+	snd_soc_component_update_bits(component, AIU_958_CTRL,
+				      AIU_958_CTRL_HOLD_EN,
+				      enable ? AIU_958_CTRL_HOLD_EN : 0);
+}
+
+static int
+aiu_encoder_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+			  struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		aiu_encoder_spdif_hold(component, false);
+		return 0;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		aiu_encoder_spdif_hold(component, true);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int aiu_encoder_spdif_setup_cs_word(struct snd_soc_component *component,
+					   struct snd_pcm_hw_params *params)
+{
+	u8 cs[AIU_CS_WORD_LEN];
+	unsigned int val;
+	int ret;
+
+	ret = snd_pcm_create_iec958_consumer_hw_params(params, cs,
+						       AIU_CS_WORD_LEN);
+	if (ret < 0)
+		return ret;
+
+	/* Write the 1st half word */
+	val = cs[1] | cs[0] << 8;
+	snd_soc_component_write(component, AIU_958_CHSTAT_L0, val);
+	snd_soc_component_write(component, AIU_958_CHSTAT_R0, val);
+
+	/* Write the 2nd half word */
+	val = cs[3] | cs[2] << 8;
+	snd_soc_component_write(component, AIU_958_CHSTAT_L1, val);
+	snd_soc_component_write(component, AIU_958_CHSTAT_R1, val);
+
+	return 0;
+}
+
+static int aiu_encoder_spdif_hw_params(struct snd_pcm_substream *substream,
+				       struct snd_pcm_hw_params *params,
+				       struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct aiu *aiu = snd_soc_component_get_drvdata(component);
+	unsigned int val = 0, mrate;
+	int ret;
+
+	/* Disable the clock while changing the settings */
+	aiu_encoder_spdif_divider_enable(component, false);
+
+	switch (params_physical_width(params)) {
+	case 16:
+		val |= AIU_958_MISC_MODE_16BITS;
+		val |= FIELD_PREP(AIU_958_MISC_16BITS_ALIGN, 2);
+		break;
+	case 32:
+		val |= AIU_958_MISC_MODE_32BITS;
+		break;
+	default:
+		dev_err(dai->dev, "Unsupport physical width\n");
+		return -EINVAL;
+	}
+
+	snd_soc_component_update_bits(component, AIU_958_MISC,
+				      AIU_958_MISC_NON_PCM |
+				      AIU_958_MISC_MODE_16BITS |
+				      AIU_958_MISC_16BITS_ALIGN |
+				      AIU_958_MISC_MODE_32BITS |
+				      AIU_958_MISC_FORCE_LR |
+				      AIU_958_MISC_U_FROM_STREAM,
+				      val);
+
+	/* Set the stream channel status word */
+	ret = aiu_encoder_spdif_setup_cs_word(component, params);
+	if (ret) {
+		dev_err(dai->dev, "failed to set channel status word\n");
+		return ret;
+	}
+
+	snd_soc_component_update_bits(component, AIU_CLK_CTRL,
+				      AIU_CLK_CTRL_958_DIV |
+				      AIU_CLK_CTRL_958_DIV_MORE,
+				      FIELD_PREP(AIU_CLK_CTRL_958_DIV,
+						 __ffs(AIU_958_INTERNAL_DIV)));
+
+	/* 2 * 32bits per subframe * 2 channels = 128 */
+	mrate = params_rate(params) * 128 * AIU_958_INTERNAL_DIV;
+	ret = clk_set_rate(aiu->spdif.clks[MCLK].clk, mrate);
+	if (ret) {
+		dev_err(dai->dev, "failed to set mclk rate\n");
+		return ret;
+	}
+
+	aiu_encoder_spdif_divider_enable(component, true);
+
+	return 0;
+}
+
+static int aiu_encoder_spdif_hw_free(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+
+	aiu_encoder_spdif_divider_enable(component, false);
+
+	return 0;
+}
+
+static int aiu_encoder_spdif_startup(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
+{
+	struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
+	int ret;
+
+	/*
+	 * NOTE: Make sure the spdif block is on its own divider.
+	 *
+	 * The spdif can be clocked by the i2s master clock or its own
+	 * clock. We should (in theory) change the source depending on the
+	 * origin of the data.
+	 *
+	 * However, considering the clocking scheme used on these platforms,
+	 * the master clocks will pick the same PLL source when they are
+	 * playing from the same FIFO. The clock should be in sync so, it
+	 * should not be necessary to reparent the spdif master clock.
+	 */
+	ret = clk_set_parent(aiu->spdif.clks[MCLK].clk,
+			     aiu->spdif_mclk);
+	if (ret)
+		return ret;
+
+	ret = clk_bulk_prepare_enable(aiu->spdif.clk_num, aiu->spdif.clks);
+	if (ret)
+		dev_err(dai->dev, "failed to enable spdif clocks\n");
+
+	return ret;
+}
+
+static void aiu_encoder_spdif_shutdown(struct snd_pcm_substream *substream,
+				       struct snd_soc_dai *dai)
+{
+	struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
+
+	clk_bulk_disable_unprepare(aiu->spdif.clk_num, aiu->spdif.clks);
+}
+
+const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops = {
+	.trigger	= aiu_encoder_spdif_trigger,
+	.hw_params	= aiu_encoder_spdif_hw_params,
+	.hw_free	= aiu_encoder_spdif_hw_free,
+	.startup	= aiu_encoder_spdif_startup,
+	.shutdown	= aiu_encoder_spdif_shutdown,
+};