From patchwork Thu Aug 3 09:22:33 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Sylwester Nawrocki/Kernel \\(PLT\\) /SRPOL/Staff Engineer/Samsung Electronics" X-Patchwork-Id: 9878531 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id F32A8603F4 for ; Thu, 3 Aug 2017 09:22:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E3E252888B for ; Thu, 3 Aug 2017 09:22:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D61E42888D; Thu, 3 Aug 2017 09:22:52 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id DAA2C2888E for ; Thu, 3 Aug 2017 09:22:51 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id CA6636E121; Thu, 3 Aug 2017 09:22:50 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) by gabe.freedesktop.org (Postfix) with ESMTPS id 55EF86E121 for ; Thu, 3 Aug 2017 09:22:49 +0000 (UTC) Received: from epcas2p4.samsung.com (unknown [182.195.41.56]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20170803092245epoutp034771b7fc021b0fd9f114fe5af0f876dd~XS6M1OvPi0140101401epoutp03Y; Thu, 3 Aug 2017 09:22:45 +0000 (GMT) Received: from epsmges2p2.samsung.com (unknown [182.195.42.70]) by epcas2p4.samsung.com (KnoxPortal) with ESMTP id 20170803092244epcas2p4aa4eef1e6b1189b74d1ac23b4ca661b8~XS6MroDHT0695306953epcas2p4o; Thu, 3 Aug 2017 09:22:44 +0000 (GMT) Received: from epcas2p3.samsung.com ( [182.195.41.55]) by epsmges2p2.samsung.com (Symantec Messaging Gateway) with SMTP id BE.3D.15349.46BE2895; Thu, 3 Aug 2017 18:22:44 +0900 (KST) Received: from epsmgms2p2.samsung.com (unknown [182.195.42.80]) by epcas2p2.samsung.com (KnoxPortal) with ESMTP id 20170803092244epcas2p22ba6f676780e3efb2cf7ae87edaa550c~XS6MVLI2T2198821988epcas2p2I; Thu, 3 Aug 2017 09:22:44 +0000 (GMT) X-AuditID: b6c32a46-f790d6d000003bf5-21-5982eb649233 Received: from epmmp2 ( [203.254.227.17]) by epsmgms2p2.samsung.com (Symantec Messaging Gateway) with SMTP id 8C.9E.09541.46BE2895; Thu, 3 Aug 2017 18:22:44 +0900 (KST) Received: from AMDC3061.digital.local ([106.116.147.40]) by mmp2.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0OU300MSTRDN2D90@mmp2.samsung.com>; Thu, 03 Aug 2017 18:22:44 +0900 (KST) From: Sylwester Nawrocki To: inki.dae@samsung.com Subject: [PATCH] drm: exynos: Add driver for HDMI audio interface Date: Thu, 03 Aug 2017 11:22:33 +0200 Message-id: <1501752153-26669-1-git-send-email-s.nawrocki@samsung.com> X-Mailer: git-send-email 1.9.1 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrOIsWRmVeSWpSXmKPExsWy7bCmuW7K66ZIg/2vzCyuXDzEZLFxxnpW iytf37NZTLo/gcXixb2LLBbnz29gt5hxfh+TxeE37awWMya/ZHPg9NjwuYnNY9OqTjaP+93H mTz6tqxi9Pi8SS6ANYrLJiU1J7MstUjfLoEr4+L8zawFrckVL/fNZ2pg7A7qYuTkkBAwkZj2 7g8LhC0mceHeerYuRi4OIYEdjBLblrezQzifGSX+LP3MBtOxomEmK0RiLaPElnOroZxfjBK9 G1aBVbEJGEr0Hu1jBLFFBCQkZr66yARiMwt8YpS4N08QxBYWcJSYdmUzUJyDg0VAVeLeQiuQ MK+Am0TDvo2MEMvkJE4em8wKYb9mk/g43RmkXEJAVmLTAWaIsIvE7N2roEqEJV4d38IOYUtJ dHfMAntAQqCfUeLEmmZGCGcGo8Sd9glMEFXWEoePX2SFuI1PouPwX3aIBbwSHW1CEKaHxM1v FRDVjhLrjr0EqxYSiJVYNXUL6wRG6QWMDKsYxVILinPTU4uNCoz0ihNzi0vz0vWS83M3MYKj V8ttB+OScz6HGAU4GJV4eDtuNEYKsSaWFVfmHmKU4GBWEuFNedIUKcSbklhZlVqUH19UmpNa fIhRmoNFSZy3btu1CCGB9MSS1OzU1ILUIpgsEwenVANj1x93Q4NQroi1RqlPvgabM/WxLMvb eH9/9N4sCcZnl3eybZwZEaAcZPM9QJRd+kGeir1+hsGJM/fXr5NXdyhW7L/OeOzk4boDT3xf mqzb05hjtOrv7bZwZ5+fl7qduD6KsdnzavkcFeFRXfT6rGZ/4Y4ezdTTeevUvYQm3z79bNJf g5N5HI+VWIozEg21mIuKEwF3+5kr2gIAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrDLMWRmVeSWpSXmKPExsVy+t9jQd2U102RBhcfyFtcuXiIyWLjjPWs Fle+vmezmHR/AovFi3sXWSzOn9/AbjHj/D4mi8Nv2lktZkx+yebA6bHhcxObx6ZVnWwe97uP M3n0bVnF6PF5k1wAa5SbTUZqYkpqkUJqXnJ+SmZeuq1SaIibroWSQl5ibqqtUoSub0iQkkJZ Yk4pkGdkgAYcnAPcg5X07RLcMi7O38xa0Jpc8XLffKYGxu6gLkZODgkBE4kVDTNZIWwxiQv3 1rN1MXJxCAmsZpR4PfMPWEJI4BejxI4v8SA2m4ChRO/RPkYQW0RAQmLmq4tMIA3MAp8YJX5v mMQEkhAWcJSYdmUzkM3BwSKgKnFvoRVImFfATaJh30ZGiGVyEiePTWadwMi9gJFhFaNYakFx bnpusVGBkV5xYm5xaV66XnJ+7iZGYMhuO6wVsIOx6Vz0IUYBDkYlHt6OG42RQqyJZcWVuYcY JTiYlUR4U540RQrxpiRWVqUW5ccXleakFh9iNAXaPpFZSjQ5HxhPeSXxhiaWRiYGZmaGRgbG ZkrivBMCv0QICaQnlqRmp6YWpBbB9DFxcEo1MFryK8axavw0yngWuDzxdSdHucORa87LonM+ MjiGzf6v1cmQyT7h9bsD+ndFVE0V+udU1ymoNk9uFz/vtJt/3paJBpN3fZ28drILk4RdhdLX 9uf+7z//8rf9YD+9W4dbN+tK0stLJ9vlGWY8q+o+W7f/8EaxW9fk/Yxj2H5NunaNr/Tnu/tH hJRYijMSDbWYi4oTAZUuRA1vAgAA X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20170803092244epcas2p22ba6f676780e3efb2cf7ae87edaa550c X-Msg-Generator: CA X-Sender-IP: 182.195.42.80 X-Local-Sender: =?UTF-8?B?U3lsd2VzdGVyIE5hd3JvY2tpG1NSUE9MLUtlcm5lbCAoVFAp?= =?UTF-8?B?G+yCvOyEseyghOyekBtTZW5pb3IgU29mdHdhcmUgRW5naW5lZXI=?= X-Global-Sender: =?UTF-8?B?U3lsd2VzdGVyIE5hd3JvY2tpG1NSUE9MLUtlcm5lbCAoVFAp?= =?UTF-8?B?G1NhbXN1bmcgRWxlY3Ryb25pY3MbU2VuaW9yIFNvZnR3YXJlIEVuZ2luZWVy?= X-Sender-Code: =?UTF-8?B?QzEwG0VIURtDMTBDRDAyQ0QwMjczOTI=?= CMS-TYPE: 102P X-CMS-RootMailID: 20170803092244epcas2p22ba6f676780e3efb2cf7ae87edaa550c X-RootMTR: 20170803092244epcas2p22ba6f676780e3efb2cf7ae87edaa550c References: Cc: alsa-devel@alsa-project.org, linux-samsung-soc@vger.kernel.org, b.zolnierkie@samsung.com, sw0312.kim@samsung.com, dri-devel@lists.freedesktop.org, krzk@kernel.org, Sylwester Nawrocki X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP The hdmi-codec interface added in this patch is required to properly support HDMI audio. Currently the audio part of the SoC internal HDMI transmitter is configured with fixed values, which makes HDMI audio working by chance, only on boards equipped with external audio codec connected in parallel with the HDMI audio transmitter I2S input interface. Signed-off-by: Sylwester Nawrocki --- Changes since RFC version: - fixed hdmi-codec locking issue - added a comment documenting struct hdmi_contex::mutex --- drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_hdmi.c | 226 +++++++++++++++++++++++++++++------ 2 files changed, 193 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 1d18534..a6edbb6 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -3,6 +3,7 @@ config DRM_EXYNOS depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || ARCH_MULTIPLATFORM) select DRM_KMS_HELPER select VIDEOMODE_HELPERS + select SND_SOC_HDMI_CODEC if SND_SOC help Choose this option if you have a Samsung SoC EXYNOS chipset. If M is selected the module will be called exynosdrm. diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 06bfbe4..64eadf58 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -40,7 +40,7 @@ #include #include #include - +#include #include #include @@ -111,12 +111,19 @@ struct hdmi_driver_data { struct string_array_spec clk_muxes; }; +struct hdmi_audio { + struct platform_device *pdev; + struct hdmi_audio_infoframe infoframe; + unsigned int sample_rate; + unsigned int sample_width; + u8 enable; +}; + struct hdmi_context { struct drm_encoder encoder; struct device *dev; struct drm_device *drm_dev; struct drm_connector connector; - bool powered; bool dvi_mode; struct delayed_work hotplug_work; struct drm_display_mode current_mode; @@ -137,6 +144,11 @@ struct hdmi_context { struct regulator *reg_hdmi_en; struct exynos_drm_clk phy_clk; struct drm_bridge *bridge; + + /* mutex protecting subsequent fields below */ + struct mutex mutex; + struct hdmi_audio audio; + bool powered; }; static inline struct hdmi_context *encoder_to_hdmi(struct drm_encoder *e) @@ -768,6 +780,22 @@ static int hdmi_clk_set_parents(struct hdmi_context *hdata, bool to_phy) return ret; } +static int hdmi_audio_infoframe_apply(struct hdmi_context *hdata) +{ + struct hdmi_audio_infoframe *infoframe = &hdata->audio.infoframe; + u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)]; + int len; + + len = hdmi_audio_infoframe_pack(infoframe, buf, sizeof(buf)); + if (len < 0) + return len; + + hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC); + hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, len); + + return 0; +} + static void hdmi_reg_infoframes(struct hdmi_context *hdata) { union hdmi_infoframe frm; @@ -805,15 +833,7 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata) hdmi_reg_write_buf(hdata, HDMI_VSI_DATA(0), buf + 3, ret - 3); } - ret = hdmi_audio_infoframe_init(&frm.audio); - if (!ret) { - frm.audio.channels = 2; - ret = hdmi_audio_infoframe_pack(&frm.audio, buf, sizeof(buf)); - } - if (ret > 0) { - hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_EVERY_VSYNC); - hdmi_reg_write_buf(hdata, HDMI_AUI_HEADER0, buf, ret); - } + hdmi_audio_infoframe_apply(hdata); } static enum drm_connector_status hdmi_detect(struct drm_connector *connector, @@ -996,23 +1016,18 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u32 freq) hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4); } -static void hdmi_audio_init(struct hdmi_context *hdata) +static void hdmi_audio_config(struct hdmi_context *hdata) { - u32 sample_rate, bits_per_sample; - u32 data_num, bit_ch, sample_frq; - u32 val; + u32 data_num, sample_freq, val; + u32 bit_ch = 1; - sample_rate = 44100; - bits_per_sample = 16; - switch (bits_per_sample) { + switch (hdata->audio.sample_width) { case 20: data_num = 2; - bit_ch = 1; break; case 24: data_num = 3; - bit_ch = 1; break; default: data_num = 1; @@ -1020,7 +1035,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata) break; } - hdmi_reg_acr(hdata, sample_rate); + hdmi_reg_acr(hdata, hdata->audio.sample_rate); hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE @@ -1031,10 +1046,21 @@ static void hdmi_audio_init(struct hdmi_context *hdata) hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN); - sample_frq = (sample_rate == 44100) ? 0 : - (sample_rate == 48000) ? 2 : - (sample_rate == 32000) ? 3 : - (sample_rate == 96000) ? 0xa : 0x0; + switch(hdata->audio.sample_rate) { + case 32000: + sample_freq = 0x3; + break; + case 48000: + sample_freq = 0x2; + break; + case 96000: + sample_freq = 0xa; + break; + case 44100: + default: + sample_freq = 0; + break; + } hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS); hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN); @@ -1068,7 +1094,7 @@ static void hdmi_audio_init(struct hdmi_context *hdata) hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER); hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0)); hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2 - | HDMI_I2S_SET_SMP_FREQ(sample_frq)); + | HDMI_I2S_SET_SMP_FREQ(sample_freq)); hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4, HDMI_I2S_ORG_SMP_FREQ_44_1 | HDMI_I2S_WORD_LEN_MAX24_24BITS @@ -1077,13 +1103,15 @@ static void hdmi_audio_init(struct hdmi_context *hdata) hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD); } -static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff) +static void hdmi_audio_control(struct hdmi_context *hdata) { + bool enable = hdata->audio.enable; + if (hdata->dvi_mode) return; - hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0); - hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ? + hdmi_reg_writeb(hdata, HDMI_AUI_CON, enable ? 2 : 0); + hdmi_reg_writemask(hdata, HDMI_CON_0, enable ? HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK); } @@ -1403,9 +1431,9 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) { hdmi_start(hdata, false); hdmi_conf_init(hdata); - hdmi_audio_init(hdata); + hdmi_audio_config(hdata); hdmi_mode_apply(hdata); - hdmi_audio_control(hdata, true); + hdmi_audio_control(hdata); } static void hdmi_mode_set(struct drm_encoder *encoder, @@ -1479,17 +1507,24 @@ static void hdmi_enable(struct drm_encoder *encoder) { struct hdmi_context *hdata = encoder_to_hdmi(encoder); + mutex_lock(&hdata->mutex); + hdmiphy_enable(hdata); hdmi_conf_apply(hdata); + + mutex_unlock(&hdata->mutex); } static void hdmi_disable(struct drm_encoder *encoder) { struct hdmi_context *hdata = encoder_to_hdmi(encoder); - if (!hdata->powered) - return; + mutex_lock(&hdata->mutex); + if (!hdata->powered) { + mutex_unlock(&hdata->mutex); + return; + } /* * The SFRs of VP and Mixer are updated by Vertical Sync of * Timing generator which is a part of HDMI so the sequence @@ -1503,6 +1538,8 @@ static void hdmi_disable(struct drm_encoder *encoder) cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID); hdmiphy_disable(hdata); + + mutex_unlock(&hdata->mutex); } static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = { @@ -1516,6 +1553,109 @@ static void hdmi_disable(struct drm_encoder *encoder) .destroy = drm_encoder_cleanup, }; +static void hdmi_audio_shutdown(struct device *dev, void *data) +{ + struct hdmi_context *hdata = dev_get_drvdata(dev); + + mutex_lock(&hdata->mutex); + + hdata->audio.enable = false; + + if (hdata->powered) + hdmi_audio_control(hdata); + + mutex_unlock(&hdata->mutex); +} + +static int hdmi_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct hdmi_context *hdata = dev_get_drvdata(dev); + + if (daifmt->fmt != HDMI_I2S || daifmt->bit_clk_inv || + daifmt->frame_clk_inv || daifmt->bit_clk_master || + daifmt->frame_clk_master) { + dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__, + daifmt->bit_clk_inv, daifmt->frame_clk_inv, + daifmt->bit_clk_master, + daifmt->frame_clk_master); + return -EINVAL; + } + + mutex_lock(&hdata->mutex); + + hdata->audio.sample_rate = params->sample_rate; + hdata->audio.sample_width = params->sample_width; + hdata->audio.infoframe = params->cea; + + if (hdata->powered) { + hdmi_audio_config(hdata); + hdmi_audio_infoframe_apply(hdata); + } + + mutex_unlock(&hdata->mutex); + + return 0; +} + +static int hdmi_audio_digital_mute(struct device *dev, void *data, bool mute) +{ + struct hdmi_context *hdata = dev_get_drvdata(dev); + + mutex_lock(&hdata->mutex); + + hdata->audio.enable = !mute; + + if (hdata->powered) + hdmi_audio_control(hdata); + + mutex_unlock(&hdata->mutex); + + return 0; +} + +static int hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, + size_t len) +{ + struct hdmi_context *hdata = dev_get_drvdata(dev); + struct drm_connector *connector = &hdata->connector; + + memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); + + return 0; +} + +static const struct hdmi_codec_ops audio_codec_ops = { + .hw_params = hdmi_audio_hw_params, + .audio_shutdown = hdmi_audio_shutdown, + .digital_mute = hdmi_audio_digital_mute, + .get_eld = hdmi_audio_get_eld, +}; + +static int hdmi_register_audio_device(struct hdmi_context *hdata) +{ + struct hdmi_codec_pdata codec_data = { + .ops = &audio_codec_ops, + .max_i2s_channels = 6, + .i2s = 1, + }; + + hdata->audio.pdev = platform_device_register_data( + hdata->dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, + &codec_data, sizeof(codec_data)); + + if (IS_ERR(hdata->audio.pdev)) + return PTR_ERR(hdata->audio.pdev); + + return 0; +} + +static void hdmi_unregister_audio_device(struct hdmi_context *hdata) +{ + platform_device_unregister(hdata->audio.pdev); +} + static void hdmi_hotplug_work_func(struct work_struct *work) { struct hdmi_context *hdata; @@ -1700,6 +1840,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) struct drm_device *drm_dev = data; struct hdmi_context *hdata = dev_get_drvdata(dev); struct drm_encoder *encoder = &hdata->encoder; + struct hdmi_audio_infoframe *audio_infoframe = &hdata->audio.infoframe; struct exynos_drm_crtc *exynos_crtc; struct drm_crtc *crtc; int ret, pipe; @@ -1721,6 +1862,12 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); + hdmi_audio_infoframe_init(audio_infoframe); + audio_infoframe->coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; + audio_infoframe->sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; + audio_infoframe->sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + audio_infoframe->channels = 2; + drm_encoder_init(drm_dev, encoder, &exynos_hdmi_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); @@ -1828,6 +1975,8 @@ static int hdmi_probe(struct platform_device *pdev) hdata->dev = dev; + mutex_init(&hdata->mutex); + ret = hdmi_resources_init(hdata); if (ret) { if (ret != -EPROBE_DEFER) @@ -1887,12 +2036,19 @@ static int hdmi_probe(struct platform_device *pdev) pm_runtime_enable(dev); - ret = component_add(&pdev->dev, &hdmi_component_ops); + ret = hdmi_register_audio_device(hdata); if (ret) goto err_notifier_put; + ret = component_add(&pdev->dev, &hdmi_component_ops); + if (ret) + goto err_unregister_audio; + return ret; +err_unregister_audio: + hdmi_unregister_audio_device(hdata); + err_notifier_put: cec_notifier_put(hdata->notifier); pm_runtime_disable(dev); @@ -1931,6 +2087,8 @@ static int hdmi_remove(struct platform_device *pdev) put_device(&hdata->ddc_adpt->dev); + mutex_destroy(&hdata->mutex); + return 0; }