Commit | Line | Data |
---|---|---|
fda8d26e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
53c515be JS |
2 | /* |
3 | * Analog Devices ADV7511 HDMI transmitter driver | |
4 | * | |
5 | * Copyright 2012 Analog Devices Inc. | |
6 | * Copyright (c) 2016, Linaro Limited | |
53c515be JS |
7 | */ |
8 | ||
9 | #include <sound/core.h> | |
10 | #include <sound/hdmi-codec.h> | |
11 | #include <sound/pcm.h> | |
12 | #include <sound/soc.h> | |
7204e976 | 13 | #include <linux/of_graph.h> |
53c515be JS |
14 | |
15 | #include "adv7511.h" | |
16 | ||
17 | static 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: | |
b97b6a1f BT |
22 | case 48000: |
23 | case 96000: | |
24 | case 192000: | |
25 | *n = fs * 128 / 1000; | |
53c515be JS |
26 | break; |
27 | case 44100: | |
b97b6a1f BT |
28 | case 88200: |
29 | case 176400: | |
30 | *n = fs * 128 / 900; | |
53c515be JS |
31 | break; |
32 | } | |
33 | ||
34 | *cts = ((f_tmds * *n) / (128 * fs)) * 1000; | |
35 | } | |
36 | ||
37 | static 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 | ||
759962b5 JY |
58 | static int adv7511_hdmi_hw_params(struct device *dev, void *data, |
59 | struct hdmi_codec_daifmt *fmt, | |
60 | struct hdmi_codec_params *hparms) | |
53c515be JS |
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; | |
ae053fa2 SJH |
104 | case 32: |
105 | if (fmt->bit_fmt != SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) | |
106 | return -EINVAL; | |
107 | fallthrough; | |
53c515be JS |
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; | |
ae053fa2 SJH |
119 | if (fmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) |
120 | i2s_format = ADV7511_I2S_IEC958_DIRECT; | |
53c515be JS |
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; | |
f7f436b9 BT |
130 | case HDMI_SPDIF: |
131 | audio_source = ADV7511_AUDIO_SOURCE_SPDIF; | |
132 | break; | |
53c515be JS |
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 | ||
161 | static 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); | |
f7f436b9 BT |
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 | ||
53c515be JS |
194 | return 0; |
195 | } | |
196 | ||
197 | static void audio_shutdown(struct device *dev, void *data) | |
198 | { | |
f7f436b9 BT |
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); | |
53c515be JS |
204 | } |
205 | ||
7204e976 JS |
206 | static 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 | ||
53c515be JS |
226 | static 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, | |
7204e976 | 230 | .get_dai_id = adv7511_hdmi_i2s_get_dai_id, |
53c515be JS |
231 | }; |
232 | ||
15910174 | 233 | static const struct hdmi_codec_pdata codec_data = { |
53c515be JS |
234 | .ops = &adv7511_codec_ops, |
235 | .max_i2s_channels = 2, | |
236 | .i2s = 1, | |
f7f436b9 | 237 | .spdif = 1, |
53c515be JS |
238 | }; |
239 | ||
240 | int 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 | ||
250 | void 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 | } |