From patchwork Thu Oct 1 16:50:20 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnaud POULIQUEN X-Patchwork-Id: 7313371 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 5C96FBEEA4 for ; Fri, 2 Oct 2015 06:29:03 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 721E3208EA for ; Fri, 2 Oct 2015 06:29:01 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 9FE7C208E5 for ; Fri, 2 Oct 2015 06:28:59 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 44BF86EDB4; Thu, 1 Oct 2015 23:28:56 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mx07-00178001.pphosted.com (mx07-00178001.pphosted.com [62.209.51.94]) by gabe.freedesktop.org (Postfix) with ESMTPS id 650516E4D4 for ; Thu, 1 Oct 2015 09:51:44 -0700 (PDT) Received: from pps.filterd (m0046668.ppops.net [127.0.0.1]) by mx07-00178001.pphosted.com (8.14.5/8.14.5) with SMTP id t91GjsOQ006048; Thu, 1 Oct 2015 18:51:04 +0200 Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx07-00178001.pphosted.com with ESMTP id 1x8edrnwx7-1 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NOT); Thu, 01 Oct 2015 18:51:04 +0200 Received: from zeta.dmz-eu.st.com (zeta.dmz-eu.st.com [164.129.230.9]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id DD2F734; Thu, 1 Oct 2015 16:50:44 +0000 (GMT) Received: from Webmail-eu.st.com (Safex1hubcas21.st.com [10.75.90.44]) by zeta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 19910A821; Thu, 1 Oct 2015 16:51:03 +0000 (GMT) Received: from localhost (10.201.23.162) by Webmail-ga.st.com (10.75.90.48) with Microsoft SMTP Server (TLS) id 14.3.224.2; Thu, 1 Oct 2015 18:51:02 +0200 From: Arnaud Pouliquen To: , Subject: [PATCH RFC V2 4/5] drm: sti: connect audio driver Date: Thu, 1 Oct 2015 18:50:20 +0200 Message-ID: <1443718221-5120-5-git-send-email-arnaud.pouliquen@st.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1443718221-5120-1-git-send-email-arnaud.pouliquen@st.com> References: <1443718221-5120-1-git-send-email-arnaud.pouliquen@st.com> MIME-Version: 1.0 X-Originating-IP: [10.201.23.162] X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:5.14.151, 1.0.33, 0.0.0000 definitions=2015-10-01_06:2015-10-01, 2015-10-01, 1970-01-01 signatures=0 X-Mailman-Approved-At: Thu, 01 Oct 2015 23:28:55 -0700 Cc: linux@arm.linux.org.uk, bcousson@baylibre.com, tomi.valkeinen@ti.com, arnaud.pouliquen@st.com, lgirdwood@gmail.com, Jyri Sarha , peter.ujfalusi@ti.com, tony@atomide.com, broonie@kernel.org, Benjamin Gaignard 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: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Registeur Asoc codec and implement audio bridge ops. Signed-off-by: Arnaud Pouliquen --- drivers/gpu/drm/sti/sti_hdmi.c | 184 ++++++++++++++++++++++++++++++++++++++--- drivers/gpu/drm/sti/sti_hdmi.h | 3 + 2 files changed, 176 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 06595e9..cf0e307 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -17,6 +17,8 @@ #include #include +#include + #include "sti_hdmi.h" #include "sti_hdmi_tx3g4c28phy.h" #include "sti_hdmi_tx3g0c55phy.h" @@ -34,6 +36,8 @@ #define HDMI_DFLT_CHL0_DAT 0x0110 #define HDMI_DFLT_CHL1_DAT 0x0114 #define HDMI_DFLT_CHL2_DAT 0x0118 +#define HDMI_AUDIO_CFG 0x0200 +#define HDMI_SPDIF_FIFO_STATUS 0x0204 #define HDMI_SW_DI_1_HEAD_WORD 0x0210 #define HDMI_SW_DI_1_PKT_WORD0 0x0214 #define HDMI_SW_DI_1_PKT_WORD1 0x0218 @@ -43,6 +47,9 @@ #define HDMI_SW_DI_1_PKT_WORD5 0x0228 #define HDMI_SW_DI_1_PKT_WORD6 0x022C #define HDMI_SW_DI_CFG 0x0230 +#define HDMI_SAMPLE_FLAT_MASK 0x0244 +#define HDMI_AUDN 0x0400 +#define HDMI_AUD_CTS 0x0404 #define HDMI_SW_DI_2_HEAD_WORD 0x0600 #define HDMI_SW_DI_2_PKT_WORD0 0x0604 #define HDMI_SW_DI_2_PKT_WORD1 0x0608 @@ -109,6 +116,29 @@ #define HDMI_STA_SW_RST BIT(1) +#define HDMI_AUD_CFG_8CH BIT(0) +#define HDMI_AUD_CFG_SPDIF_DIV_2 BIT(1) +#define HDMI_AUD_CFG_SPDIF_DIV_3 BIT(2) +#define HDMI_AUD_CFG_SPDIF_CLK_DIV_4 (BIT(1) | BIT(2)) +#define HDMI_AUD_CFG_CTS_CLK_256FS BIT(12) +#define HDMI_AUD_CFG_DTS_INVALID BIT(16) +#define HDMI_AUD_CFG_ONE_BIT_INVALID (BIT(18) | BIT(19) | BIT(20) | BIT(21)) +#define HDMI_AUD_CFG_CH12_VALID BIT(28) +#define HDMI_AUD_CFG_CH34_VALID BIT(29) +#define HDMI_AUD_CFG_CH56_VALID BIT(30) +#define HDMI_AUD_CFG_CH78_VALID BIT(31) + +/* sample flat mask */ +#define HDMI_SAMPLE_FLAT_NO 0 +#define HDMI_SAMPLE_FLAT_SP0 BIT(0) +#define HDMI_SAMPLE_FLAT_SP1 BIT(1) +#define HDMI_SAMPLE_FLAT_SP2 BIT(2) +#define HDMI_SAMPLE_FLAT_SP3 BIT(3) +#define HDMI_SAMPLE_FLAT_ALL (HDMI_SAMPLE_FLAT_SP0 \ + | HDMI_SAMPLE_FLAT_SP1 \ + | HDMI_SAMPLE_FLAT_SP2 \ + | HDMI_SAMPLE_FLAT_SP3) + #define HDMI_INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0) #define HDMI_INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8) #define HDMI_INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16) @@ -380,19 +410,15 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) */ static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi) { - struct hdmi_audio_infoframe infofame; + struct hdmi_audio_infoframe *infoframe; u8 buffer[HDMI_INFOFRAME_SIZE(AUDIO)]; int ret; - ret = hdmi_audio_infoframe_init(&infofame); - if (ret < 0) { - DRM_ERROR("failed to setup audio infoframe: %d\n", ret); - return ret; - } + DRM_DEBUG_DRIVER("enter %s\n", __func__); - infofame.channels = 2; + infoframe = &hdmi->audio.infoframe; - ret = hdmi_audio_infoframe_pack(&infofame, buffer, sizeof(buffer)); + ret = hdmi_audio_infoframe_pack(infoframe, buffer, sizeof(buffer)); if (ret < 0) { DRM_ERROR("failed to pack audio infoframe: %d\n", ret); return ret; @@ -404,6 +430,60 @@ static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi) } /** + * set audio frame rate + * + * @hdmi: pointer on the hdmi internal structure + * + */ +static int hdmi_audio_set_infoframe(struct sti_hdmi *hdmi, + struct hdmi_audio_infoframe *info) +{ + struct hdmi_audio_n_cts n_cts; + int ret, audio_cfg; + + DRM_DEBUG_DRIVER("enter %s\n", __func__); + + hdmi->audio.infoframe = *info; + + if (!hdmi->enabled) + return 0; + + /* update HDMI registers according to configuration */ + audio_cfg = HDMI_AUD_CFG_SPDIF_DIV_2 | HDMI_AUD_CFG_DTS_INVALID | + HDMI_AUD_CFG_ONE_BIT_INVALID; + + switch (info->channels) { + case 8: + audio_cfg |= HDMI_AUD_CFG_CH78_VALID; + case 6: + audio_cfg |= HDMI_AUD_CFG_CH56_VALID; + case 4: + audio_cfg |= HDMI_AUD_CFG_CH34_VALID | HDMI_AUD_CFG_8CH; + case 2: + audio_cfg |= HDMI_AUD_CFG_CH12_VALID; + break; + default: + DRM_ERROR("ERROR: Unsupported number of channels (%d)!\n", + info->channels); + return -EINVAL; + } + + hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG); + + /* update N parameter */ + ret = hdmi_audio_compute_n_cts(info->sample_frequency, + hdmi->mode.clock, &n_cts); + + DRM_DEBUG_DRIVER("sample_frequency= %d, pix clock = %d\n", + info->sample_frequency, hdmi->mode.clock); + DRM_DEBUG_DRIVER("n= %d, cts = %d\n", n_cts.n, n_cts.cts); + + hdmi_write(hdmi, n_cts.n, HDMI_AUDN); + + return hdmi_audio_infoframe_config(hdmi); +} + +/** * Software reset of the hdmi subsystem * * @hdmi: pointer on the hdmi internal structure @@ -462,7 +542,6 @@ static void sti_hdmi_disable(struct drm_bridge *bridge) /* Disable HDMI */ val &= ~HDMI_CFG_DEVICE_EN; hdmi_write(hdmi, val, HDMI_CFG); - hdmi_write(hdmi, 0xffffffff, HDMI_INT_CLR); /* Stop the phy */ @@ -567,6 +646,53 @@ static const struct drm_bridge_funcs sti_hdmi_bridge_funcs = { .mode_set = sti_hdmi_set_mode, }; +static int sti_hdmi_audio_disable(struct drm_bridge *bridge) +{ + struct sti_hdmi *hdmi = bridge->driver_private; + + DRM_DEBUG_DRIVER("enter %s\n", __func__); + /* mute */ + hdmi_write(hdmi, HDMI_SAMPLE_FLAT_ALL, HDMI_SAMPLE_FLAT_MASK); + + return 0; +} + +static int sti_hdmi_audio_set_mode(struct drm_bridge *bridge, + struct hdmi_audio_mode *mode) +{ + struct sti_hdmi *hdmi = bridge->driver_private; + + DRM_DEBUG_DRIVER("enter %s\n", __func__); + hdmi_audio_set_infoframe(hdmi, &mode->infoframe); + + return 0; +} + +static int sti_hdmi_audio_bridge_enable(struct drm_bridge *bridge) +{ + struct sti_hdmi *hdmi = bridge->driver_private; + + DRM_DEBUG_DRIVER("enter %s\n", __func__); + /* unmute */ + hdmi_write(hdmi, HDMI_SAMPLE_FLAT_NO, HDMI_SAMPLE_FLAT_MASK); + + return 0; +} + +static uint8_t *sti_hdmi_audio_get_mode(struct drm_bridge *bridge) +{ + struct sti_hdmi *hdmi = bridge->driver_private; + + return hdmi->drm_connector->eld; +} + +static const struct drm_audio_bridge_funcs sti_hdmi_audio_bridge_funcs = { + .enable = sti_hdmi_audio_bridge_enable, + .disable = sti_hdmi_audio_disable, + .mode_set = sti_hdmi_audio_set_mode, + .mode_get = sti_hdmi_audio_get_mode, +}; + static int sti_hdmi_connector_get_modes(struct drm_connector *connector) { struct sti_hdmi_connector *hdmi_connector @@ -583,6 +709,7 @@ static int sti_hdmi_connector_get_modes(struct drm_connector *connector) count = drm_add_edid_modes(connector, edid); drm_mode_connector_update_edid_property(connector, edid); + drm_edid_to_eld(connector, edid); kfree(edid); return count; @@ -657,10 +784,13 @@ static void sti_hdmi_connector_destroy(struct drm_connector *connector) { struct sti_hdmi_connector *hdmi_connector = to_sti_hdmi_connector(connector); + struct sti_hdmi *hdmi = hdmi_connector->hdmi; + drm_bridge_remove(connector->encoder->bridge); drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(hdmi_connector); + hdmi->drm_connector = NULL; } static struct drm_connector_funcs sti_hdmi_connector_funcs = { @@ -698,6 +828,9 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) /* Set the drm device handle */ hdmi->drm_dev = drm_dev; + /* initialise audio infoframe */ + hdmi_audio_infoframe_init(&hdmi->audio.infoframe); + encoder = sti_hdmi_find_encoder(drm_dev); if (!encoder) goto err_adapt; @@ -705,8 +838,6 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL); if (!connector) goto err_adapt; - - connector->hdmi = hdmi; bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); @@ -715,8 +846,14 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) bridge->driver_private = hdmi; bridge->funcs = &sti_hdmi_bridge_funcs; + bridge->audio_funcs = &sti_hdmi_audio_bridge_funcs; drm_bridge_attach(drm_dev, bridge); + bridge->of_node = dev->of_node; + err = drm_bridge_add(bridge); + if (err) + goto err_adapt; + encoder->bridge = bridge; connector->encoder = encoder; @@ -733,6 +870,8 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) if (err) goto err_connector; + hdmi->drm_connector = drm_connector; + err = drm_mode_connector_attach_encoder(drm_connector, encoder); if (err) { DRM_ERROR("Failed to attach a connector to a encoder\n"); @@ -748,6 +887,8 @@ err_sysfs: drm_connector_unregister(drm_connector); err_connector: drm_connector_cleanup(drm_connector); + drm_bridge_remove(bridge); + hdmi->drm_connector = NULL; err_adapt: put_device(&hdmi->ddc_adapt->dev); return -EINVAL; @@ -777,6 +918,25 @@ static const struct of_device_id hdmi_of_match[] = { }; MODULE_DEVICE_TABLE(of, hdmi_of_match); +static void sti_hdmi_register_audio_driver(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *np_child; + struct platform_device *pdev; + + np_child = of_get_child_by_name(np, "sound-dai"); + if (!np_child) + return; + + pdev = platform_device_register_data(dev, + HDMI_DRM_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, NULL, 0); + if (IS_ERR(pdev)) + return; + + DRM_INFO("%s driver bound to HDMI\n", HDMI_DRM_CODEC_DRV_NAME); +} + static int sti_hdmi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -877,6 +1037,8 @@ static int sti_hdmi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hdmi); + sti_hdmi_register_audio_driver(dev); + return component_add(&pdev->dev, &sti_hdmi_ops); } diff --git a/drivers/gpu/drm/sti/sti_hdmi.h b/drivers/gpu/drm/sti/sti_hdmi.h index 3d22390..54bd824 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.h +++ b/drivers/gpu/drm/sti/sti_hdmi.h @@ -36,6 +36,7 @@ struct hdmi_phy_ops { * @clk_tmds: hdmi tmds clock * @clk_phy: hdmi phy clock * @clk_audio: hdmi audio clock + * @audio: hdmi audio state * @irq: hdmi interrupt number * @irq_status: interrupt status register * @phy_ops: phy start/stop operations @@ -55,6 +56,7 @@ struct sti_hdmi { struct clk *clk_tmds; struct clk *clk_phy; struct clk *clk_audio; + struct hdmi_audio_mode audio; int irq; u32 irq_status; struct hdmi_phy_ops *phy_ops; @@ -64,6 +66,7 @@ struct sti_hdmi { bool event_received; struct reset_control *reset; struct i2c_adapter *ddc_adapt; + struct drm_connector *drm_connector; }; u32 hdmi_read(struct sti_hdmi *hdmi, int offset);