dw-hdmi-gp-audio.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
  2. /*
  3. * dw-hdmi-gp-audio.c
  4. *
  5. * Copyright 2020-2022 NXP
  6. */
  7. #include <linux/io.h>
  8. #include <linux/interrupt.h>
  9. #include <linux/module.h>
  10. #include <linux/platform_device.h>
  11. #include <linux/dmaengine.h>
  12. #include <linux/dma-mapping.h>
  13. #include <drm/bridge/dw_hdmi.h>
  14. #include <drm/drm_edid.h>
  15. #include <drm/drm_connector.h>
  16. #include <sound/hdmi-codec.h>
  17. #include <sound/asoundef.h>
  18. #include <sound/core.h>
  19. #include <sound/initval.h>
  20. #include <sound/pcm.h>
  21. #include <sound/pcm_drm_eld.h>
  22. #include <sound/pcm_iec958.h>
  23. #include <sound/dmaengine_pcm.h>
  24. #include "dw-hdmi-audio.h"
  25. #define DRIVER_NAME "dw-hdmi-gp-audio"
  26. #define DRV_NAME "hdmi-gp-audio"
  27. struct snd_dw_hdmi {
  28. struct dw_hdmi_audio_data data;
  29. struct platform_device *audio_pdev;
  30. unsigned int pos;
  31. };
  32. struct dw_hdmi_channel_conf {
  33. u8 conf1;
  34. u8 ca;
  35. };
  36. /*
  37. * The default mapping of ALSA channels to HDMI channels and speaker
  38. * allocation bits. Note that we can't do channel remapping here -
  39. * channels must be in the same order.
  40. *
  41. * Mappings for alsa-lib pcm/surround*.conf files:
  42. *
  43. * Front Sur4.0 Sur4.1 Sur5.0 Sur5.1 Sur7.1
  44. * Channels 2 4 6 6 6 8
  45. *
  46. * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
  47. *
  48. * Number of ALSA channels
  49. * ALSA Channel 2 3 4 5 6 7 8
  50. * 0 FL:0 = = = = = =
  51. * 1 FR:1 = = = = = =
  52. * 2 FC:3 RL:4 LFE:2 = = =
  53. * 3 RR:5 RL:4 FC:3 = =
  54. * 4 RR:5 RL:4 = =
  55. * 5 RR:5 = =
  56. * 6 RC:6 =
  57. * 7 RLC/FRC RLC/FRC
  58. */
  59. static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
  60. { 0x03, 0x00 }, /* FL,FR */
  61. { 0x0b, 0x02 }, /* FL,FR,FC */
  62. { 0x33, 0x08 }, /* FL,FR,RL,RR */
  63. { 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */
  64. { 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */
  65. { 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */
  66. { 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
  67. };
  68. static int audio_hw_params(struct device *dev, void *data,
  69. struct hdmi_codec_daifmt *daifmt,
  70. struct hdmi_codec_params *params)
  71. {
  72. struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
  73. u8 ca;
  74. dw_hdmi_set_sample_rate(dw->data.hdmi, params->sample_rate);
  75. ca = default_hdmi_channel_config[params->channels - 2].ca;
  76. dw_hdmi_set_channel_count(dw->data.hdmi, params->channels);
  77. dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
  78. dw_hdmi_set_sample_non_pcm(dw->data.hdmi,
  79. params->iec.status[0] & IEC958_AES0_NONAUDIO);
  80. dw_hdmi_set_sample_width(dw->data.hdmi, params->sample_width);
  81. return 0;
  82. }
  83. static void audio_shutdown(struct device *dev, void *data)
  84. {
  85. }
  86. static int audio_mute_stream(struct device *dev, void *data,
  87. bool enable, int direction)
  88. {
  89. struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
  90. if (!enable)
  91. dw_hdmi_audio_enable(dw->data.hdmi);
  92. else
  93. dw_hdmi_audio_disable(dw->data.hdmi);
  94. return 0;
  95. }
  96. static int audio_get_eld(struct device *dev, void *data,
  97. u8 *buf, size_t len)
  98. {
  99. struct dw_hdmi_audio_data *audio = data;
  100. u8 *eld;
  101. eld = audio->get_eld(audio->hdmi);
  102. if (eld)
  103. memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));
  104. else
  105. /* Pass en empty ELD if connector not available */
  106. memset(buf, 0, len);
  107. return 0;
  108. }
  109. static int audio_hook_plugged_cb(struct device *dev, void *data,
  110. hdmi_codec_plugged_cb fn,
  111. struct device *codec_dev)
  112. {
  113. struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
  114. return dw_hdmi_set_plugged_cb(dw->data.hdmi, fn, codec_dev);
  115. }
  116. static const struct hdmi_codec_ops audio_codec_ops = {
  117. .hw_params = audio_hw_params,
  118. .audio_shutdown = audio_shutdown,
  119. .mute_stream = audio_mute_stream,
  120. .get_eld = audio_get_eld,
  121. .hook_plugged_cb = audio_hook_plugged_cb,
  122. };
  123. static int snd_dw_hdmi_probe(struct platform_device *pdev)
  124. {
  125. struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
  126. struct snd_dw_hdmi *dw;
  127. const struct hdmi_codec_pdata codec_data = {
  128. .i2s = 1,
  129. .spdif = 0,
  130. .ops = &audio_codec_ops,
  131. .max_i2s_channels = 8,
  132. .data = data,
  133. };
  134. dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
  135. if (!dw)
  136. return -ENOMEM;
  137. dw->data = *data;
  138. platform_set_drvdata(pdev, dw);
  139. dw->audio_pdev = platform_device_register_data(&pdev->dev,
  140. HDMI_CODEC_DRV_NAME, 1,
  141. &codec_data,
  142. sizeof(codec_data));
  143. return PTR_ERR_OR_ZERO(dw->audio_pdev);
  144. }
  145. static int snd_dw_hdmi_remove(struct platform_device *pdev)
  146. {
  147. struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
  148. platform_device_unregister(dw->audio_pdev);
  149. return 0;
  150. }
  151. static struct platform_driver snd_dw_hdmi_driver = {
  152. .probe = snd_dw_hdmi_probe,
  153. .remove = snd_dw_hdmi_remove,
  154. .driver = {
  155. .name = DRIVER_NAME,
  156. },
  157. };
  158. module_platform_driver(snd_dw_hdmi_driver);
  159. MODULE_AUTHOR("Shengjiu Wang <[email protected]>");
  160. MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface");
  161. MODULE_LICENSE("GPL");
  162. MODULE_ALIAS("platform:" DRIVER_NAME);