| .. | .. |
|---|
| 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) { |
|---|