blob: 3a48f530d1ef65f6c0e2e08a242f83ec71e2bbec [file] [log] [blame]
Asish Bhattacharya366f7502017-07-25 15:15:56 +05301/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/platform_device.h>
13#include <linux/slab.h>
14#include <linux/module.h>
15#include <linux/of_device.h>
16#include <linux/err.h>
17#include <sound/core.h>
18#include <sound/pcm.h>
19#include <sound/soc.h>
20#include <linux/msm_ext_display.h>
21
22#define MSM_EXT_DISP_PCM_RATES SNDRV_PCM_RATE_48000
23#define AUD_EXT_DISP_ACK_DISCONNECT (AUDIO_ACK_CONNECT ^ AUDIO_ACK_CONNECT)
24#define AUD_EXT_DISP_ACK_CONNECT (AUDIO_ACK_CONNECT)
25#define AUD_EXT_DISP_ACK_ENABLE (AUDIO_ACK_SET_ENABLE | AUDIO_ACK_ENABLE)
26
27static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"};
28static const char *const ext_disp_audio_ack_text[] = {"Disconnect", "Connect",
29 "Ack_Enable"};
30
31static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_type, ext_disp_audio_type_text);
32static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_ack_state,
33 ext_disp_audio_ack_text);
34
35struct msm_ext_disp_audio_codec_rx_data {
36 struct platform_device *ext_disp_core_pdev;
37 struct msm_ext_disp_audio_codec_ops ext_disp_ops;
38 int cable_status;
39};
40
41static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol,
42 struct snd_ctl_elem_info *uinfo)
43{
44 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
45 struct msm_ext_disp_audio_codec_rx_data *codec_data;
46 struct msm_ext_disp_audio_edid_blk edid_blk;
47 int rc;
48
49 codec_data = snd_soc_codec_get_drvdata(codec);
50
51 if (!codec_data) {
52 dev_err(codec->dev, "%s: codec_data is NULL\n", __func__);
53 return -EINVAL;
54 }
55
56 if (!codec_data->ext_disp_ops.get_audio_edid_blk) {
57 dev_dbg(codec->dev, "%s: get_audio_edid_blk() is NULL\n",
58 __func__);
59 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
60 uinfo->count = 0;
61 return 0;
62 }
63
64 rc = codec_data->ext_disp_ops.get_audio_edid_blk(
65 codec_data->ext_disp_core_pdev, &edid_blk);
66 if (rc >= 0) {
67 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
68 uinfo->count = edid_blk.audio_data_blk_size +
69 edid_blk.spk_alloc_data_blk_size;
70 }
71
72 dev_dbg(codec->dev, "%s: count: %d\n", __func__, uinfo->count);
73
74 return rc;
75}
76
77static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol,
78 struct snd_ctl_elem_value *ucontrol) {
79 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
80 struct msm_ext_disp_audio_codec_rx_data *codec_data;
81 struct msm_ext_disp_audio_edid_blk edid_blk;
82 int rc;
83
84 codec_data = snd_soc_codec_get_drvdata(codec);
85 if (!codec_data || !codec_data->ext_disp_ops.get_audio_edid_blk) {
86 dev_err(codec->dev, "%s: codec_data or get_audio_edid_blk() is NULL\n",
87 __func__);
88 return -EINVAL;
89 }
90
91 rc = codec_data->ext_disp_ops.get_audio_edid_blk(
92 codec_data->ext_disp_core_pdev, &edid_blk);
93 if (rc >= 0) {
94 if (sizeof(ucontrol->value.bytes.data) <
95 (edid_blk.audio_data_blk_size +
96 edid_blk.spk_alloc_data_blk_size)) {
97 dev_err(codec->dev,
98 "%s: Not enough memory to copy EDID data\n",
99 __func__);
100 return -ENOMEM;
101 }
102
103 memcpy(ucontrol->value.bytes.data,
104 edid_blk.audio_data_blk,
105 edid_blk.audio_data_blk_size);
106 memcpy((ucontrol->value.bytes.data +
107 edid_blk.audio_data_blk_size),
108 edid_blk.spk_alloc_data_blk,
109 edid_blk.spk_alloc_data_blk_size);
110
111 dev_dbg(codec->dev, "%s: data_blk_size:%d, spk_alloc_data_blk_size:%d\n",
112 __func__, edid_blk.audio_data_blk_size,
113 edid_blk.spk_alloc_data_blk_size);
114 }
115
116 return rc;
117}
118
119static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol,
120 struct snd_ctl_elem_value *ucontrol)
121{
122 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
123 struct msm_ext_disp_audio_codec_rx_data *codec_data;
124 enum msm_ext_disp_cable_state cable_state;
125 enum msm_ext_disp_type disp_type;
126 int rc;
127
128 codec_data = snd_soc_codec_get_drvdata(codec);
129 if (!codec_data ||
130 !codec_data->ext_disp_ops.get_audio_edid_blk ||
131 !codec_data->ext_disp_ops.get_intf_id) {
132 dev_err(codec->dev, "%s: codec_data, get_audio_edid_blk() or get_intf_id is NULL\n",
133 __func__);
134 return -EINVAL;
135 }
136
137 cable_state = codec_data->ext_disp_ops.cable_status(
138 codec_data->ext_disp_core_pdev, 1);
139 if (cable_state < 0) {
140 dev_err(codec->dev, "%s: Error retrieving cable state from ext_disp, err:%d\n",
141 __func__, cable_state);
142 rc = cable_state;
143 goto done;
144 }
145
146 codec_data->cable_status = cable_state;
147 if (cable_state == EXT_DISPLAY_CABLE_DISCONNECT) {
148 dev_err(codec->dev, "%s: Display cable disconnected\n",
149 __func__);
150 ucontrol->value.integer.value[0] = 0;
151 rc = 0;
152 goto done;
153 }
154
155 disp_type = codec_data->ext_disp_ops.get_intf_id(
156 codec_data->ext_disp_core_pdev);
157 if (disp_type >= 0) {
158 switch (disp_type) {
159 case EXT_DISPLAY_TYPE_DP:
160 ucontrol->value.integer.value[0] = 2;
161 rc = 0;
162 break;
163 case EXT_DISPLAY_TYPE_HDMI:
164 ucontrol->value.integer.value[0] = 1;
165 rc = 0;
166 break;
167 default:
168 rc = -EINVAL;
169 dev_err(codec->dev, "%s: Invalid disp_type:%d\n",
170 __func__, disp_type);
171 goto done;
172 }
173 dev_dbg(codec->dev, "%s: Display type: %d\n",
174 __func__, disp_type);
175 } else {
176 dev_err(codec->dev, "%s: Error retrieving disp_type from ext_disp, err:%d\n",
177 __func__, disp_type);
178 rc = disp_type;
179 }
180
181done:
182 return rc;
183}
184
185static int msm_ext_disp_audio_ack_set(struct snd_kcontrol *kcontrol,
186 struct snd_ctl_elem_value *ucontrol)
187{
188 struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
189 struct msm_ext_disp_audio_codec_rx_data *codec_data;
190 u32 ack_state = 0;
191 int rc;
192
193 codec_data = snd_soc_codec_get_drvdata(codec);
194 if (!codec_data ||
195 !codec_data->ext_disp_ops.acknowledge) {
196 dev_err(codec->dev,
197 "%s: codec_data or ops acknowledge() is NULL\n",
198 __func__);
199 rc = -EINVAL;
200 goto done;
201 }
202
203 switch (ucontrol->value.enumerated.item[0]) {
204 case 0:
205 ack_state = AUD_EXT_DISP_ACK_DISCONNECT;
206 break;
207 case 1:
208 ack_state = AUD_EXT_DISP_ACK_CONNECT;
209 break;
210 case 2:
211 ack_state = AUD_EXT_DISP_ACK_ENABLE;
212 break;
213 default:
214 rc = -EINVAL;
215 dev_err(codec->dev,
216 "%s: invalid value %d for mixer ctl\n",
217 __func__, ucontrol->value.enumerated.item[0]);
218 goto done;
219 }
220 dev_dbg(codec->dev, "%s: control %d, ack set value 0x%x\n",
221 __func__, ucontrol->value.enumerated.item[0], ack_state);
222
223 rc = codec_data->ext_disp_ops.acknowledge(
224 codec_data->ext_disp_core_pdev, ack_state);
225 if (rc < 0) {
226 dev_err(codec->dev, "%s: error from acknowledge(), err:%d\n",
227 __func__, rc);
228 }
229
230done:
231 return rc;
232}
233
234static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = {
235 {
236 .access = SNDRV_CTL_ELEM_ACCESS_READ |
237 SNDRV_CTL_ELEM_ACCESS_VOLATILE,
238 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
239 .name = "HDMI EDID",
240 .info = msm_ext_disp_edid_ctl_info,
241 .get = msm_ext_disp_edid_get,
242 },
243 {
244 .access = SNDRV_CTL_ELEM_ACCESS_READ |
245 SNDRV_CTL_ELEM_ACCESS_VOLATILE,
246 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
247 .name = "Display Port EDID",
248 .info = msm_ext_disp_edid_ctl_info,
249 .get = msm_ext_disp_edid_get,
250 },
251 SOC_ENUM_EXT("External Display Type", ext_disp_audio_type,
252 msm_ext_disp_audio_type_get, NULL),
253 SOC_ENUM_EXT("External Display Audio Ack", ext_disp_audio_ack_state,
254 NULL, msm_ext_disp_audio_ack_set),
255};
256
257static int msm_ext_disp_audio_codec_rx_dai_startup(
258 struct snd_pcm_substream *substream,
259 struct snd_soc_dai *dai)
260{
261 int ret = 0;
262 struct msm_ext_disp_audio_codec_rx_data *codec_data =
263 dev_get_drvdata(dai->codec->dev);
264
265 if (!codec_data || !codec_data->ext_disp_ops.cable_status) {
266 dev_err(dai->dev, "%s() codec_data or cable_status is null\n",
267 __func__);
268 return -EINVAL;
269 }
270
271 codec_data->cable_status =
272 codec_data->ext_disp_ops.cable_status(
273 codec_data->ext_disp_core_pdev, 1);
274 if (codec_data->cable_status < 0) {
275 dev_err(dai->dev,
276 "%s() ext disp core is not ready (ret val = %d)\n",
277 __func__, codec_data->cable_status);
278 ret = codec_data->cable_status;
279 } else if (!codec_data->cable_status) {
280 dev_err(dai->dev,
281 "%s() ext disp cable is not connected (ret val = %d)\n",
282 __func__, codec_data->cable_status);
283 ret = -ENODEV;
284 }
285
286 return ret;
287}
288
289static int msm_ext_disp_audio_codec_rx_dai_hw_params(
290 struct snd_pcm_substream *substream,
291 struct snd_pcm_hw_params *params,
292 struct snd_soc_dai *dai)
293{
294 u32 channel_allocation = 0;
295 u32 level_shift = 0; /* 0dB */
296 bool down_mix = 0;
297 u32 num_channels = params_channels(params);
298 int rc = 0;
299 struct msm_ext_disp_audio_setup_params audio_setup_params = {0};
300
301 struct msm_ext_disp_audio_codec_rx_data *codec_data =
302 dev_get_drvdata(dai->codec->dev);
303
304 if (!codec_data || !codec_data->ext_disp_ops.audio_info_setup) {
305 dev_err(dai->dev, "%s: codec_data or audio_info_setup is null\n",
306 __func__);
307 return -EINVAL;
308 }
309
310 if (codec_data->cable_status < 0) {
311 dev_err_ratelimited(dai->dev,
312 "%s() ext disp core is not ready (ret val = %d)\n",
313 __func__, codec_data->cable_status);
314 return codec_data->cable_status;
315 } else if (!codec_data->cable_status) {
316 dev_err_ratelimited(dai->dev,
317 "%s() ext disp cable is not connected (ret val = %d)\n",
318 __func__, codec_data->cable_status);
319 return -ENODEV;
320 }
321
322 /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/
323 switch (num_channels) {
324 case 2:
325 channel_allocation = 0;
326 break;
327 case 3:
328 channel_allocation = 0x02;/*default to FL/FR/FC*/
329 audio_setup_params.sample_present = 0x3;
330 break;
331 case 4:
332 channel_allocation = 0x06;/*default to FL/FR/FC/RC*/
333 audio_setup_params.sample_present = 0x7;
334 break;
335 case 5:
336 channel_allocation = 0x0A;/*default to FL/FR/FC/RR/RL*/
337 audio_setup_params.sample_present = 0x7;
338 break;
339 case 6:
340 channel_allocation = 0x0B;
341 audio_setup_params.sample_present = 0x7;
342 break;
343 case 7:
344 channel_allocation = 0x12;/*default to FL/FR/FC/RL/RR/RRC/RLC*/
345 audio_setup_params.sample_present = 0xf;
346 break;
347 case 8:
348 channel_allocation = 0x13;
349 audio_setup_params.sample_present = 0xf;
350 break;
351 default:
352 dev_err(dai->dev, "invalid Channels = %u\n", num_channels);
353 return -EINVAL;
354 }
355
356 dev_dbg(dai->dev,
357 "%s() num_ch %u samplerate %u channel_allocation = %u\n",
358 __func__, num_channels, params_rate(params),
359 channel_allocation);
360
361 audio_setup_params.sample_rate_hz = params_rate(params);
362 audio_setup_params.num_of_channels = num_channels;
363 audio_setup_params.channel_allocation = channel_allocation;
364 audio_setup_params.level_shift = level_shift;
365 audio_setup_params.down_mix = down_mix;
366
367 rc = codec_data->ext_disp_ops.audio_info_setup(
368 codec_data->ext_disp_core_pdev, &audio_setup_params);
369 if (rc < 0) {
370 dev_err_ratelimited(dai->dev,
371 "%s() ext disp core is not ready, rc: %d\n",
372 __func__, rc);
373 }
374
375 return rc;
376}
377
378static void msm_ext_disp_audio_codec_rx_dai_shutdown(
379 struct snd_pcm_substream *substream,
380 struct snd_soc_dai *dai)
381{
382 int rc;
383
384 struct msm_ext_disp_audio_codec_rx_data *codec_data =
385 dev_get_drvdata(dai->codec->dev);
386
387 if (!codec_data || !codec_data->ext_disp_ops.teardown_done ||
388 !codec_data->ext_disp_ops.cable_status) {
389 dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n",
390 __func__);
391 return;
392 }
393
394 rc = codec_data->ext_disp_ops.cable_status(
395 codec_data->ext_disp_core_pdev, 0);
396 if (rc < 0) {
397 dev_err(dai->dev,
398 "%s: ext disp core had problems releasing audio flag\n",
399 __func__);
400 }
401
402 codec_data->ext_disp_ops.teardown_done(
403 codec_data->ext_disp_core_pdev);
404}
405
406static int msm_ext_disp_audio_codec_rx_probe(struct snd_soc_codec *codec)
407{
408 struct msm_ext_disp_audio_codec_rx_data *codec_data;
409 struct device_node *of_node_parent = NULL;
410
411 codec_data = kzalloc(sizeof(struct msm_ext_disp_audio_codec_rx_data),
412 GFP_KERNEL);
413
414 if (!codec_data) {
415 dev_err(codec->dev, "%s(): fail to allocate dai data\n",
416 __func__);
417 return -ENOMEM;
418 }
419
420 of_node_parent = of_get_parent(codec->dev->of_node);
421 if (!of_node_parent) {
422 dev_err(codec->dev, "%s(): Parent device tree node not found\n",
423 __func__);
424 kfree(codec_data);
425 return -ENODEV;
426 }
427
428 codec_data->ext_disp_core_pdev = of_find_device_by_node(of_node_parent);
429 if (!codec_data->ext_disp_core_pdev) {
430 dev_err(codec->dev, "%s(): can't get parent pdev\n", __func__);
431 kfree(codec_data);
432 return -ENODEV;
433 }
434
435 if (msm_ext_disp_register_audio_codec(codec_data->ext_disp_core_pdev,
436 &codec_data->ext_disp_ops)) {
437 dev_err(codec->dev, "%s(): can't register with ext disp core",
438 __func__);
439 kfree(codec_data);
440 return -ENODEV;
441 }
442
443 dev_set_drvdata(codec->dev, codec_data);
444
445 dev_dbg(codec->dev, "%s(): registered %s with ext disp core\n",
446 __func__, codec->component.name);
447
448 return 0;
449}
450
451static int msm_ext_disp_audio_codec_rx_remove(struct snd_soc_codec *codec)
452{
453 struct msm_ext_disp_audio_codec_rx_data *codec_data;
454
455 codec_data = dev_get_drvdata(codec->dev);
456 kfree(codec_data);
457
458 return 0;
459}
460
461static struct snd_soc_dai_ops msm_ext_disp_audio_codec_rx_dai_ops = {
462 .startup = msm_ext_disp_audio_codec_rx_dai_startup,
463 .hw_params = msm_ext_disp_audio_codec_rx_dai_hw_params,
464 .shutdown = msm_ext_disp_audio_codec_rx_dai_shutdown
465};
466
467static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = {
468 {
469 .name = "msm_hdmi_audio_codec_rx_dai",
470 .playback = {
471 .stream_name = "HDMI Playback",
472 .channels_min = 1,
473 .channels_max = 8,
474 .rate_min = 48000,
475 .rate_max = 48000,
476 .rates = MSM_EXT_DISP_PCM_RATES,
477 .formats = SNDRV_PCM_FMTBIT_S16_LE,
478 },
479 .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
480 },
481 {
482 .name = "msm_dp_audio_codec_rx_dai",
483 .playback = {
484 .stream_name = "Display Port Playback",
485 .channels_min = 1,
486 .channels_max = 8,
487 .rate_min = 48000,
488 .rate_max = 192000,
489 .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
490 SNDRV_PCM_RATE_192000,
491 .formats = SNDRV_PCM_FMTBIT_S16_LE |
Ben Romberger8e4368d2017-08-18 15:54:18 -0700492 SNDRV_PCM_FMTBIT_S24_LE |
493 SNDRV_PCM_FMTBIT_S24_3LE,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530494 },
495 .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
496 },
497};
498
499static struct snd_soc_codec_driver msm_ext_disp_audio_codec_rx_soc_driver = {
500 .probe = msm_ext_disp_audio_codec_rx_probe,
501 .remove = msm_ext_disp_audio_codec_rx_remove,
502 .component_driver = {
503 .controls = msm_ext_disp_codec_rx_controls,
504 .num_controls = ARRAY_SIZE(msm_ext_disp_codec_rx_controls),
505 },
506};
507
508static int msm_ext_disp_audio_codec_rx_plat_probe(
509 struct platform_device *pdev)
510{
511 dev_dbg(&pdev->dev, "%s(): dev name %s\n", __func__,
512 dev_name(&pdev->dev));
513
514 return snd_soc_register_codec(&pdev->dev,
515 &msm_ext_disp_audio_codec_rx_soc_driver,
516 msm_ext_disp_audio_codec_rx_dais,
517 ARRAY_SIZE(msm_ext_disp_audio_codec_rx_dais));
518}
519
520static int msm_ext_disp_audio_codec_rx_plat_remove(
521 struct platform_device *pdev)
522{
523 snd_soc_unregister_codec(&pdev->dev);
524 return 0;
525}
526static const struct of_device_id msm_ext_disp_audio_codec_rx_dt_match[] = {
527 { .compatible = "qcom,msm-ext-disp-audio-codec-rx", },
528 {}
529};
530MODULE_DEVICE_TABLE(of, msm_ext_disp_audio_codec_rx_dt_match);
531
532static struct platform_driver msm_ext_disp_audio_codec_rx_driver = {
533 .driver = {
534 .name = "msm-ext-disp-audio-codec-rx",
535 .owner = THIS_MODULE,
536 .of_match_table = msm_ext_disp_audio_codec_rx_dt_match,
537 },
538 .probe = msm_ext_disp_audio_codec_rx_plat_probe,
539 .remove = msm_ext_disp_audio_codec_rx_plat_remove,
540};
541
542static int __init msm_ext_disp_audio_codec_rx_init(void)
543{
544 int rc;
545
546 rc = platform_driver_register(&msm_ext_disp_audio_codec_rx_driver);
547 if (rc) {
548 pr_err("%s: failed to register ext disp codec driver err:%d\n",
549 __func__, rc);
550 }
551
552 return rc;
553}
554module_init(msm_ext_disp_audio_codec_rx_init);
555
556static void __exit msm_ext_disp_audio_codec_rx_exit(void)
557{
558 platform_driver_unregister(&msm_ext_disp_audio_codec_rx_driver);
559}
560module_exit(msm_ext_disp_audio_codec_rx_exit);
561
562MODULE_DESCRIPTION("MSM External Display Audio CODEC Driver");
563MODULE_LICENSE("GPL v2");