From patchwork Wed Dec 14 17:20:39 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alban Bedel X-Patchwork-Id: 9477141 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 3EFC960825 for ; Fri, 16 Dec 2016 00:33:35 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3E8B228888 for ; Fri, 16 Dec 2016 00:33:35 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 331DB288A6; Fri, 16 Dec 2016 00:33:35 +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 2E4BA28888 for ; Fri, 16 Dec 2016 00:33:34 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 131216EAC1; Fri, 16 Dec 2016 00:32:45 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org X-Greylist: delayed 316 seconds by postgrey-1.35 at gabe; Wed, 14 Dec 2016 17:26:09 UTC Received: from mout.kundenserver.de (mout.kundenserver.de [217.72.192.73]) by gabe.freedesktop.org (Postfix) with ESMTPS id C655F6E875 for ; Wed, 14 Dec 2016 17:26:09 +0000 (UTC) Received: from mailbox.adnet.avionic-design.de ([109.75.18.3]) by mrelayeu.kundenserver.de (mreue103 [212.227.15.183]) with ESMTPSA (Nemesis) id 0MUClm-1c8rDJ0KbS-00R4hf; Wed, 14 Dec 2016 18:20:47 +0100 Received: from localhost (localhost [127.0.0.1]) by mailbox.adnet.avionic-design.de (Postfix) with ESMTP id BDC9A3A300E1; Wed, 14 Dec 2016 18:20:43 +0100 (CET) X-Virus-Scanned: amavisd-new at avionic-design.de Received: from mailbox.adnet.avionic-design.de ([127.0.0.1]) by localhost (mailbox.avionic-design.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ytDdLBhUVHuy; Wed, 14 Dec 2016 18:20:42 +0100 (CET) Received: from avionic-0020.adnet.avionic-design.de (avionic-0020.adnet.avionic-design.de [172.20.31.193]) by mailbox.adnet.avionic-design.de (Postfix) with ESMTP id 7E3693A30084; Wed, 14 Dec 2016 18:20:42 +0100 (CET) From: Alban Bedel To: Thierry Reding Subject: [PATCH] drm/tegra: hdmi: Fix audio to work with any pixel clock rate Date: Wed, 14 Dec 2016 18:20:39 +0100 Message-Id: <20161214172039.5615-1-alban.bedel@avionic-design.de> X-Mailer: git-send-email 2.11.0 X-Provags-ID: V03:K0:J4/0niQTLyVTK7/2NUCYsr+Soyl+EWqoAryI66qPNQNL4YEG+R4 M9HahKBrtW04kJWAlBrTv2BDtR8Hj+bHdP8wl97trS5f1u7e3oGa8gMP6eZIkiiMBgHp90W zppByDHc9wJO/jqaQHzFgfkjNow8RNpdsSIPFWX8d/1mqj9zPQxW87BzRwKi5DqdoJVOKNt ZFubQTICA4hMv5VJWcJFA== X-UI-Out-Filterresults: notjunk:1; V01:K0:XdW7/LU7BlA=:/HuD/BNNG6LbbaNgilG4nJ mtOTi/f95M/Z3SG5IGRgi/oc7K4N4FcYG5VnSIbjlAFGjx3NuR7vvmSKPD9ghVTwOqSIoskr3 YzBTm0fywjQnfV6eFpqUXEs+PAqW592RhWA7NNSUIW5hzY8d3EYNBhibfIVtfB9Su/OoRh8R5 3TCSxPWKx0CmS468iPP2mBI2NwbDm3bKcYrrCaEsmzZ50g2iKqqcEk2zxfaMDgAASnwxVfA8I hYeJizHG9DVbDsjdVLUbaOqbXSsVZrahirnUhBjXdtYpMNwjmzEGDUAypnGiurbJuVL0lmulz CXXN2oA1pOZ74X3iyS+kIzEMvpoanzch/NI7wghggbGhHKg69dKOOdeK25ruDolAvCgCbrncx WKc+UIk01HHS4LpCbtoJzxs+qiiiM+e3e613VHIv2bTJGTBBfeEuGvv2DpCXcZafaq0+1sdfs ydI0jvfJXATRGc+MFKB1Kkos5mkt3FWMwEmMQgezrhVKLg8DFL3HsH9+/o0UYFeCeMLvpFysp QM55hSWbUGM7Sl8Y09tBw95ozgGsosKSlsdwf6T5LadBJ3eFWoluNYwZqyeCJugP3Erd7SccP URm/4+lwpXnD+VeIWUomDSPHyuOIUIovYOrysTQhwn9vgc6m4a5eaAEYGIGfahYzqyYxzFLIF SVyTm6sfPV9v3FMVXLhI9ZnxXNk3WvV66orHTavOMDrA1+4oBCzxa/YnCTwp8gyYP6tg= X-Mailman-Approved-At: Fri, 16 Dec 2016 00:32:30 +0000 Cc: Alexandre Courbot , Stephen Warren , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-tegra@vger.kernel.org, Alban Bedel 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 audio setting implementation was limited to a few specific pixel clocks. This prevented HDMI audio from working on several test devices as they need a pixel clock that is not supported by this implementation. Fix this by implementing the algorithm provided in the TRM using fixed point arithmetic. This allows the driver to cope with any sane pixel clock rate. Signed-off-by: Alban Bedel --- drivers/gpu/drm/tegra/hdmi.c | 163 ++++++++++++++----------------------------- 1 file changed, 54 insertions(+), 109 deletions(-) diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index cda0491ed6bf..b6078d6604b1 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -112,68 +113,11 @@ static inline void tegra_hdmi_writel(struct tegra_hdmi *hdmi, u32 value, } struct tegra_hdmi_audio_config { - unsigned int pclk; unsigned int n; unsigned int cts; unsigned int aval; }; -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_32k[] = { - { 25200000, 4096, 25200, 24000 }, - { 27000000, 4096, 27000, 24000 }, - { 74250000, 4096, 74250, 24000 }, - { 148500000, 4096, 148500, 24000 }, - { 0, 0, 0, 0 }, -}; - -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_44_1k[] = { - { 25200000, 5880, 26250, 25000 }, - { 27000000, 5880, 28125, 25000 }, - { 74250000, 4704, 61875, 20000 }, - { 148500000, 4704, 123750, 20000 }, - { 0, 0, 0, 0 }, -}; - -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_48k[] = { - { 25200000, 6144, 25200, 24000 }, - { 27000000, 6144, 27000, 24000 }, - { 74250000, 6144, 74250, 24000 }, - { 148500000, 6144, 148500, 24000 }, - { 0, 0, 0, 0 }, -}; - -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_88_2k[] = { - { 25200000, 11760, 26250, 25000 }, - { 27000000, 11760, 28125, 25000 }, - { 74250000, 9408, 61875, 20000 }, - { 148500000, 9408, 123750, 20000 }, - { 0, 0, 0, 0 }, -}; - -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_96k[] = { - { 25200000, 12288, 25200, 24000 }, - { 27000000, 12288, 27000, 24000 }, - { 74250000, 12288, 74250, 24000 }, - { 148500000, 12288, 148500, 24000 }, - { 0, 0, 0, 0 }, -}; - -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_176_4k[] = { - { 25200000, 23520, 26250, 25000 }, - { 27000000, 23520, 28125, 25000 }, - { 74250000, 18816, 61875, 20000 }, - { 148500000, 18816, 123750, 20000 }, - { 0, 0, 0, 0 }, -}; - -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_192k[] = { - { 25200000, 24576, 25200, 24000 }, - { 27000000, 24576, 27000, 24000 }, - { 74250000, 24576, 74250, 24000 }, - { 148500000, 24576, 148500, 24000 }, - { 0, 0, 0, 0 }, -}; - static const struct tmds_config tegra20_tmds_config[] = { { /* slow pixel clock modes */ .pclk = 27000000, @@ -411,52 +355,49 @@ static const struct tmds_config tegra124_tmds_config[] = { }, }; -static const struct tegra_hdmi_audio_config * -tegra_hdmi_get_audio_config(unsigned int sample_rate, unsigned int pclk) +static int +tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pix_clock, + struct tegra_hdmi_audio_config *config) { - const struct tegra_hdmi_audio_config *table; - - switch (sample_rate) { - case 32000: - table = tegra_hdmi_audio_32k; - break; - - case 44100: - table = tegra_hdmi_audio_44_1k; - break; - - case 48000: - table = tegra_hdmi_audio_48k; - break; - - case 88200: - table = tegra_hdmi_audio_88_2k; - break; - - case 96000: - table = tegra_hdmi_audio_96k; - break; - - case 176400: - table = tegra_hdmi_audio_176_4k; - break; - - case 192000: - table = tegra_hdmi_audio_192k; - break; - - default: - return NULL; - } - - while (table->pclk) { - if (table->pclk == pclk) - return table; - - table++; + const int afreq = 128 * audio_freq; + const int min_n = afreq / 1500; + const int max_n = afreq / 300; + const int ideal_n = afreq / 1000; + int64_t min_err = (uint64_t)-1 >> 1; + int n; + + config->n = -1; + + for (n = min_n; n <= max_n; n++) { + uint64_t cts_f, aval_f; + int64_t cts, err; + + /* compute aval in 48.16 fixed point */ + aval_f = ((int64_t)24000000 << 16) * n; + do_div(aval_f, afreq); + /* It should round without any rest */ + if (aval_f & 0xFFFF) + continue; + + /* Compute cts in 48.16 fixed point */ + cts_f = ((int64_t)pix_clock << 16) * n; + do_div(cts_f, afreq); + /* Round it to the nearest integer */ + cts = (cts_f & ~0xFFFF) + ((cts_f & BIT(15)) << 1); + + /* Compute the absolute error */ + err = abs((int64_t)cts_f - cts); + if (err < min_err || + (err == min_err && + abs(n - ideal_n) < abs(config->n - ideal_n))) { + config->n = n; + config->cts = cts >> 16; + config->aval = aval_f >> 16; + min_err = err; + } } - return NULL; + return config->n != -1 ? 0 : -EINVAL; } static void tegra_hdmi_setup_audio_fs_tables(struct tegra_hdmi *hdmi) @@ -512,8 +453,9 @@ static void tegra_hdmi_write_aval(struct tegra_hdmi *hdmi, u32 value) static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) { - const struct tegra_hdmi_audio_config *config; + struct tegra_hdmi_audio_config config; u32 source, value; + int err; switch (hdmi->audio_source) { case HDA: @@ -588,25 +530,28 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); } - config = tegra_hdmi_get_audio_config(hdmi->audio_sample_rate, - hdmi->pixel_clock); - if (!config) { + err = tegra_hdmi_get_audio_config(hdmi->audio_sample_rate, + hdmi->pixel_clock, &config); + if (err) { dev_err(hdmi->dev, "cannot set audio to %u Hz at %u Hz pixel clock\n", hdmi->audio_sample_rate, hdmi->pixel_clock); - return -EINVAL; + return err; } + dev_dbg(hdmi->dev, "audio: pixclk=%d, n=%d, cts=%d, aval=%d\n", + hdmi->pixel_clock, config.n, config.cts, config.aval); + tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_HDMI_ACR_CTRL); value = AUDIO_N_RESETF | AUDIO_N_GENERATE_ALTERNATE | - AUDIO_N_VALUE(config->n - 1); + AUDIO_N_VALUE(config.n - 1); tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); - tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config->n) | ACR_ENABLE, + tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config.n) | ACR_ENABLE, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); - tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config->cts), + tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config.cts), HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); value = SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1); @@ -617,7 +562,7 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); if (hdmi->config->has_hda) - tegra_hdmi_write_aval(hdmi, config->aval); + tegra_hdmi_write_aval(hdmi, config.aval); tegra_hdmi_setup_audio_fs_tables(hdmi);