Commit | Line | Data |
---|---|---|
09184118 JS |
1 | /* |
2 | * ALSA SoC codec for HDMI encoder drivers | |
3 | * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ | |
4 | * Author: Jyri Sarha <jsarha@ti.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * version 2 as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | */ | |
15 | #include <linux/module.h> | |
16 | #include <linux/string.h> | |
17 | #include <sound/core.h> | |
18 | #include <sound/pcm.h> | |
19 | #include <sound/pcm_params.h> | |
20 | #include <sound/soc.h> | |
21 | #include <sound/pcm_drm_eld.h> | |
22 | #include <sound/hdmi-codec.h> | |
23 | #include <sound/pcm_iec958.h> | |
24 | ||
25 | #include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */ | |
26 | ||
27 | struct hdmi_codec_priv { | |
28 | struct hdmi_codec_pdata hcd; | |
29 | struct snd_soc_dai_driver *daidrv; | |
30 | struct hdmi_codec_daifmt daifmt[2]; | |
31 | struct mutex current_stream_lock; | |
32 | struct snd_pcm_substream *current_stream; | |
33 | struct snd_pcm_hw_constraint_list ratec; | |
34 | uint8_t eld[MAX_ELD_BYTES]; | |
35 | }; | |
36 | ||
37 | static const struct snd_soc_dapm_widget hdmi_widgets[] = { | |
38 | SND_SOC_DAPM_OUTPUT("TX"), | |
39 | }; | |
40 | ||
41 | static const struct snd_soc_dapm_route hdmi_routes[] = { | |
42 | { "TX", NULL, "Playback" }, | |
43 | }; | |
44 | ||
45 | enum { | |
46 | DAI_ID_I2S = 0, | |
47 | DAI_ID_SPDIF, | |
48 | }; | |
49 | ||
81151cfb PZ |
50 | static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, |
51 | struct snd_ctl_elem_info *uinfo) | |
52 | { | |
53 | struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); | |
54 | struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); | |
55 | ||
56 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; | |
57 | uinfo->count = sizeof(hcp->eld); | |
58 | ||
59 | return 0; | |
60 | } | |
61 | ||
62 | static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, | |
63 | struct snd_ctl_elem_value *ucontrol) | |
64 | { | |
65 | struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); | |
66 | struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); | |
67 | ||
81151cfb | 68 | memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld)); |
81151cfb PZ |
69 | |
70 | return 0; | |
71 | } | |
72 | ||
73 | static const struct snd_kcontrol_new hdmi_controls[] = { | |
74 | { | |
75 | .access = SNDRV_CTL_ELEM_ACCESS_READ | | |
76 | SNDRV_CTL_ELEM_ACCESS_VOLATILE, | |
77 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | |
78 | .name = "ELD", | |
79 | .info = hdmi_eld_ctl_info, | |
80 | .get = hdmi_eld_ctl_get, | |
81 | }, | |
82 | }; | |
83 | ||
09184118 JS |
84 | static int hdmi_codec_new_stream(struct snd_pcm_substream *substream, |
85 | struct snd_soc_dai *dai) | |
86 | { | |
87 | struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); | |
88 | int ret = 0; | |
89 | ||
90 | mutex_lock(&hcp->current_stream_lock); | |
91 | if (!hcp->current_stream) { | |
92 | hcp->current_stream = substream; | |
93 | } else if (hcp->current_stream != substream) { | |
94 | dev_err(dai->dev, "Only one simultaneous stream supported!\n"); | |
95 | ret = -EINVAL; | |
96 | } | |
97 | mutex_unlock(&hcp->current_stream_lock); | |
98 | ||
99 | return ret; | |
100 | } | |
101 | ||
102 | static int hdmi_codec_startup(struct snd_pcm_substream *substream, | |
103 | struct snd_soc_dai *dai) | |
104 | { | |
105 | struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); | |
106 | int ret = 0; | |
107 | ||
108 | dev_dbg(dai->dev, "%s()\n", __func__); | |
109 | ||
110 | ret = hdmi_codec_new_stream(substream, dai); | |
111 | if (ret) | |
112 | return ret; | |
113 | ||
114 | if (hcp->hcd.ops->audio_startup) { | |
115 | ret = hcp->hcd.ops->audio_startup(dai->dev->parent); | |
116 | if (ret) { | |
117 | mutex_lock(&hcp->current_stream_lock); | |
118 | hcp->current_stream = NULL; | |
119 | mutex_unlock(&hcp->current_stream_lock); | |
120 | return ret; | |
121 | } | |
122 | } | |
123 | ||
124 | if (hcp->hcd.ops->get_eld) { | |
125 | ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld, | |
126 | sizeof(hcp->eld)); | |
127 | ||
128 | if (!ret) { | |
129 | ret = snd_pcm_hw_constraint_eld(substream->runtime, | |
130 | hcp->eld); | |
131 | if (ret) | |
132 | return ret; | |
133 | } | |
134 | } | |
135 | return 0; | |
136 | } | |
137 | ||
138 | static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, | |
139 | struct snd_soc_dai *dai) | |
140 | { | |
141 | struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); | |
142 | ||
143 | dev_dbg(dai->dev, "%s()\n", __func__); | |
144 | ||
145 | WARN_ON(hcp->current_stream != substream); | |
146 | ||
147 | hcp->hcd.ops->audio_shutdown(dai->dev->parent); | |
148 | ||
149 | mutex_lock(&hcp->current_stream_lock); | |
150 | hcp->current_stream = NULL; | |
151 | mutex_unlock(&hcp->current_stream_lock); | |
152 | } | |
153 | ||
154 | static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, | |
155 | struct snd_pcm_hw_params *params, | |
156 | struct snd_soc_dai *dai) | |
157 | { | |
158 | struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); | |
159 | struct hdmi_codec_params hp = { | |
160 | .iec = { | |
161 | .status = { 0 }, | |
162 | .subcode = { 0 }, | |
163 | .pad = 0, | |
164 | .dig_subframe = { 0 }, | |
165 | } | |
166 | }; | |
167 | int ret; | |
168 | ||
169 | dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, | |
170 | params_width(params), params_rate(params), | |
171 | params_channels(params)); | |
172 | ||
173 | if (params_width(params) > 24) | |
174 | params->msbits = 24; | |
175 | ||
176 | ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status, | |
177 | sizeof(hp.iec.status)); | |
178 | if (ret < 0) { | |
179 | dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", | |
180 | ret); | |
181 | return ret; | |
182 | } | |
183 | ||
184 | ret = hdmi_codec_new_stream(substream, dai); | |
185 | if (ret) | |
186 | return ret; | |
187 | ||
188 | hdmi_audio_infoframe_init(&hp.cea); | |
189 | hp.cea.channels = params_channels(params); | |
190 | hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; | |
191 | hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; | |
192 | hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; | |
193 | ||
194 | hp.sample_width = params_width(params); | |
195 | hp.sample_rate = params_rate(params); | |
196 | hp.channels = params_channels(params); | |
197 | ||
198 | return hcp->hcd.ops->hw_params(dai->dev->parent, &hcp->daifmt[dai->id], | |
199 | &hp); | |
200 | } | |
201 | ||
202 | static int hdmi_codec_set_fmt(struct snd_soc_dai *dai, | |
203 | unsigned int fmt) | |
204 | { | |
205 | struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); | |
206 | struct hdmi_codec_daifmt cf = { 0 }; | |
207 | int ret = 0; | |
208 | ||
209 | dev_dbg(dai->dev, "%s()\n", __func__); | |
210 | ||
211 | if (dai->id == DAI_ID_SPDIF) { | |
212 | cf.fmt = HDMI_SPDIF; | |
213 | } else { | |
214 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
215 | case SND_SOC_DAIFMT_CBM_CFM: | |
216 | cf.bit_clk_master = 1; | |
217 | cf.frame_clk_master = 1; | |
218 | break; | |
219 | case SND_SOC_DAIFMT_CBS_CFM: | |
220 | cf.frame_clk_master = 1; | |
221 | break; | |
222 | case SND_SOC_DAIFMT_CBM_CFS: | |
223 | cf.bit_clk_master = 1; | |
224 | break; | |
225 | case SND_SOC_DAIFMT_CBS_CFS: | |
226 | break; | |
227 | default: | |
228 | return -EINVAL; | |
229 | } | |
230 | ||
231 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
232 | case SND_SOC_DAIFMT_NB_NF: | |
233 | break; | |
234 | case SND_SOC_DAIFMT_NB_IF: | |
235 | cf.frame_clk_inv = 1; | |
236 | break; | |
237 | case SND_SOC_DAIFMT_IB_NF: | |
238 | cf.bit_clk_inv = 1; | |
239 | break; | |
240 | case SND_SOC_DAIFMT_IB_IF: | |
241 | cf.frame_clk_inv = 1; | |
242 | cf.bit_clk_inv = 1; | |
243 | break; | |
244 | } | |
245 | ||
246 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
247 | case SND_SOC_DAIFMT_I2S: | |
248 | cf.fmt = HDMI_I2S; | |
249 | break; | |
250 | case SND_SOC_DAIFMT_DSP_A: | |
251 | cf.fmt = HDMI_DSP_A; | |
252 | break; | |
253 | case SND_SOC_DAIFMT_DSP_B: | |
254 | cf.fmt = HDMI_DSP_B; | |
255 | break; | |
256 | case SND_SOC_DAIFMT_RIGHT_J: | |
257 | cf.fmt = HDMI_RIGHT_J; | |
258 | break; | |
259 | case SND_SOC_DAIFMT_LEFT_J: | |
260 | cf.fmt = HDMI_LEFT_J; | |
261 | break; | |
262 | case SND_SOC_DAIFMT_AC97: | |
263 | cf.fmt = HDMI_AC97; | |
264 | break; | |
265 | default: | |
266 | dev_err(dai->dev, "Invalid DAI interface format\n"); | |
267 | return -EINVAL; | |
268 | } | |
269 | } | |
270 | ||
271 | hcp->daifmt[dai->id] = cf; | |
272 | ||
273 | return ret; | |
274 | } | |
275 | ||
276 | static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute) | |
277 | { | |
278 | struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); | |
279 | ||
280 | dev_dbg(dai->dev, "%s()\n", __func__); | |
281 | ||
282 | if (hcp->hcd.ops->digital_mute) | |
283 | return hcp->hcd.ops->digital_mute(dai->dev->parent, mute); | |
284 | ||
285 | return 0; | |
286 | } | |
287 | ||
288 | static const struct snd_soc_dai_ops hdmi_dai_ops = { | |
289 | .startup = hdmi_codec_startup, | |
290 | .shutdown = hdmi_codec_shutdown, | |
291 | .hw_params = hdmi_codec_hw_params, | |
292 | .set_fmt = hdmi_codec_set_fmt, | |
293 | .digital_mute = hdmi_codec_digital_mute, | |
294 | }; | |
295 | ||
296 | ||
297 | #define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ | |
298 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ | |
299 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ | |
300 | SNDRV_PCM_RATE_192000) | |
301 | ||
302 | #define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ | |
303 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\ | |
304 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\ | |
305 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) | |
306 | ||
307 | /* | |
308 | * This list is only for formats allowed on the I2S bus. So there is | |
309 | * some formats listed that are not supported by HDMI interface. For | |
310 | * instance allowing the 32-bit formats enables 24-precision with CPU | |
311 | * DAIs that do not support 24-bit formats. If the extra formats cause | |
312 | * problems, we should add the video side driver an option to disable | |
313 | * them. | |
314 | */ | |
315 | #define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ | |
316 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\ | |
317 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\ | |
318 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ | |
319 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) | |
320 | ||
321 | static struct snd_soc_dai_driver hdmi_i2s_dai = { | |
322 | .name = "i2s-hifi", | |
323 | .id = DAI_ID_I2S, | |
324 | .playback = { | |
325 | .stream_name = "Playback", | |
326 | .channels_min = 2, | |
327 | .channels_max = 8, | |
328 | .rates = HDMI_RATES, | |
329 | .formats = I2S_FORMATS, | |
330 | .sig_bits = 24, | |
331 | }, | |
332 | .ops = &hdmi_dai_ops, | |
333 | }; | |
334 | ||
335 | static const struct snd_soc_dai_driver hdmi_spdif_dai = { | |
336 | .name = "spdif-hifi", | |
337 | .id = DAI_ID_SPDIF, | |
338 | .playback = { | |
339 | .stream_name = "Playback", | |
340 | .channels_min = 2, | |
341 | .channels_max = 2, | |
342 | .rates = HDMI_RATES, | |
343 | .formats = SPDIF_FORMATS, | |
344 | }, | |
345 | .ops = &hdmi_dai_ops, | |
346 | }; | |
347 | ||
348 | static struct snd_soc_codec_driver hdmi_codec = { | |
81151cfb PZ |
349 | .controls = hdmi_controls, |
350 | .num_controls = ARRAY_SIZE(hdmi_controls), | |
09184118 JS |
351 | .dapm_widgets = hdmi_widgets, |
352 | .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), | |
353 | .dapm_routes = hdmi_routes, | |
354 | .num_dapm_routes = ARRAY_SIZE(hdmi_routes), | |
355 | }; | |
356 | ||
357 | static int hdmi_codec_probe(struct platform_device *pdev) | |
358 | { | |
359 | struct hdmi_codec_pdata *hcd = pdev->dev.platform_data; | |
360 | struct device *dev = &pdev->dev; | |
361 | struct hdmi_codec_priv *hcp; | |
362 | int dai_count, i = 0; | |
363 | int ret; | |
364 | ||
365 | dev_dbg(dev, "%s()\n", __func__); | |
366 | ||
367 | if (!hcd) { | |
368 | dev_err(dev, "%s: No plalform data\n", __func__); | |
369 | return -EINVAL; | |
370 | } | |
371 | ||
372 | dai_count = hcd->i2s + hcd->spdif; | |
373 | if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params || | |
374 | !hcd->ops->audio_shutdown) { | |
375 | dev_err(dev, "%s: Invalid parameters\n", __func__); | |
376 | return -EINVAL; | |
377 | } | |
378 | ||
379 | hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL); | |
380 | if (!hcp) | |
381 | return -ENOMEM; | |
382 | ||
383 | hcp->hcd = *hcd; | |
384 | mutex_init(&hcp->current_stream_lock); | |
385 | ||
386 | hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv), | |
387 | GFP_KERNEL); | |
388 | if (!hcp->daidrv) | |
389 | return -ENOMEM; | |
390 | ||
391 | if (hcd->i2s) { | |
392 | hcp->daidrv[i] = hdmi_i2s_dai; | |
393 | hcp->daidrv[i].playback.channels_max = | |
394 | hcd->max_i2s_channels; | |
395 | i++; | |
396 | } | |
397 | ||
398 | if (hcd->spdif) | |
399 | hcp->daidrv[i] = hdmi_spdif_dai; | |
400 | ||
401 | ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv, | |
402 | dai_count); | |
403 | if (ret) { | |
404 | dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n", | |
405 | __func__, ret); | |
406 | return ret; | |
407 | } | |
408 | ||
409 | dev_set_drvdata(dev, hcp); | |
410 | return 0; | |
411 | } | |
412 | ||
413 | static int hdmi_codec_remove(struct platform_device *pdev) | |
414 | { | |
415 | snd_soc_unregister_codec(&pdev->dev); | |
416 | return 0; | |
417 | } | |
418 | ||
419 | static struct platform_driver hdmi_codec_driver = { | |
420 | .driver = { | |
421 | .name = HDMI_CODEC_DRV_NAME, | |
422 | }, | |
423 | .probe = hdmi_codec_probe, | |
424 | .remove = hdmi_codec_remove, | |
425 | }; | |
426 | ||
427 | module_platform_driver(hdmi_codec_driver); | |
428 | ||
429 | MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>"); | |
430 | MODULE_DESCRIPTION("HDMI Audio Codec Driver"); | |
431 | MODULE_LICENSE("GPL"); | |
432 | MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME); |