.. | .. |
---|
14 | 14 | */ |
---|
15 | 15 | #include <linux/module.h> |
---|
16 | 16 | #include <linux/string.h> |
---|
| 17 | +#include <linux/extcon-provider.h> |
---|
17 | 18 | #include <sound/core.h> |
---|
18 | 19 | #include <sound/jack.h> |
---|
19 | 20 | #include <sound/pcm.h> |
---|
.. | .. |
---|
277 | 278 | .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC }, |
---|
278 | 279 | }; |
---|
279 | 280 | |
---|
| 281 | +static const unsigned int hdmi_extcon_cable[] = { |
---|
| 282 | + EXTCON_DISP_HDMI_AUDIO, |
---|
| 283 | + EXTCON_NONE, |
---|
| 284 | +}; |
---|
| 285 | + |
---|
280 | 286 | struct hdmi_codec_priv { |
---|
281 | 287 | struct hdmi_codec_pdata hcd; |
---|
282 | 288 | struct snd_soc_dai_driver *daidrv; |
---|
.. | .. |
---|
288 | 294 | unsigned int chmap_idx; |
---|
289 | 295 | unsigned int mode; |
---|
290 | 296 | struct snd_soc_jack *jack; |
---|
| 297 | + struct extcon_dev *edev; |
---|
291 | 298 | unsigned int jack_status; |
---|
292 | 299 | }; |
---|
293 | 300 | |
---|
.. | .. |
---|
498 | 505 | WARN_ON(hcp->current_stream != substream); |
---|
499 | 506 | |
---|
500 | 507 | hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; |
---|
501 | | - hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); |
---|
| 508 | + if (hcp->hcd.ops->audio_shutdown) |
---|
| 509 | + hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); |
---|
502 | 510 | |
---|
503 | 511 | mutex_lock(&hcp->current_stream_lock); |
---|
504 | 512 | hcp->current_stream = NULL; |
---|
.. | .. |
---|
774 | 782 | { |
---|
775 | 783 | struct hdmi_codec_priv *hcp = dev_get_drvdata(dev); |
---|
776 | 784 | |
---|
777 | | - if (plugged) |
---|
| 785 | + if (plugged) { |
---|
778 | 786 | hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT); |
---|
779 | | - else |
---|
| 787 | + extcon_set_state_sync(hcp->edev, |
---|
| 788 | + EXTCON_DISP_HDMI_AUDIO, true); |
---|
| 789 | + } else { |
---|
780 | 790 | hdmi_codec_jack_report(hcp, 0); |
---|
| 791 | + extcon_set_state_sync(hcp->edev, |
---|
| 792 | + EXTCON_DISP_HDMI_AUDIO, false); |
---|
| 793 | + } |
---|
| 794 | + |
---|
| 795 | + mutex_lock(&hcp->current_stream_lock); |
---|
| 796 | + if (hcp->current_stream) { |
---|
| 797 | + /* |
---|
| 798 | + * Workaround for HDMIIN and HDMIOUT plug-{in,out} when streaming. |
---|
| 799 | + * |
---|
| 800 | + * Actually, we should do stop stream both for HDMI_{OUT,IN} on |
---|
| 801 | + * plug-{out,in} event. but for better experience and depop stream, |
---|
| 802 | + * we optimize as follows: |
---|
| 803 | + * |
---|
| 804 | + * a) Do stop stream for HDMIIN on plug-out when streaming. |
---|
| 805 | + * because HDMIIN work as SLAVE mode, CLK lost after HDMI cable |
---|
| 806 | + * plugged out which will make stream stuck until ALSA timeout(10s). |
---|
| 807 | + * so, for better experience, we should stop stream at the moment. |
---|
| 808 | + * |
---|
| 809 | + * b) Do stop stream for HDMIOUT on plug-in when streaming. |
---|
| 810 | + * because HDMIOUT work as MASTER mode, there is no clk-issue like |
---|
| 811 | + * HDMIIN, but, on HDR situation, HDMI will be reconfigured which |
---|
| 812 | + * make HDMI audio configure lost, especially for NLPCM/HBR bitstream |
---|
| 813 | + * which require IEC937 packet alignment, so, for this situation, |
---|
| 814 | + * we stop stream to notify user to re-open and configure sound card |
---|
| 815 | + * and then go on streaming. |
---|
| 816 | + */ |
---|
| 817 | + int stream = hcp->current_stream->stream; |
---|
| 818 | + |
---|
| 819 | + if (stream == SNDRV_PCM_STREAM_PLAYBACK && plugged) |
---|
| 820 | + snd_pcm_stop(hcp->current_stream, SNDRV_PCM_STATE_SETUP); |
---|
| 821 | + else if (stream == SNDRV_PCM_STREAM_CAPTURE && !plugged) |
---|
| 822 | + snd_pcm_stop(hcp->current_stream, SNDRV_PCM_STATE_DISCONNECTED); |
---|
| 823 | + |
---|
| 824 | + dev_dbg(dev, "stream[%d]: %s\n", stream, plugged ? "plug in" : "plug out"); |
---|
| 825 | + } |
---|
| 826 | + mutex_unlock(&hcp->current_stream_lock); |
---|
781 | 827 | } |
---|
782 | 828 | |
---|
783 | 829 | static int hdmi_codec_set_jack(struct snd_soc_component *component, |
---|
.. | .. |
---|
884 | 930 | } |
---|
885 | 931 | |
---|
886 | 932 | dai_count = hcd->i2s + hcd->spdif; |
---|
887 | | - if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params || |
---|
888 | | - !hcd->ops->audio_shutdown) { |
---|
| 933 | + if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params) { |
---|
889 | 934 | dev_err(dev, "%s: Invalid parameters\n", __func__); |
---|
890 | 935 | return -EINVAL; |
---|
891 | 936 | } |
---|
.. | .. |
---|
916 | 961 | |
---|
917 | 962 | dev_set_drvdata(dev, hcp); |
---|
918 | 963 | |
---|
| 964 | + hcp->edev = devm_extcon_dev_allocate(&pdev->dev, hdmi_extcon_cable); |
---|
| 965 | + if (IS_ERR(hcp->edev)) { |
---|
| 966 | + dev_err(&pdev->dev, "Failed to allocate extcon device\n"); |
---|
| 967 | + return -ENOMEM; |
---|
| 968 | + } |
---|
| 969 | + |
---|
| 970 | + ret = devm_extcon_dev_register(&pdev->dev, hcp->edev); |
---|
| 971 | + if (ret < 0) { |
---|
| 972 | + dev_err(&pdev->dev, "Failed to register extcon device\n"); |
---|
| 973 | + return ret; |
---|
| 974 | + } |
---|
| 975 | + |
---|
919 | 976 | ret = devm_snd_soc_register_component(dev, &hdmi_driver, hcp->daidrv, |
---|
920 | 977 | dai_count); |
---|
921 | 978 | if (ret) { |
---|