cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

aiu-encoder-spdif.c (5857B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// Copyright (c) 2020 BayLibre, SAS.
      4// Author: Jerome Brunet <jbrunet@baylibre.com>
      5
      6#include <linux/bitfield.h>
      7#include <linux/clk.h>
      8#include <sound/pcm_params.h>
      9#include <sound/pcm_iec958.h>
     10#include <sound/soc.h>
     11#include <sound/soc-dai.h>
     12
     13#include "aiu.h"
     14
     15#define AIU_958_MISC_NON_PCM		BIT(0)
     16#define AIU_958_MISC_MODE_16BITS	BIT(1)
     17#define AIU_958_MISC_16BITS_ALIGN	GENMASK(6, 5)
     18#define AIU_958_MISC_MODE_32BITS	BIT(7)
     19#define AIU_958_MISC_U_FROM_STREAM	BIT(12)
     20#define AIU_958_MISC_FORCE_LR		BIT(13)
     21#define AIU_958_CTRL_HOLD_EN		BIT(0)
     22#define AIU_CLK_CTRL_958_DIV_EN		BIT(1)
     23#define AIU_CLK_CTRL_958_DIV		GENMASK(5, 4)
     24#define AIU_CLK_CTRL_958_DIV_MORE	BIT(12)
     25
     26#define AIU_CS_WORD_LEN			4
     27#define AIU_958_INTERNAL_DIV		2
     28
     29static void
     30aiu_encoder_spdif_divider_enable(struct snd_soc_component *component,
     31				 bool enable)
     32{
     33	snd_soc_component_update_bits(component, AIU_CLK_CTRL,
     34				      AIU_CLK_CTRL_958_DIV_EN,
     35				      enable ? AIU_CLK_CTRL_958_DIV_EN : 0);
     36}
     37
     38static void aiu_encoder_spdif_hold(struct snd_soc_component *component,
     39				   bool enable)
     40{
     41	snd_soc_component_update_bits(component, AIU_958_CTRL,
     42				      AIU_958_CTRL_HOLD_EN,
     43				      enable ? AIU_958_CTRL_HOLD_EN : 0);
     44}
     45
     46static int
     47aiu_encoder_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
     48			  struct snd_soc_dai *dai)
     49{
     50	struct snd_soc_component *component = dai->component;
     51
     52	switch (cmd) {
     53	case SNDRV_PCM_TRIGGER_START:
     54	case SNDRV_PCM_TRIGGER_RESUME:
     55	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
     56		aiu_encoder_spdif_hold(component, false);
     57		return 0;
     58
     59	case SNDRV_PCM_TRIGGER_STOP:
     60	case SNDRV_PCM_TRIGGER_SUSPEND:
     61	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
     62		aiu_encoder_spdif_hold(component, true);
     63		return 0;
     64
     65	default:
     66		return -EINVAL;
     67	}
     68}
     69
     70static int aiu_encoder_spdif_setup_cs_word(struct snd_soc_component *component,
     71					   struct snd_pcm_hw_params *params)
     72{
     73	u8 cs[AIU_CS_WORD_LEN];
     74	unsigned int val;
     75	int ret;
     76
     77	ret = snd_pcm_create_iec958_consumer_hw_params(params, cs,
     78						       AIU_CS_WORD_LEN);
     79	if (ret < 0)
     80		return ret;
     81
     82	/* Write the 1st half word */
     83	val = cs[1] | cs[0] << 8;
     84	snd_soc_component_write(component, AIU_958_CHSTAT_L0, val);
     85	snd_soc_component_write(component, AIU_958_CHSTAT_R0, val);
     86
     87	/* Write the 2nd half word */
     88	val = cs[3] | cs[2] << 8;
     89	snd_soc_component_write(component, AIU_958_CHSTAT_L1, val);
     90	snd_soc_component_write(component, AIU_958_CHSTAT_R1, val);
     91
     92	return 0;
     93}
     94
     95static int aiu_encoder_spdif_hw_params(struct snd_pcm_substream *substream,
     96				       struct snd_pcm_hw_params *params,
     97				       struct snd_soc_dai *dai)
     98{
     99	struct snd_soc_component *component = dai->component;
    100	struct aiu *aiu = snd_soc_component_get_drvdata(component);
    101	unsigned int val = 0, mrate;
    102	int ret;
    103
    104	/* Disable the clock while changing the settings */
    105	aiu_encoder_spdif_divider_enable(component, false);
    106
    107	switch (params_physical_width(params)) {
    108	case 16:
    109		val |= AIU_958_MISC_MODE_16BITS;
    110		val |= FIELD_PREP(AIU_958_MISC_16BITS_ALIGN, 2);
    111		break;
    112	case 32:
    113		val |= AIU_958_MISC_MODE_32BITS;
    114		break;
    115	default:
    116		dev_err(dai->dev, "Unsupported physical width\n");
    117		return -EINVAL;
    118	}
    119
    120	snd_soc_component_update_bits(component, AIU_958_MISC,
    121				      AIU_958_MISC_NON_PCM |
    122				      AIU_958_MISC_MODE_16BITS |
    123				      AIU_958_MISC_16BITS_ALIGN |
    124				      AIU_958_MISC_MODE_32BITS |
    125				      AIU_958_MISC_FORCE_LR |
    126				      AIU_958_MISC_U_FROM_STREAM,
    127				      val);
    128
    129	/* Set the stream channel status word */
    130	ret = aiu_encoder_spdif_setup_cs_word(component, params);
    131	if (ret) {
    132		dev_err(dai->dev, "failed to set channel status word\n");
    133		return ret;
    134	}
    135
    136	snd_soc_component_update_bits(component, AIU_CLK_CTRL,
    137				      AIU_CLK_CTRL_958_DIV |
    138				      AIU_CLK_CTRL_958_DIV_MORE,
    139				      FIELD_PREP(AIU_CLK_CTRL_958_DIV,
    140						 __ffs(AIU_958_INTERNAL_DIV)));
    141
    142	/* 2 * 32bits per subframe * 2 channels = 128 */
    143	mrate = params_rate(params) * 128 * AIU_958_INTERNAL_DIV;
    144	ret = clk_set_rate(aiu->spdif.clks[MCLK].clk, mrate);
    145	if (ret) {
    146		dev_err(dai->dev, "failed to set mclk rate\n");
    147		return ret;
    148	}
    149
    150	aiu_encoder_spdif_divider_enable(component, true);
    151
    152	return 0;
    153}
    154
    155static int aiu_encoder_spdif_hw_free(struct snd_pcm_substream *substream,
    156				     struct snd_soc_dai *dai)
    157{
    158	struct snd_soc_component *component = dai->component;
    159
    160	aiu_encoder_spdif_divider_enable(component, false);
    161
    162	return 0;
    163}
    164
    165static int aiu_encoder_spdif_startup(struct snd_pcm_substream *substream,
    166				     struct snd_soc_dai *dai)
    167{
    168	struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
    169	int ret;
    170
    171	/*
    172	 * NOTE: Make sure the spdif block is on its own divider.
    173	 *
    174	 * The spdif can be clocked by the i2s master clock or its own
    175	 * clock. We should (in theory) change the source depending on the
    176	 * origin of the data.
    177	 *
    178	 * However, considering the clocking scheme used on these platforms,
    179	 * the master clocks will pick the same PLL source when they are
    180	 * playing from the same FIFO. The clock should be in sync so, it
    181	 * should not be necessary to reparent the spdif master clock.
    182	 */
    183	ret = clk_set_parent(aiu->spdif.clks[MCLK].clk,
    184			     aiu->spdif_mclk);
    185	if (ret)
    186		return ret;
    187
    188	ret = clk_bulk_prepare_enable(aiu->spdif.clk_num, aiu->spdif.clks);
    189	if (ret)
    190		dev_err(dai->dev, "failed to enable spdif clocks\n");
    191
    192	return ret;
    193}
    194
    195static void aiu_encoder_spdif_shutdown(struct snd_pcm_substream *substream,
    196				       struct snd_soc_dai *dai)
    197{
    198	struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
    199
    200	clk_bulk_disable_unprepare(aiu->spdif.clk_num, aiu->spdif.clks);
    201}
    202
    203const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops = {
    204	.trigger	= aiu_encoder_spdif_trigger,
    205	.hw_params	= aiu_encoder_spdif_hw_params,
    206	.hw_free	= aiu_encoder_spdif_hw_free,
    207	.startup	= aiu_encoder_spdif_startup,
    208	.shutdown	= aiu_encoder_spdif_shutdown,
    209};