From patchwork Mon Jan 27 08:48:54 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Francois Moine X-Patchwork-Id: 3565081 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.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 9E82BC02DC for ; Sat, 1 Feb 2014 17:33:12 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B90CB2027D for ; Sat, 1 Feb 2014 17:33:11 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id C4FB320270 for ; Sat, 1 Feb 2014 17:33:10 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 282F16C95; Sat, 1 Feb 2014 09:33:08 -0800 (PST) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from smtp2-g21.free.fr (smtp2-g21.free.fr [212.27.42.2]) by gabe.freedesktop.org (Postfix) with ESMTP id 00F406C95 for ; Sat, 1 Feb 2014 09:33:00 -0800 (PST) Received: from localhost (unknown [IPv6:2a01:e35:2f5c:9de0:212:bfff:fe1e:9ce4]) by smtp2-g21.free.fr (Postfix) with ESMTP id 6D53B4B0045; Sat, 1 Feb 2014 18:32:47 +0100 (CET) X-Mailbox-Line: From 1b15025671d9099863a3091346536e45891e4a26 Mon Sep 17 00:00:00 2001 Message-Id: <1b15025671d9099863a3091346536e45891e4a26.1391274628.git.moinejf@free.fr> In-Reply-To: References: From: Jean-Francois Moine Date: Mon, 27 Jan 2014 09:48:54 +0100 Subject: [PATCH v3 4/5] ASoC: tda998x: adjust the audio hw parameters from EDID To: alsa-devel@alsa-project.org Cc: Russell King - ARM Linux , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, broonie@kernel.org, linux-arm-kernel@lists.infradead.org X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: dri-devel-bounces@lists.freedesktop.org Errors-To: dri-devel-bounces@lists.freedesktop.org X-Spam-Status: No, score=-1.3 required=5.0 tests=BAYES_00, DATE_IN_PAST_96_XX, FREEMAIL_FROM,RCVD_IN_DNSWL_MED,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 The supported audio parameters are described in the EDID which is received by the HDMI transmitter from the connected screen. Use these ones to adjust the audio stream parameters. Signed-off-by: Jean-Francois Moine --- drivers/gpu/drm/i2c/tda998x_drv.c | 15 ++++++++ include/drm/i2c/tda998x.h | 1 + sound/soc/codecs/tda998x.c | 79 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 68f0b7b..b833fa5 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -47,6 +47,8 @@ struct tda998x_priv { wait_queue_head_t wq_edid; volatile int wq_edid_wait; struct drm_encoder *encoder; + + u8 *eld; }; #define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv) @@ -733,6 +735,14 @@ tda998x_configure_audio(struct tda998x_priv *priv, } /* tda998x codec interface */ +u8 *tda998x_audio_get_eld(struct i2c_client *client) +{ + struct tda998x_priv *priv = i2c_get_clientdata(client); + + return priv->eld; +} +EXPORT_SYMBOL_GPL(tda998x_audio_get_eld); + void tda998x_audio_update(struct i2c_client *client, int format, int port) @@ -1187,6 +1197,11 @@ tda998x_encoder_get_modes(struct drm_encoder *encoder, drm_mode_connector_update_edid_property(connector, edid); n = drm_add_edid_modes(connector, edid); priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid); + + /* keep the EDID as ELD for the audio subsystem */ + drm_edid_to_eld(connector, edid); + priv->eld = connector->eld; + kfree(edid); } diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h index 7e4806d..99387ae 100644 --- a/include/drm/i2c/tda998x.h +++ b/include/drm/i2c/tda998x.h @@ -27,6 +27,7 @@ struct tda998x_encoder_params { unsigned audio_sample_rate; }; +u8 *tda998x_audio_get_eld(struct i2c_client *client); void tda998x_audio_update(struct i2c_client *client, int format, int port); diff --git a/sound/soc/codecs/tda998x.c b/sound/soc/codecs/tda998x.c index 34d7086..0493163 100644 --- a/sound/soc/codecs/tda998x.c +++ b/sound/soc/codecs/tda998x.c @@ -64,10 +64,79 @@ static int tda_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct tda_priv *priv = snd_soc_codec_get_drvdata(dai->codec); + u8 *eld = NULL; + static unsigned rates_mask[] = { + SNDRV_PCM_RATE_32000, + SNDRV_PCM_RATE_44100, + SNDRV_PCM_RATE_48000, + SNDRV_PCM_RATE_88200, + SNDRV_PCM_RATE_96000, + SNDRV_PCM_RATE_176400, + SNDRV_PCM_RATE_192000, + }; /* memorize the used DAI */ priv->dai_id = dai->id; + /* get the ELD from the tda998x driver */ + if (!priv->i2c_client) + tda_get_encoder(priv); + if (priv->i2c_client) + eld = tda998x_audio_get_eld(priv->i2c_client); + + /* adjust the hw params from the ELD (EDID) */ + if (eld) { + struct snd_soc_dai_driver *dai_drv = dai->driver; + struct snd_soc_pcm_stream *stream = &dai_drv->playback; + u8 *sad; + int sad_count; + unsigned eld_ver, mnl, rates, rate_mask, i; + unsigned max_channels, fmt; + u64 formats; + + eld_ver = eld[0] >> 3; + if (eld_ver != 2 && eld_ver != 31) + return 0; + + mnl = eld[4] & 0x1f; + if (mnl > 16) + return 0; + + sad_count = eld[5] >> 4; + sad = eld + 20 + mnl; + + /* Start from the basic audio settings */ + max_channels = 2; + rates = 0; + fmt = 0; + while (sad_count--) { + switch (sad[0] & 0x78) { + case 0x08: /* PCM */ + max_channels = max(max_channels, (sad[0] & 7) + 1u); + rates |= sad[1]; + fmt |= sad[2] & 0x07; + break; + } + sad += 3; + } + + for (rate_mask = i = 0; i < ARRAY_SIZE(rates_mask); i++) + if (rates & 1 << i) + rate_mask |= rates_mask[i]; + formats = 0; + if (fmt & 1) + formats |= SNDRV_PCM_FMTBIT_S16_LE; + if (fmt & 2) + formats |= SNDRV_PCM_FMTBIT_S20_3LE; + if (fmt & 4) + formats |= SNDRV_PCM_FMTBIT_S24_LE; + + /* change the snd_soc_pcm_stream values of the driver */ + stream->rates = rate_mask; + stream->channels_max = max_channels; + stream->formats = formats; + } + /* start the TDA998x audio */ return tda_start_stop(priv); } @@ -182,9 +251,17 @@ static const struct snd_soc_codec_driver soc_codec_tda998x = { static int tda998x_dev_probe(struct platform_device *pdev) { + struct snd_soc_dai_driver *dai_drv; + + /* copy the DAI driver to a writable area */ + dai_drv = devm_kzalloc(&pdev->dev, sizeof(tda998x_dai), GFP_KERNEL); + if (!dai_drv) + return -ENOMEM; + memcpy(dai_drv, tda998x_dai, sizeof(tda998x_dai)); + return snd_soc_register_codec(&pdev->dev, &soc_codec_tda998x, - tda998x_dai, ARRAY_SIZE(tda998x_dai)); + dai_drv, ARRAY_SIZE(tda998x_dai)); } static int tda998x_dev_remove(struct platform_device *pdev)