From patchwork Fri Apr 21 17:19:48 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: 9693415 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 AA1F26038D for ; Fri, 21 Apr 2017 18:34:03 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9F0C228654 for ; Fri, 21 Apr 2017 18:34:03 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 93B5228655; Fri, 21 Apr 2017 18:34:03 +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=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2CC472865C for ; Fri, 21 Apr 2017 18:34:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1041967AbdDUSd6 (ORCPT ); Fri, 21 Apr 2017 14:33:58 -0400 Received: from mailout4.samsung.com ([203.254.224.34]:50715 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1041947AbdDUSdq (ORCPT ); Fri, 21 Apr 2017 14:33:46 -0400 Received: from epcas5p2.samsung.com (unknown [182.195.41.40]) by mailout4.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0OOR01J7NS6BGH10@mailout4.samsung.com>; Sat, 22 Apr 2017 02:20:35 +0900 (KST) Received: from epsmges5p2.samsung.com (unknown [182.195.42.45]) by epcas5p4.samsung.com (KnoxPortal) with ESMTP id 20170421172035epcas5p46bfd1c7fe8a9ed88468597f5e8037d91~3eVt5YzXq2392223922epcas5p4v; Fri, 21 Apr 2017 17:20:35 +0000 (GMT) Received: from epcas5p3.samsung.com ( [182.195.41.41]) by epsmges5p2.samsung.com (EPCPMTA) with SMTP id 98.F9.21921.36F3AF85; Sat, 22 Apr 2017 02:20:35 +0900 (KST) Received: from epcpsbgm2new.samsung.com (u27.gpu120.samsung.co.kr [203.254.230.27]) by epcas5p1.samsung.com (KnoxPortal) with ESMTP id 20170421172034epcas5p1dafc1794a7649ec30053f7f62d1831e5~3eVtQzDhs3035730357epcas5p1o; Fri, 21 Apr 2017 17:20:34 +0000 (GMT) X-AuditID: b6c32a2d-f792a6d0000055a1-62-58fa3f63f1fe Received: from epmmp1.local.host ( [203.254.227.16]) by epcpsbgm2new.samsung.com (EPCPMTA) with SMTP id 06.F8.05013.26F3AF85; Sat, 22 Apr 2017 02:20:34 +0900 (KST) Received: from AMDC3061.digital.local ([106.116.147.40]) by mmp1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0OOR00KJBS560G40@mmp1.samsung.com>; Sat, 22 Apr 2017 02:20:34 +0900 (KST) From: Sylwester Nawrocki To: linux-samsung-soc@vger.kernel.org, linux-clk@vger.kernel.org, dri-devel@lists.freedesktop.org, alsa-devel@alsa-project.org, devicetree@vger.kernel.org Cc: inki.dae@samsung.com, sw0312.kim@samsung.com, cw00.choi@samsung.com, javier@osg.samsung.com, krzk@kernel.org, jy0922.shim@samsung.com, broonie@kernel.org, robh+dt@kernel.org, b.zolnierkie@samsung.com, Sylwester Nawrocki Subject: [PATCH RFC 4/7] drm: exynos: Add driver for HDMI audio interface Date: Fri, 21 Apr 2017 19:19:48 +0200 Message-id: <1492795191-31298-5-git-send-email-s.nawrocki@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1492795191-31298-1-git-send-email-s.nawrocki@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrOIsWRmVeSWpSXmKPExsWy7bCmpm6y/a8IgxvfeS2uXDzEZLFxxnpW i6kPn7BZXP/ynNVi/pFzrBZXvr5ns5h0fwKLxZu3a5gsXty7yGJx/vwGdouPPfdYLWac38dk 0br3CLvF4TftQO7kl2wO/B4bPjexeWxa1cnmcb/7OJPHlv677B59W1YxenzeJBfAFsVlk5Ka k1mWWqRvl8CVseZMK1vBqcSK/TNOMDYwdgd2MXJySAiYSEz/PoEZwhaTuHBvPRuILSSwlFHi 0TW5LkYuILudSWLGuf2sXYwcYA0vlihBxJczSqy4s4kVwvnFKNHw9gcTSDebgKFE79E+RpCE iMA8Rolr2yawgTjMAv8YJaZdmMwOUiUs4Cnx6XY7K4jNIqAqcX5fP5jNK+AmsbBpH9RNchIn j00GW80p4C5x8Sk7yBwJgV3sEqcm3GaBOElWYtMBZgjTReLna2WITmGJV8e3sEPY0hJ/l95i hGjtZ5Q4saYZypnBKHGnfQITRJW1xOHjF8FuYBbgk+j9/YQJYiivREebEESJh8Sl041Q5Y4S l1+tZIcE1yxGiVlXbCcwyixgZFjFKJZaUJybnlpsWmCkV5yYW1yal66XnJ+7iRGcHLR0dzB+ WeB9iFGAg1GJh3cFy68IIdbEsuLK3EOMEhzMSiK83vpAId6UxMqq1KL8+KLSnNTiQ4zSHCxK 4rzqK69FCAmkJ5akZqemFqQWwWSZODilGhj31h+Te7vx1LFfJkcn33JO+zKr3/n1Pn1+pT9e iuVOS+ff7pl5V43tyCEVtju+Dn+zLt2o8Elq7bis/+T1gUwWEf10dgHldCHDRVovFloFezbo FHoyPT23UXXrVLbzNQsfObnUfmnTSb6ql8t6VuakpbLckUtmzu73eYzzcjS9g65WbTN3vKbE UpyRaKjFXFScCAAp9EQ/CgMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprFIsWRmVeSWpSXmKPExsVy+t9jAd0k+18RBsc6lSyuXDzEZLFxxnpW i6kPn7BZXP/ynNVi/pFzrBZXvr5ns5h0fwKLxZu3a5gsXty7yGJx/vwGdouPPfdYLWac38dk 0br3CLvF4TftQO7kl2wO/B4bPjexeWxa1cnmcb/7OJPHlv677B59W1YxenzeJBfAFuVmk5Ga mJJapJCal5yfkpmXbqsUGuKma6GkkJeYm2qrFKHrGxKkpFCWmFMK5BkZoAEH5wD3YCV9uwS3 jDVnWtkKTiVW7J9xgrGBsTuwi5GDQ0LAROLFEqUuRk4gU0ziwr31bF2MXBxCAksZJV6vXsQC 4fxilHix/i4bSBWbgKFE79E+RpCEiMA8RokNJ9eCOcwC/xgljjX1s4BUCQt4Sny63c4KYrMI qEqc39cPZvMKuEksbNrHDLFPTuLkscmsIGdwCrhLXHzKDhIWAipZ2n+ZdQIj7wJGhlWMEqkF yQXFSem5Rnmp5XrFibnFpXnpesn5uZsYwfHyTHoH4+Fd7ocYBTgYlXh4V7D8ihBiTSwrrsw9 xCjBwawkwuutDxTiTUmsrEotyo8vKs1JLT7EaAp010RmKdHkfGAs55XEG5qYm5gbG1iYW1qa GCmJ8zbOfhYuJJCeWJKanZpakFoE08fEwSnVwBj/QqlonuztWXP3evtOyj2uUfPmUXWa9cdA vxmbl847f61t8aWNt5ZuYdN6o9ZbWzr/fen+ozcveW2JluzXthFe3rJVlGkBf27o4owDSRuX +VYd8slO4M88Z7AmUcpx7Z7Ih/wRcr4LBHRizcylrovk/L1Zdi1h5lSvpBMXlHfG6Id77P9j OV2JpTgj0VCLuag4EQCZH3EurQIAAA== X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20170421172034epcas5p1dafc1794a7649ec30053f7f62d1831e5 X-Msg-Generator: CA X-Sender-IP: 203.254.230.27 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: 105P X-HopCount: 7 X-CMS-RootMailID: 20170421172034epcas5p1dafc1794a7649ec30053f7f62d1831e5 X-RootMTR: 20170421172034epcas5p1dafc1794a7649ec30053f7f62d1831e5 References: <1492795191-31298-1-git-send-email-s.nawrocki@samsung.com> Sender: linux-clk-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-clk@vger.kernel.org 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 --- drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_hdmi.c | 220 +++++++++++++++++++++++++++++------ 2 files changed, 188 insertions(+), 33 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 88ccc04..be18023 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 "exynos_drm_drv.h" @@ -110,13 +110,23 @@ 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; + struct hdmi_audio audio; bool powered; bool dvi_mode; + struct mutex mutex; struct delayed_work hotplug_work; struct drm_display_mode current_mode; const struct hdmi_driver_data *drv_data; @@ -766,6 +776,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; @@ -803,15 +829,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, @@ -993,23 +1011,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; @@ -1017,7 +1030,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 @@ -1028,10 +1041,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); @@ -1065,7 +1089,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 @@ -1074,13 +1098,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); } @@ -1400,9 +1426,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, @@ -1476,8 +1502,12 @@ 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) @@ -1486,6 +1516,8 @@ static void hdmi_disable(struct drm_encoder *encoder) struct drm_crtc *crtc = encoder->crtc; const struct drm_crtc_helper_funcs *funcs = NULL; + mutex_lock(&hdata->mutex); + if (!hdata->powered) return; @@ -1506,6 +1538,8 @@ static void hdmi_disable(struct drm_encoder *encoder) cancel_delayed_work(&hdata->hotplug_work); hdmiphy_disable(hdata); + + mutex_unlock(&hdata->mutex); } static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = { @@ -1519,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; @@ -1703,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; int ret, pipe; hdata->drm_dev = drm_dev; @@ -1720,6 +1858,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); @@ -1822,11 +1966,11 @@ static int hdmi_probe(struct platform_device *pdev) return -ENOMEM; hdata->drv_data = of_device_get_match_data(dev); - platform_set_drvdata(pdev, hdata); - hdata->dev = dev; + mutex_init(&hdata->mutex); + ret = hdmi_resources_init(hdata); if (ret) { if (ret != -EPROBE_DEFER) @@ -1880,12 +2024,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_disable_pm_runtime; + 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_disable_pm_runtime: pm_runtime_disable(dev); @@ -1906,6 +2057,7 @@ static int hdmi_remove(struct platform_device *pdev) cancel_delayed_work_sync(&hdata->hotplug_work); + hdmi_unregister_audio_device(hdata); component_del(&pdev->dev, &hdmi_component_ops); pm_runtime_disable(&pdev->dev); @@ -1921,6 +2073,8 @@ static int hdmi_remove(struct platform_device *pdev) put_device(&hdata->ddc_adpt->dev); + mutex_destroy(&hdata->mutex); + return 0; }