xref: /openbmc/linux/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c (revision 9144f784f852f9a125cabe9927b986d909bfa439)
1fda8d26eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
253c515beSJohn Stultz /*
353c515beSJohn Stultz  * Analog Devices ADV7511 HDMI transmitter driver
453c515beSJohn Stultz  *
553c515beSJohn Stultz  * Copyright 2012 Analog Devices Inc.
653c515beSJohn Stultz  * Copyright (c) 2016, Linaro Limited
753c515beSJohn Stultz  */
853c515beSJohn Stultz 
953c515beSJohn Stultz #include <sound/core.h>
1053c515beSJohn Stultz #include <sound/hdmi-codec.h>
1153c515beSJohn Stultz #include <sound/pcm.h>
1253c515beSJohn Stultz #include <sound/soc.h>
137204e976SJohn Stultz #include <linux/of_graph.h>
1453c515beSJohn Stultz 
1553c515beSJohn Stultz #include "adv7511.h"
1653c515beSJohn Stultz 
adv7511_calc_cts_n(unsigned int f_tmds,unsigned int fs,unsigned int * cts,unsigned int * n)1753c515beSJohn Stultz static void adv7511_calc_cts_n(unsigned int f_tmds, unsigned int fs,
1853c515beSJohn Stultz 			       unsigned int *cts, unsigned int *n)
1953c515beSJohn Stultz {
2053c515beSJohn Stultz 	switch (fs) {
2153c515beSJohn Stultz 	case 32000:
22b97b6a1fSBogdan Togorean 	case 48000:
23b97b6a1fSBogdan Togorean 	case 96000:
24b97b6a1fSBogdan Togorean 	case 192000:
25b97b6a1fSBogdan Togorean 		*n = fs * 128 / 1000;
2653c515beSJohn Stultz 		break;
2753c515beSJohn Stultz 	case 44100:
28b97b6a1fSBogdan Togorean 	case 88200:
29b97b6a1fSBogdan Togorean 	case 176400:
30b97b6a1fSBogdan Togorean 		*n = fs * 128 / 900;
3153c515beSJohn Stultz 		break;
3253c515beSJohn Stultz 	}
3353c515beSJohn Stultz 
3453c515beSJohn Stultz 	*cts = ((f_tmds * *n) / (128 * fs)) * 1000;
3553c515beSJohn Stultz }
3653c515beSJohn Stultz 
adv7511_update_cts_n(struct adv7511 * adv7511)3753c515beSJohn Stultz static int adv7511_update_cts_n(struct adv7511 *adv7511)
3853c515beSJohn Stultz {
3953c515beSJohn Stultz 	unsigned int cts = 0;
4053c515beSJohn Stultz 	unsigned int n = 0;
4153c515beSJohn Stultz 
4253c515beSJohn Stultz 	adv7511_calc_cts_n(adv7511->f_tmds, adv7511->f_audio, &cts, &n);
4353c515beSJohn Stultz 
4453c515beSJohn Stultz 	regmap_write(adv7511->regmap, ADV7511_REG_N0, (n >> 16) & 0xf);
4553c515beSJohn Stultz 	regmap_write(adv7511->regmap, ADV7511_REG_N1, (n >> 8) & 0xff);
4653c515beSJohn Stultz 	regmap_write(adv7511->regmap, ADV7511_REG_N2, n & 0xff);
4753c515beSJohn Stultz 
4853c515beSJohn Stultz 	regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL0,
4953c515beSJohn Stultz 		     (cts >> 16) & 0xf);
5053c515beSJohn Stultz 	regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL1,
5153c515beSJohn Stultz 		     (cts >> 8) & 0xff);
5253c515beSJohn Stultz 	regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL2,
5353c515beSJohn Stultz 		     cts & 0xff);
5453c515beSJohn Stultz 
5553c515beSJohn Stultz 	return 0;
5653c515beSJohn Stultz }
5753c515beSJohn Stultz 
adv7511_hdmi_hw_params(struct device * dev,void * data,struct hdmi_codec_daifmt * fmt,struct hdmi_codec_params * hparms)58759962b5SJason Yan static int adv7511_hdmi_hw_params(struct device *dev, void *data,
5953c515beSJohn Stultz 				  struct hdmi_codec_daifmt *fmt,
6053c515beSJohn Stultz 				  struct hdmi_codec_params *hparms)
6153c515beSJohn Stultz {
6253c515beSJohn Stultz 	struct adv7511 *adv7511 = dev_get_drvdata(dev);
6353c515beSJohn Stultz 	unsigned int audio_source, i2s_format = 0;
6453c515beSJohn Stultz 	unsigned int invert_clock;
6553c515beSJohn Stultz 	unsigned int rate;
6653c515beSJohn Stultz 	unsigned int len;
6753c515beSJohn Stultz 
6853c515beSJohn Stultz 	switch (hparms->sample_rate) {
6953c515beSJohn Stultz 	case 32000:
7053c515beSJohn Stultz 		rate = ADV7511_SAMPLE_FREQ_32000;
7153c515beSJohn Stultz 		break;
7253c515beSJohn Stultz 	case 44100:
7353c515beSJohn Stultz 		rate = ADV7511_SAMPLE_FREQ_44100;
7453c515beSJohn Stultz 		break;
7553c515beSJohn Stultz 	case 48000:
7653c515beSJohn Stultz 		rate = ADV7511_SAMPLE_FREQ_48000;
7753c515beSJohn Stultz 		break;
7853c515beSJohn Stultz 	case 88200:
7953c515beSJohn Stultz 		rate = ADV7511_SAMPLE_FREQ_88200;
8053c515beSJohn Stultz 		break;
8153c515beSJohn Stultz 	case 96000:
8253c515beSJohn Stultz 		rate = ADV7511_SAMPLE_FREQ_96000;
8353c515beSJohn Stultz 		break;
8453c515beSJohn Stultz 	case 176400:
8553c515beSJohn Stultz 		rate = ADV7511_SAMPLE_FREQ_176400;
8653c515beSJohn Stultz 		break;
8753c515beSJohn Stultz 	case 192000:
8853c515beSJohn Stultz 		rate = ADV7511_SAMPLE_FREQ_192000;
8953c515beSJohn Stultz 		break;
9053c515beSJohn Stultz 	default:
9153c515beSJohn Stultz 		return -EINVAL;
9253c515beSJohn Stultz 	}
9353c515beSJohn Stultz 
9453c515beSJohn Stultz 	switch (hparms->sample_width) {
9553c515beSJohn Stultz 	case 16:
9653c515beSJohn Stultz 		len = ADV7511_I2S_SAMPLE_LEN_16;
9753c515beSJohn Stultz 		break;
9853c515beSJohn Stultz 	case 18:
9953c515beSJohn Stultz 		len = ADV7511_I2S_SAMPLE_LEN_18;
10053c515beSJohn Stultz 		break;
10153c515beSJohn Stultz 	case 20:
10253c515beSJohn Stultz 		len = ADV7511_I2S_SAMPLE_LEN_20;
10353c515beSJohn Stultz 		break;
104ae053fa2SSia Jee Heng 	case 32:
105ae053fa2SSia Jee Heng 		if (fmt->bit_fmt != SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
106ae053fa2SSia Jee Heng 			return -EINVAL;
107ae053fa2SSia Jee Heng 		fallthrough;
10853c515beSJohn Stultz 	case 24:
10953c515beSJohn Stultz 		len = ADV7511_I2S_SAMPLE_LEN_24;
11053c515beSJohn Stultz 		break;
11153c515beSJohn Stultz 	default:
11253c515beSJohn Stultz 		return -EINVAL;
11353c515beSJohn Stultz 	}
11453c515beSJohn Stultz 
11553c515beSJohn Stultz 	switch (fmt->fmt) {
11653c515beSJohn Stultz 	case HDMI_I2S:
11753c515beSJohn Stultz 		audio_source = ADV7511_AUDIO_SOURCE_I2S;
11853c515beSJohn Stultz 		i2s_format = ADV7511_I2S_FORMAT_I2S;
119ae053fa2SSia Jee Heng 		if (fmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
120ae053fa2SSia Jee Heng 			i2s_format = ADV7511_I2S_IEC958_DIRECT;
12153c515beSJohn Stultz 		break;
12253c515beSJohn Stultz 	case HDMI_RIGHT_J:
12353c515beSJohn Stultz 		audio_source = ADV7511_AUDIO_SOURCE_I2S;
12453c515beSJohn Stultz 		i2s_format = ADV7511_I2S_FORMAT_RIGHT_J;
12553c515beSJohn Stultz 		break;
12653c515beSJohn Stultz 	case HDMI_LEFT_J:
12753c515beSJohn Stultz 		audio_source = ADV7511_AUDIO_SOURCE_I2S;
12853c515beSJohn Stultz 		i2s_format = ADV7511_I2S_FORMAT_LEFT_J;
12953c515beSJohn Stultz 		break;
130f7f436b9SBogdan Togorean 	case HDMI_SPDIF:
131f7f436b9SBogdan Togorean 		audio_source = ADV7511_AUDIO_SOURCE_SPDIF;
132f7f436b9SBogdan Togorean 		break;
13353c515beSJohn Stultz 	default:
13453c515beSJohn Stultz 		return -EINVAL;
13553c515beSJohn Stultz 	}
13653c515beSJohn Stultz 
13753c515beSJohn Stultz 	invert_clock = fmt->bit_clk_inv;
13853c515beSJohn Stultz 
13953c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_SOURCE, 0x70,
14053c515beSJohn Stultz 			   audio_source << 4);
14153c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(6),
14253c515beSJohn Stultz 			   invert_clock << 6);
14353c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_I2S_CONFIG, 0x03,
14453c515beSJohn Stultz 			   i2s_format);
14553c515beSJohn Stultz 
14653c515beSJohn Stultz 	adv7511->audio_source = audio_source;
14753c515beSJohn Stultz 
14853c515beSJohn Stultz 	adv7511->f_audio = hparms->sample_rate;
14953c515beSJohn Stultz 
15053c515beSJohn Stultz 	adv7511_update_cts_n(adv7511);
15153c515beSJohn Stultz 
15253c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG3,
15353c515beSJohn Stultz 			   ADV7511_AUDIO_CFG3_LEN_MASK, len);
15453c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG,
15553c515beSJohn Stultz 			   ADV7511_I2C_FREQ_ID_CFG_RATE_MASK, rate << 4);
156*fa7f9658SStefan Ekenberg 
157*fa7f9658SStefan Ekenberg 	/* send current Audio infoframe values while updating */
158*fa7f9658SStefan Ekenberg 	regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
159*fa7f9658SStefan Ekenberg 			   BIT(5), BIT(5));
160*fa7f9658SStefan Ekenberg 
161*fa7f9658SStefan Ekenberg 	regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME(0), 0x1);
162*fa7f9658SStefan Ekenberg 
163*fa7f9658SStefan Ekenberg 	/* use Audio infoframe updated info */
164*fa7f9658SStefan Ekenberg 	regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
165*fa7f9658SStefan Ekenberg 			   BIT(5), 0);
16653c515beSJohn Stultz 
16753c515beSJohn Stultz 	return 0;
16853c515beSJohn Stultz }
16953c515beSJohn Stultz 
audio_startup(struct device * dev,void * data)17053c515beSJohn Stultz static int audio_startup(struct device *dev, void *data)
17153c515beSJohn Stultz {
17253c515beSJohn Stultz 	struct adv7511 *adv7511 = dev_get_drvdata(dev);
17353c515beSJohn Stultz 
17453c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
17553c515beSJohn Stultz 				BIT(7), 0);
17653c515beSJohn Stultz 
17753c515beSJohn Stultz 	/* hide Audio infoframe updates */
17853c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
17953c515beSJohn Stultz 				BIT(5), BIT(5));
18053c515beSJohn Stultz 	/* enable N/CTS, enable Audio sample packets */
18153c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
18253c515beSJohn Stultz 				BIT(5), BIT(5));
18353c515beSJohn Stultz 	/* enable N/CTS */
18453c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
18553c515beSJohn Stultz 				BIT(6), BIT(6));
18653c515beSJohn Stultz 	/* not copyrighted */
18753c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG1,
18853c515beSJohn Stultz 				BIT(5), BIT(5));
18953c515beSJohn Stultz 	/* enable audio infoframes */
19053c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
19153c515beSJohn Stultz 				BIT(3), BIT(3));
19253c515beSJohn Stultz 	/* AV mute disable */
19353c515beSJohn Stultz 	regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(0),
19453c515beSJohn Stultz 				BIT(7) | BIT(6), BIT(7));
19553c515beSJohn Stultz 	/* use Audio infoframe updated info */
196*fa7f9658SStefan Ekenberg 	regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
19753c515beSJohn Stultz 				BIT(5), 0);
198*fa7f9658SStefan Ekenberg 
199f7f436b9SBogdan Togorean 	/* enable SPDIF receiver */
200f7f436b9SBogdan Togorean 	if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF)
201f7f436b9SBogdan Togorean 		regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
202f7f436b9SBogdan Togorean 				   BIT(7), BIT(7));
203f7f436b9SBogdan Togorean 
20453c515beSJohn Stultz 	return 0;
20553c515beSJohn Stultz }
20653c515beSJohn Stultz 
audio_shutdown(struct device * dev,void * data)20753c515beSJohn Stultz static void audio_shutdown(struct device *dev, void *data)
20853c515beSJohn Stultz {
209f7f436b9SBogdan Togorean 	struct adv7511 *adv7511 = dev_get_drvdata(dev);
210f7f436b9SBogdan Togorean 
211f7f436b9SBogdan Togorean 	if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF)
212f7f436b9SBogdan Togorean 		regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
213f7f436b9SBogdan Togorean 				   BIT(7), 0);
21453c515beSJohn Stultz }
21553c515beSJohn Stultz 
adv7511_hdmi_i2s_get_dai_id(struct snd_soc_component * component,struct device_node * endpoint)2167204e976SJohn Stultz static int adv7511_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
2177204e976SJohn Stultz 					struct device_node *endpoint)
2187204e976SJohn Stultz {
2197204e976SJohn Stultz 	struct of_endpoint of_ep;
2207204e976SJohn Stultz 	int ret;
2217204e976SJohn Stultz 
2227204e976SJohn Stultz 	ret = of_graph_parse_endpoint(endpoint, &of_ep);
2237204e976SJohn Stultz 	if (ret < 0)
2247204e976SJohn Stultz 		return ret;
2257204e976SJohn Stultz 
2267204e976SJohn Stultz 	/*
2277204e976SJohn Stultz 	 * HDMI sound should be located as reg = <2>
2287204e976SJohn Stultz 	 * Then, it is sound port 0
2297204e976SJohn Stultz 	 */
2307204e976SJohn Stultz 	if (of_ep.port == 2)
2317204e976SJohn Stultz 		return 0;
2327204e976SJohn Stultz 
2337204e976SJohn Stultz 	return -EINVAL;
2347204e976SJohn Stultz }
2357204e976SJohn Stultz 
23653c515beSJohn Stultz static const struct hdmi_codec_ops adv7511_codec_ops = {
23753c515beSJohn Stultz 	.hw_params	= adv7511_hdmi_hw_params,
23853c515beSJohn Stultz 	.audio_shutdown = audio_shutdown,
23953c515beSJohn Stultz 	.audio_startup	= audio_startup,
2407204e976SJohn Stultz 	.get_dai_id	= adv7511_hdmi_i2s_get_dai_id,
24153c515beSJohn Stultz };
24253c515beSJohn Stultz 
24315910174SLars-Peter Clausen static const struct hdmi_codec_pdata codec_data = {
24453c515beSJohn Stultz 	.ops = &adv7511_codec_ops,
24553c515beSJohn Stultz 	.max_i2s_channels = 2,
24653c515beSJohn Stultz 	.i2s = 1,
247f7f436b9SBogdan Togorean 	.spdif = 1,
24853c515beSJohn Stultz };
24953c515beSJohn Stultz 
adv7511_audio_init(struct device * dev,struct adv7511 * adv7511)25053c515beSJohn Stultz int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511)
25153c515beSJohn Stultz {
25253c515beSJohn Stultz 	adv7511->audio_pdev = platform_device_register_data(dev,
25353c515beSJohn Stultz 					HDMI_CODEC_DRV_NAME,
25453c515beSJohn Stultz 					PLATFORM_DEVID_AUTO,
25553c515beSJohn Stultz 					&codec_data,
25653c515beSJohn Stultz 					sizeof(codec_data));
25753c515beSJohn Stultz 	return PTR_ERR_OR_ZERO(adv7511->audio_pdev);
25853c515beSJohn Stultz }
25953c515beSJohn Stultz 
adv7511_audio_exit(struct adv7511 * adv7511)26053c515beSJohn Stultz void adv7511_audio_exit(struct adv7511 *adv7511)
26153c515beSJohn Stultz {
26253c515beSJohn Stultz 	if (adv7511->audio_pdev) {
26353c515beSJohn Stultz 		platform_device_unregister(adv7511->audio_pdev);
26453c515beSJohn Stultz 		adv7511->audio_pdev = NULL;
26553c515beSJohn Stultz 	}
26653c515beSJohn Stultz }
267