From nobody Fri Sep 20 21:35:14 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3F4D4C7EE29 for ; Mon, 29 May 2023 14:37:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230047AbjE2OhR (ORCPT ); Mon, 29 May 2023 10:37:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38446 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229959AbjE2Ogw (ORCPT ); Mon, 29 May 2023 10:36:52 -0400 Received: from mail-ed1-x535.google.com (mail-ed1-x535.google.com [IPv6:2a00:1450:4864:20::535]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8B3B5ED for ; Mon, 29 May 2023 07:36:48 -0700 (PDT) Received: by mail-ed1-x535.google.com with SMTP id 4fb4d7f45d1cf-5149bdb59daso1945678a12.2 for ; Mon, 29 May 2023 07:36:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20221208.gappssmtp.com; s=20221208; t=1685371007; x=1687963007; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=y4Hg6vOJoPW8zpUD2u/XK34qVTid08A4C83SJzFRpwQ=; b=BcV/WNL2KjNRE3wehRbfjoSLyK1iGIHZWjKGa2M2/36fXqOioYuZudjwten0cg/3D6 vo5Gm25HDz1jpUZ0da5rWtQQLNbbEgrUUKKE380/rX4ukPx+Ck/VEVgsloa9aSM+mHpr XdQbFj8AADwS+nJs3jJ+gHVe+LgBNAJUQxItcWaOloF7AlLaZvKzkS3WhxhUa5ZEa/EF LqesKFZdNMOVcyI4v+tUa2uCoYgMEUp8hrr2QhNF9ewVm+RaGqArMLhgElvI9jpeKnbA JXe6ZB/cTEpTBteRYTOXeEXNkCtFU/un8XJ6gXXNhSsrEXO5UKPJYnM7KBE8uj9aGJkR RDwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1685371007; x=1687963007; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=y4Hg6vOJoPW8zpUD2u/XK34qVTid08A4C83SJzFRpwQ=; b=gvNwNl/iFn0nTwCzTxpM+uHfrR3wzlmWodKuH1XUwDXmlWuZ//XM4/ak36S+ISd2ua r5DqLY+jPTtqjHP/6+DPYpBO/kl1euxWUeLyI0v1SnNVXN1TJm9CobbhE2ks4U44HQsO EifwGXjJhWbcDSKXKCdlhqOYuvhti9R4nCx99HcrRvppw3H152K4YYA62xhdD9Eb3hi1 DHmjcg/JE7iDzvnwu9sc8OzNu+k5p0C6mlRbFwjt2kqX1XFO14V+NILWvHLiT14VSdf6 toTk8CHUVgDrlPSVjWCVpW2h1ps9oKYheS47bTIsiCPEQFi2MT7Cgn08QBBvS0i8hmdt V2nA== X-Gm-Message-State: AC+VfDx2CKhyVaQQcuStSJ+Xfzo3TXYhQtofHHtsOH5fv0fJk1wsRevb c4ybKJ2v+PVznubVyAThCGDk9Q== X-Google-Smtp-Source: ACHHUZ6GMwLwJZl/5hLoTYz4EyOEyKto+m7fsJGEyNBrdLSncOznqGbSpe3xwIsU4LQ+a2XSx8ArPA== X-Received: by 2002:a17:907:7244:b0:973:cc48:f19d with SMTP id ds4-20020a170907724400b00973cc48f19dmr11762300ejc.52.1685371006780; Mon, 29 May 2023 07:36:46 -0700 (PDT) Received: from [127.0.0.1] (abordeaux-655-1-129-86.w90-5.abo.wanadoo.fr. [90.5.10.86]) by smtp.gmail.com with ESMTPSA id le8-20020a170907170800b0096f803afbe3sm5993654ejc.66.2023.05.29.07.36.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 May 2023 07:36:46 -0700 (PDT) From: Guillaume Ranquet Date: Mon, 29 May 2023 16:31:03 +0200 Subject: [PATCH v4 6/8] drm/mediatek: hdmi: v2: add audio support MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20220919-v4-6-687f09a06dd9@baylibre.com> References: <20220919-v4-0-687f09a06dd9@baylibre.com> In-Reply-To: <20220919-v4-0-687f09a06dd9@baylibre.com> To: Chunfeng Yun , Kishon Vijay Abraham I , Vinod Koul , Rob Herring , Krzysztof Kozlowski , Matthias Brugger , Chun-Kuang Hu , Philipp Zabel , David Airlie , Daniel Vetter , CK Hu , Jitao shi Cc: linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Krzysztof Kozlowski , AngeloGioacchino Del Regno , mac.shen@mediatek.com, stuart.lee@mediatek.com, Guillaume Ranquet X-Mailer: b4 0.13-dev Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add HDMI audio support for v2 Signed-off-by: Guillaume Ranquet --- drivers/gpu/drm/mediatek/mtk_hdmi_common.c | 1 + drivers/gpu/drm/mediatek/mtk_hdmi_v2.c | 198 +++++++++++++++++++++++++= ++++ drivers/gpu/drm/mediatek/mtk_hdmi_v2.h | 4 +- 3 files changed, 202 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c b/drivers/gpu/drm/m= ediatek/mtk_hdmi_common.c index 97a00102a970..9ed86834ca85 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c @@ -346,6 +346,7 @@ static const struct mtk_hdmi_conf mtk_hdmi_conf_v2 =3D { .mtk_hdmi_output_init =3D mtk_hdmi_output_init_v2, .mtk_hdmi_clk_disable =3D mtk_hdmi_clk_disable_v2, .mtk_hdmi_clk_enable =3D mtk_hdmi_clk_enable_v2, + .mtk_hdmi_set_codec_pdata =3D mtk_hdmi_set_codec_pdata_v2, .mtk_hdmi_clock_names =3D mtk_hdmi_clk_names_v2, .num_clocks =3D MTK_HDMIV2_CLK_COUNT, }; diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/media= tek/mtk_hdmi_v2.c index 98b1d50ecd72..0e41dfb36db4 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c @@ -150,6 +150,24 @@ static void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdm= i, bool black) regmap_update_bits(hdmi->regs, TOP_VMUTE_CFG1, REG_VMUTE_EN, val); } =20 +static void mtk_hdmi_hw_aud_mute(struct mtk_hdmi *hdmi) +{ + u32 aip, val; + + regmap_read(hdmi->regs, AIP_CTRL, &aip); + + val =3D FIELD_PREP(AUD_MUTE_FIFO_EN, 1); + if (aip & DSD_EN) + val |=3D FIELD_PREP(DSD_MUTE_DATA, 1); + + regmap_update_bits(hdmi->regs, AIP_TXCTRL, val, val); +} + +static void mtk_hdmi_hw_aud_unmute(struct mtk_hdmi *hdmi) +{ + regmap_update_bits(hdmi->regs, AIP_TXCTRL, AUD_MUTE_FIFO_EN, AUD_MUTE_DIS= ); +} + static void mtk_hdmi_hw_reset(struct mtk_hdmi *hdmi) { regmap_update_bits(hdmi->regs, HDMITX_CONFIG, HDMITX_SW_RSTB, 0); @@ -766,6 +784,7 @@ static void mtk_hdmi_audio_reset(struct mtk_hdmi *hdmi,= bool rst) static void mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi, struct drm_display_mode *display_mode) { + mtk_hdmi_hw_aud_mute(hdmi); mtk_hdmi_aud_enable_packet(hdmi, false); mtk_hdmi_audio_reset(hdmi, true); mtk_hdmi_aip_ctrl_init(hdmi); @@ -778,6 +797,7 @@ static void mtk_hdmi_aud_output_config(struct mtk_hdmi = *hdmi, usleep_range(25, 50); mtk_hdmi_aud_on_off_hw_ncts(hdmi, true); mtk_hdmi_aud_enable_packet(hdmi, true); + mtk_hdmi_hw_aud_unmute(hdmi); } =20 void mtk_hdmi_output_init_v2(struct mtk_hdmi *hdmi) @@ -794,6 +814,16 @@ void mtk_hdmi_output_init_v2(struct mtk_hdmi *hdmi) hdmi->hpd =3D HDMI_PLUG_OUT; } =20 +static void mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi, + struct hdmi_audio_param *param) +{ + if (!hdmi->audio_enable) + return; + + memcpy(&hdmi->aud_param, param, sizeof(*param)); + mtk_hdmi_aud_output_config(hdmi, &hdmi->mode); +} + static void mtk_hdmi_change_video_resolution(struct mtk_hdmi *hdmi) { mtk_hdmi_hw_reset(hdmi); @@ -812,6 +842,7 @@ static void mtk_hdmi_change_video_resolution(struct mtk= _hdmi *hdmi) =20 usleep_range(5, 10); mtk_hdmi_hw_vid_black(hdmi, true); + mtk_hdmi_hw_aud_mute(hdmi); mtk_hdmi_hw_send_av_unmute(hdmi); =20 regmap_update_bits(hdmi->regs, TOP_CFG01, NULL_PKT_VSYNC_HIGH_EN | NULL_P= KT_EN, NULL_PKT_VSYNC_HIGH_EN); @@ -1022,6 +1053,7 @@ static void mtk_hdmi_bridge_disable(struct drm_bridge= *bridge, mtk_hdmi_hw_send_av_mute(hdmi); usleep_range(50000, 50050); mtk_hdmi_hw_vid_black(hdmi, true); + mtk_hdmi_hw_aud_mute(hdmi); mtk_hdmi_disable_hdcp_encrypt(hdmi); usleep_range(50000, 50050); =20 @@ -1030,6 +1062,14 @@ static void mtk_hdmi_bridge_disable(struct drm_bridg= e *bridge, hdmi->enabled =3D false; } =20 +static void mtk_hdmi_handle_plugged_change(struct mtk_hdmi *hdmi, bool plu= gged) +{ + mutex_lock(&hdmi->update_plugged_status_lock); + if (hdmi->plugged_cb && hdmi->codec_dev) + hdmi->plugged_cb(hdmi->codec_dev, plugged); + mutex_unlock(&hdmi->update_plugged_status_lock); +} + static void mtk_hdmi_bridge_post_disable(struct drm_bridge *bridge, struct drm_bridge_state *old_state) { @@ -1041,6 +1081,9 @@ static void mtk_hdmi_bridge_post_disable(struct drm_b= ridge *bridge, phy_power_off(hdmi->phy); =20 hdmi->powered =3D false; + + /* signal the disconnect event to audio codec */ + mtk_hdmi_handle_plugged_change(hdmi, false); } =20 static void mtk_hdmi_bridge_pre_enable(struct drm_bridge *bridge, @@ -1077,6 +1120,10 @@ static void mtk_hdmi_bridge_enable(struct drm_bridge= *bridge, mtk_hdmi_hw_avi_infoframe(hdmi, buffer_avi, sizeof(buffer_avi)); =20 mtk_hdmi_hw_vid_black(hdmi, false); + mtk_hdmi_hw_aud_unmute(hdmi); + + /* signal the connect event to audio codec */ + mtk_hdmi_handle_plugged_change(hdmi, true); =20 hdmi->enabled =3D true; } @@ -1103,3 +1150,154 @@ const struct drm_bridge_funcs mtk_v2_hdmi_bridge_fu= ncs =3D { .get_edid =3D mtk_hdmi_bridge_get_edid, .detect =3D mtk_hdmi_bridge_detect, }; + +static void mtk_hdmi_set_plugged_cb(struct mtk_hdmi *hdmi, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + bool plugged; + + mutex_lock(&hdmi->update_plugged_status_lock); + hdmi->plugged_cb =3D fn; + hdmi->codec_dev =3D codec_dev; + plugged =3D (hdmi->hpd =3D=3D HDMI_PLUG_IN_AND_SINK_POWER_ON); + mutex_unlock(&hdmi->update_plugged_status_lock); + + mtk_hdmi_handle_plugged_change(hdmi, plugged); +} + +/* + * HDMI audio codec callbacks + */ +static int mtk_hdmi_audio_hook_plugged_cb(struct device *dev, void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct mtk_hdmi *hdmi =3D dev_get_drvdata(dev); + + if (!hdmi) + return -ENODEV; + + mtk_hdmi_set_plugged_cb(hdmi, fn, codec_dev); + return 0; +} + +static int mtk_hdmi_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct mtk_hdmi *hdmi =3D dev_get_drvdata(dev); + struct hdmi_audio_param hdmi_params; + unsigned int chan =3D params->cea.channels; + + if (!hdmi->bridge.encoder) + return -ENODEV; + + switch (chan) { + case 2: + hdmi_params.aud_input_chan_type =3D HDMI_AUD_CHAN_TYPE_2_0; + break; + case 4: + hdmi_params.aud_input_chan_type =3D HDMI_AUD_CHAN_TYPE_4_0; + break; + case 6: + hdmi_params.aud_input_chan_type =3D HDMI_AUD_CHAN_TYPE_5_1; + break; + case 8: + hdmi_params.aud_input_chan_type =3D HDMI_AUD_CHAN_TYPE_7_1; + break; + default: + return -EINVAL; + } + + switch (params->sample_rate) { + case 32000: + case 44100: + case 48000: + case 88200: + case 96000: + case 176400: + case 192000: + break; + default: + return -EINVAL; + } + + switch (daifmt->fmt) { + case HDMI_I2S: + hdmi_params.aud_codec =3D HDMI_AUDIO_CODING_TYPE_PCM; + hdmi_params.aud_sampe_size =3D HDMI_AUDIO_SAMPLE_SIZE_16; + hdmi_params.aud_input_type =3D HDMI_AUD_INPUT_I2S; + hdmi_params.aud_i2s_fmt =3D HDMI_I2S_MODE_I2S_24BIT; + hdmi_params.aud_mclk =3D HDMI_AUD_MCLK_128FS; + break; + default: + return -EINVAL; + } + + memcpy(&hdmi_params.codec_params, params, + sizeof(hdmi_params.codec_params)); + + mtk_hdmi_audio_set_param(hdmi, &hdmi_params); + + return 0; +} + +static int mtk_hdmi_audio_startup(struct device *dev, void *data) +{ + struct mtk_hdmi *hdmi =3D dev_get_drvdata(dev); + + mtk_hdmi_aud_enable_packet(hdmi, true); + hdmi->audio_enable =3D true; + + return 0; +} + +static void mtk_hdmi_audio_shutdown(struct device *dev, void *data) +{ + struct mtk_hdmi *hdmi =3D dev_get_drvdata(dev); + + mtk_hdmi_aud_enable_packet(hdmi, false); + hdmi->audio_enable =3D false; +} + +static int mtk_hdmi_audio_mute(struct device *dev, void *data, bool enable, + int direction) +{ + struct mtk_hdmi *hdmi =3D dev_get_drvdata(dev); + + if (enable) + mtk_hdmi_hw_aud_mute(hdmi); + else + mtk_hdmi_hw_aud_unmute(hdmi); + + return 0; +} + +static int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t = *buf, + size_t len) +{ + struct mtk_hdmi *hdmi =3D dev_get_drvdata(dev); + + if (hdmi->enabled) + memcpy(buf, hdmi->curr_conn->eld, min(sizeof(hdmi->curr_conn->eld), len)= ); + else + memset(buf, 0, len); + return 0; +} + +static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops =3D { + .hw_params =3D mtk_hdmi_audio_hw_params, + .audio_startup =3D mtk_hdmi_audio_startup, + .audio_shutdown =3D mtk_hdmi_audio_shutdown, + .mute_stream =3D mtk_hdmi_audio_mute, + .get_eld =3D mtk_hdmi_audio_get_eld, + .hook_plugged_cb =3D mtk_hdmi_audio_hook_plugged_cb, +}; + +void mtk_hdmi_set_codec_pdata_v2(struct hdmi_codec_pdata *codec_data) +{ + codec_data->ops =3D &mtk_hdmi_audio_codec_ops; + codec_data->max_i2s_channels =3D 2; + codec_data->i2s =3D 1; +} diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.h b/drivers/gpu/drm/media= tek/mtk_hdmi_v2.h index 15d06d82eb8e..6b195a9c6cc8 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.h +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.h @@ -7,8 +7,9 @@ #ifndef _MTK_HDMI_V2_CTRL_H #define _MTK_HDMI_V2_CTRL_H =20 -#include #include +#include +#include =20 struct mtk_hdmi; =20 @@ -17,6 +18,7 @@ extern const struct drm_bridge_funcs mtk_v2_hdmi_bridge_f= uncs; void mtk_hdmi_output_init_v2(struct mtk_hdmi *hdmi); int mtk_hdmi_clk_enable_v2(struct mtk_hdmi *hdmi); void mtk_hdmi_clk_disable_v2(struct mtk_hdmi *hdmi); +void mtk_hdmi_set_codec_pdata_v2(struct hdmi_codec_pdata *codec_data); =20 enum mtk_hdmi_clk_id_v2 { MTK_HDMIV2_HDCP_SEL, --=20 2.40.0