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

adv7511_audio.c (6314B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Analog Devices ADV7511 HDMI transmitter driver
      4 *
      5 * Copyright 2012 Analog Devices Inc.
      6 * Copyright (c) 2016, Linaro Limited
      7 */
      8
      9#include <sound/core.h>
     10#include <sound/hdmi-codec.h>
     11#include <sound/pcm.h>
     12#include <sound/soc.h>
     13#include <linux/of_graph.h>
     14
     15#include "adv7511.h"
     16
     17static void adv7511_calc_cts_n(unsigned int f_tmds, unsigned int fs,
     18			       unsigned int *cts, unsigned int *n)
     19{
     20	switch (fs) {
     21	case 32000:
     22	case 48000:
     23	case 96000:
     24	case 192000:
     25		*n = fs * 128 / 1000;
     26		break;
     27	case 44100:
     28	case 88200:
     29	case 176400:
     30		*n = fs * 128 / 900;
     31		break;
     32	}
     33
     34	*cts = ((f_tmds * *n) / (128 * fs)) * 1000;
     35}
     36
     37static int adv7511_update_cts_n(struct adv7511 *adv7511)
     38{
     39	unsigned int cts = 0;
     40	unsigned int n = 0;
     41
     42	adv7511_calc_cts_n(adv7511->f_tmds, adv7511->f_audio, &cts, &n);
     43
     44	regmap_write(adv7511->regmap, ADV7511_REG_N0, (n >> 16) & 0xf);
     45	regmap_write(adv7511->regmap, ADV7511_REG_N1, (n >> 8) & 0xff);
     46	regmap_write(adv7511->regmap, ADV7511_REG_N2, n & 0xff);
     47
     48	regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL0,
     49		     (cts >> 16) & 0xf);
     50	regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL1,
     51		     (cts >> 8) & 0xff);
     52	regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL2,
     53		     cts & 0xff);
     54
     55	return 0;
     56}
     57
     58static int adv7511_hdmi_hw_params(struct device *dev, void *data,
     59				  struct hdmi_codec_daifmt *fmt,
     60				  struct hdmi_codec_params *hparms)
     61{
     62	struct adv7511 *adv7511 = dev_get_drvdata(dev);
     63	unsigned int audio_source, i2s_format = 0;
     64	unsigned int invert_clock;
     65	unsigned int rate;
     66	unsigned int len;
     67
     68	switch (hparms->sample_rate) {
     69	case 32000:
     70		rate = ADV7511_SAMPLE_FREQ_32000;
     71		break;
     72	case 44100:
     73		rate = ADV7511_SAMPLE_FREQ_44100;
     74		break;
     75	case 48000:
     76		rate = ADV7511_SAMPLE_FREQ_48000;
     77		break;
     78	case 88200:
     79		rate = ADV7511_SAMPLE_FREQ_88200;
     80		break;
     81	case 96000:
     82		rate = ADV7511_SAMPLE_FREQ_96000;
     83		break;
     84	case 176400:
     85		rate = ADV7511_SAMPLE_FREQ_176400;
     86		break;
     87	case 192000:
     88		rate = ADV7511_SAMPLE_FREQ_192000;
     89		break;
     90	default:
     91		return -EINVAL;
     92	}
     93
     94	switch (hparms->sample_width) {
     95	case 16:
     96		len = ADV7511_I2S_SAMPLE_LEN_16;
     97		break;
     98	case 18:
     99		len = ADV7511_I2S_SAMPLE_LEN_18;
    100		break;
    101	case 20:
    102		len = ADV7511_I2S_SAMPLE_LEN_20;
    103		break;
    104	case 32:
    105		if (fmt->bit_fmt != SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
    106			return -EINVAL;
    107		fallthrough;
    108	case 24:
    109		len = ADV7511_I2S_SAMPLE_LEN_24;
    110		break;
    111	default:
    112		return -EINVAL;
    113	}
    114
    115	switch (fmt->fmt) {
    116	case HDMI_I2S:
    117		audio_source = ADV7511_AUDIO_SOURCE_I2S;
    118		i2s_format = ADV7511_I2S_FORMAT_I2S;
    119		if (fmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
    120			i2s_format = ADV7511_I2S_IEC958_DIRECT;
    121		break;
    122	case HDMI_RIGHT_J:
    123		audio_source = ADV7511_AUDIO_SOURCE_I2S;
    124		i2s_format = ADV7511_I2S_FORMAT_RIGHT_J;
    125		break;
    126	case HDMI_LEFT_J:
    127		audio_source = ADV7511_AUDIO_SOURCE_I2S;
    128		i2s_format = ADV7511_I2S_FORMAT_LEFT_J;
    129		break;
    130	case HDMI_SPDIF:
    131		audio_source = ADV7511_AUDIO_SOURCE_SPDIF;
    132		break;
    133	default:
    134		return -EINVAL;
    135	}
    136
    137	invert_clock = fmt->bit_clk_inv;
    138
    139	regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_SOURCE, 0x70,
    140			   audio_source << 4);
    141	regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(6),
    142			   invert_clock << 6);
    143	regmap_update_bits(adv7511->regmap, ADV7511_REG_I2S_CONFIG, 0x03,
    144			   i2s_format);
    145
    146	adv7511->audio_source = audio_source;
    147
    148	adv7511->f_audio = hparms->sample_rate;
    149
    150	adv7511_update_cts_n(adv7511);
    151
    152	regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG3,
    153			   ADV7511_AUDIO_CFG3_LEN_MASK, len);
    154	regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG,
    155			   ADV7511_I2C_FREQ_ID_CFG_RATE_MASK, rate << 4);
    156	regmap_write(adv7511->regmap, 0x73, 0x1);
    157
    158	return 0;
    159}
    160
    161static int audio_startup(struct device *dev, void *data)
    162{
    163	struct adv7511 *adv7511 = dev_get_drvdata(dev);
    164
    165	regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
    166				BIT(7), 0);
    167
    168	/* hide Audio infoframe updates */
    169	regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
    170				BIT(5), BIT(5));
    171	/* enable N/CTS, enable Audio sample packets */
    172	regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
    173				BIT(5), BIT(5));
    174	/* enable N/CTS */
    175	regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
    176				BIT(6), BIT(6));
    177	/* not copyrighted */
    178	regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG1,
    179				BIT(5), BIT(5));
    180	/* enable audio infoframes */
    181	regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
    182				BIT(3), BIT(3));
    183	/* AV mute disable */
    184	regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(0),
    185				BIT(7) | BIT(6), BIT(7));
    186	/* use Audio infoframe updated info */
    187	regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(1),
    188				BIT(5), 0);
    189	/* enable SPDIF receiver */
    190	if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF)
    191		regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
    192				   BIT(7), BIT(7));
    193
    194	return 0;
    195}
    196
    197static void audio_shutdown(struct device *dev, void *data)
    198{
    199	struct adv7511 *adv7511 = dev_get_drvdata(dev);
    200
    201	if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF)
    202		regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
    203				   BIT(7), 0);
    204}
    205
    206static int adv7511_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
    207					struct device_node *endpoint)
    208{
    209	struct of_endpoint of_ep;
    210	int ret;
    211
    212	ret = of_graph_parse_endpoint(endpoint, &of_ep);
    213	if (ret < 0)
    214		return ret;
    215
    216	/*
    217	 * HDMI sound should be located as reg = <2>
    218	 * Then, it is sound port 0
    219	 */
    220	if (of_ep.port == 2)
    221		return 0;
    222
    223	return -EINVAL;
    224}
    225
    226static const struct hdmi_codec_ops adv7511_codec_ops = {
    227	.hw_params	= adv7511_hdmi_hw_params,
    228	.audio_shutdown = audio_shutdown,
    229	.audio_startup	= audio_startup,
    230	.get_dai_id	= adv7511_hdmi_i2s_get_dai_id,
    231};
    232
    233static const struct hdmi_codec_pdata codec_data = {
    234	.ops = &adv7511_codec_ops,
    235	.max_i2s_channels = 2,
    236	.i2s = 1,
    237	.spdif = 1,
    238};
    239
    240int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511)
    241{
    242	adv7511->audio_pdev = platform_device_register_data(dev,
    243					HDMI_CODEC_DRV_NAME,
    244					PLATFORM_DEVID_AUTO,
    245					&codec_data,
    246					sizeof(codec_data));
    247	return PTR_ERR_OR_ZERO(adv7511->audio_pdev);
    248}
    249
    250void adv7511_audio_exit(struct adv7511 *adv7511)
    251{
    252	if (adv7511->audio_pdev) {
    253		platform_device_unregister(adv7511->audio_pdev);
    254		adv7511->audio_pdev = NULL;
    255	}
    256}