Unverified Commit 679de7b6 authored by Jernej Skrabec's avatar Jernej Skrabec Committed by Mark Brown
Browse files

ASoC: sunxi: sun4i-spdif: Implement IEC958 control



SPDIF core is capable of sending custom status.

Implement IEC958 control handling.

Signed-off-by: default avatarJernej Skrabec <jernej.skrabec@gmail.com>
Link: https://lore.kernel.org/r/20211117194458.2249643-1-jernej.skrabec@gmail.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 425c5fce
Loading
Loading
Loading
Loading
+115 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/spinlock.h>
#include <sound/asoundef.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -186,6 +188,7 @@ struct sun4i_spdif_dev {
	struct regmap *regmap;
	struct snd_dmaengine_dai_dma_data dma_params_tx;
	const struct sun4i_spdif_quirks *quirks;
	spinlock_t lock;
};

static void sun4i_spdif_configure(struct sun4i_spdif_dev *host)
@@ -385,11 +388,122 @@ static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
	return ret;
}

static int sun4i_spdif_info(struct snd_kcontrol *kcontrol,
			    struct snd_ctl_elem_info *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
	uinfo->count = 1;

	return 0;
}

static int sun4i_spdif_get_status_mask(struct snd_kcontrol *kcontrol,
				       struct snd_ctl_elem_value *ucontrol)
{
	u8 *status = ucontrol->value.iec958.status;

	status[0] = 0xff;
	status[1] = 0xff;
	status[2] = 0xff;
	status[3] = 0xff;
	status[4] = 0xff;
	status[5] = 0x03;

	return 0;
}

static int sun4i_spdif_get_status(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
	struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
	u8 *status = ucontrol->value.iec958.status;
	unsigned long flags;
	unsigned int reg;

	spin_lock_irqsave(&host->lock, flags);

	regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA0, &reg);

	status[0] = reg & 0xff;
	status[1] = (reg >> 8) & 0xff;
	status[2] = (reg >> 16) & 0xff;
	status[3] = (reg >> 24) & 0xff;

	regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA1, &reg);

	status[4] = reg & 0xff;
	status[5] = (reg >> 8) & 0x3;

	spin_unlock_irqrestore(&host->lock, flags);

	return 0;
}

static int sun4i_spdif_set_status(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
	struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
	u8 *status = ucontrol->value.iec958.status;
	unsigned long flags;
	unsigned int reg;
	bool chg0, chg1;

	spin_lock_irqsave(&host->lock, flags);

	reg = (u32)status[3] << 24;
	reg |= (u32)status[2] << 16;
	reg |= (u32)status[1] << 8;
	reg |= (u32)status[0];

	regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA0,
				 GENMASK(31,0), reg, &chg0);

	reg = (u32)status[5] << 8;
	reg |= (u32)status[4];

	regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA1,
				 GENMASK(9,0), reg, &chg1);

	reg = SUN4I_SPDIF_TXCFG_CHSTMODE;
	if (status[0] & IEC958_AES0_NONAUDIO)
		reg |= SUN4I_SPDIF_TXCFG_NONAUDIO;

	regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
			   SUN4I_SPDIF_TXCFG_CHSTMODE |
			   SUN4I_SPDIF_TXCFG_NONAUDIO, reg);

	spin_unlock_irqrestore(&host->lock, flags);

	return chg0 || chg1;
}

static struct snd_kcontrol_new sun4i_spdif_controls[] = {
	{
		.access = SNDRV_CTL_ELEM_ACCESS_READ,
		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
		.info = sun4i_spdif_info,
		.get = sun4i_spdif_get_status_mask
	},
	{
		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
		.info = sun4i_spdif_info,
		.get = sun4i_spdif_get_status,
		.put = sun4i_spdif_set_status
	}
};

static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai)
{
	struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai);

	snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL);
	snd_soc_add_dai_controls(dai, sun4i_spdif_controls,
				 ARRAY_SIZE(sun4i_spdif_controls));

	return 0;
}

@@ -512,6 +626,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
		return -ENOMEM;

	host->pdev = pdev;
	spin_lock_init(&host->lock);

	/* Initialize this copy of the CPU DAI driver structure */
	memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai));