From patchwork Sun Dec 8 12:16:46 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jyri Sarha X-Patchwork-Id: 3306411 Return-Path: X-Original-To: patchwork-linux-omap@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id C2A229F37A for ; Sun, 8 Dec 2013 12:18:20 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id AA90520348 for ; Sun, 8 Dec 2013 12:18:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B67CA202FF for ; Sun, 8 Dec 2013 12:18:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753658Ab3LHMRu (ORCPT ); Sun, 8 Dec 2013 07:17:50 -0500 Received: from arroyo.ext.ti.com ([192.94.94.40]:36798 "EHLO arroyo.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753823Ab3LHMRl (ORCPT ); Sun, 8 Dec 2013 07:17:41 -0500 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id rB8CH6gG029939; Sun, 8 Dec 2013 06:17:06 -0600 Received: from DLEE70.ent.ti.com (dlee70.ent.ti.com [157.170.170.113]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id rB8CH6X0011785; Sun, 8 Dec 2013 06:17:06 -0600 Received: from dflp32.itg.ti.com (10.64.6.15) by DLEE70.ent.ti.com (157.170.170.113) with Microsoft SMTP Server id 14.2.342.3; Sun, 8 Dec 2013 06:17:06 -0600 Received: from localhost (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp32.itg.ti.com (8.14.3/8.13.8) with ESMTP id rB8CH5AK005441; Sun, 8 Dec 2013 06:17:06 -0600 From: Jyri Sarha To: , , , CC: , , , Jyri Sarha , Subject: [RFC v2 3/8] ASoC: davinci-evm: HDMI audio support for TDA998x trough McASP I2S bus Date: Sun, 8 Dec 2013 14:16:46 +0200 Message-ID: X-Mailer: git-send-email 1.7.9.5 In-Reply-To: References: MIME-Version: 1.0 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Spam-Status: No, score=-7.0 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, 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 Add machine driver support for BeagleBone-Black and other boards with tilcdc support and NXP TDA998X HDMI transmitter connected to McASP port in I2S mode. The 44100 Hz sample-rate and it's multiples can not be supported on Beaglebone-Black because of limited clock-rate support. The only supported sample format is SNDRV_PCM_FORMAT_S32_LE. The 8 least significant bits are ignored. Signed-off-by: Jyri Sarha cc: bcousson@baylibre.com --- .../bindings/sound/davinci-evm-audio.txt | 4 +- sound/soc/davinci/davinci-evm.c | 167 +++++++++++++++++++- 2 files changed, 168 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt index 4aa00f6..f1e1031 100644 --- a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt +++ b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt @@ -1,7 +1,9 @@ * Texas Instruments SoC audio setups with TLV320AIC3X Codec Required properties: -- compatible : "ti,da830-evm-audio" : forDM365/DA8xx/OMAPL1x/AM33xx +- compatible : + "ti,da830-evm-audio" : for DM365/DA8xx/OMAPL1x/AM33xx + "ti,am33xx-beaglebone-black-audio" : for Beaglebone-black HDMI audio - ti,model : The user-visible name of this sound complex. - ti,audio-codec : The phandle of the TLV320AIC3x audio codec - ti,mcasp-controller : The phandle of the McASP controller diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index b28c9fd..3d3138d 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -33,8 +34,13 @@ struct snd_soc_card_drvdata_davinci { struct clk *mclk; unsigned sysclk; + struct snd_pcm_hw_constraint_list *rate_constraint; }; +/* If changing sample format the tda998x configuration (REG_CTS_N) needs + to be changed. */ +#define TDA998X_SAMPLE_FORMAT SNDRV_PCM_FORMAT_S32_LE + static int evm_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -80,12 +86,80 @@ static int evm_hw_params(struct snd_pcm_substream *substream, return 0; } +static int evm_tda998x_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *soc_card = rtd->codec->card; + struct snd_soc_card_drvdata_davinci *drvdata = + (struct snd_soc_card_drvdata_davinci *) + snd_soc_card_get_drvdata(soc_card); + struct snd_mask *fmt = constrs_mask(&runtime->hw_constraints, + SNDRV_PCM_HW_PARAM_FORMAT); + snd_mask_none(fmt); + snd_mask_set(fmt, TDA998X_SAMPLE_FORMAT); + + runtime->hw.rate_min = drvdata->rate_constraint->list[0]; + runtime->hw.rate_max = drvdata->rate_constraint->list[ + drvdata->rate_constraint->count - 1]; + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + drvdata->rate_constraint); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, + 2, 2); + + return evm_startup(substream); +} + +static unsigned int evm_get_bclk(struct snd_pcm_hw_params *params) +{ + int sample_size = snd_pcm_format_width(params_format(params)); + int rate = params_rate(params); + int channels = params_channels(params); + + return sample_size * channels * rate; +} + +static int evm_tda998x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *soc_card = codec->card; + struct platform_device *pdev = to_platform_device(soc_card->dev); + unsigned int bclk_freq = evm_get_bclk(params); + unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *) + snd_soc_card_get_drvdata(soc_card))->sysclk; + int ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, 1, sysclk / bclk_freq); + if (ret < 0) { + dev_err(&pdev->dev, "can't set CPU DAI clock divider %d\n", + ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + return ret; +} + static struct snd_soc_ops evm_ops = { .startup = evm_startup, .shutdown = evm_shutdown, .hw_params = evm_hw_params, }; +static struct snd_soc_ops evm_tda998x_ops = { + .startup = evm_tda998x_startup, + .shutdown = evm_shutdown, + .hw_params = evm_tda998x_hw_params, +}; + /* davinci-evm machine dapm widgets */ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), @@ -152,6 +226,81 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static unsigned int tda998x_hdmi_rates[] = { + 32000, + 44100, + 48000, + 88200, + 96000, +}; + +static struct snd_pcm_hw_constraint_list *evm_tda998x_rate_constraint( + struct snd_soc_card *soc_card) +{ + struct platform_device *pdev = to_platform_device(soc_card->dev); + unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *) + snd_soc_card_get_drvdata(soc_card))->sysclk; + struct snd_pcm_hw_constraint_list *ret; + unsigned int *rates; + int i = 0, j = 0; + + ret = devm_kzalloc(soc_card->dev, sizeof(*ret) + + sizeof(tda998x_hdmi_rates), GFP_KERNEL); + if (!ret) { + dev_err(&pdev->dev, "Unable to allocate rate constraint!\n"); + return NULL; + } + + rates = (unsigned int *)&ret[1]; + ret->list = rates; + ret->mask = 0; + for (; i < ARRAY_SIZE(tda998x_hdmi_rates); i++) { + unsigned int bclk_freq = tda998x_hdmi_rates[i] * 2 * + snd_pcm_format_width(TDA998X_SAMPLE_FORMAT); + if (sysclk % bclk_freq == 0) { + rates[j++] = tda998x_hdmi_rates[i]; + dev_dbg(soc_card->dev, "Allowing rate %u\n", + tda998x_hdmi_rates[i]); + } + } + ret->count = j; + return ret; +} + +static const struct snd_soc_dapm_widget tda998x_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HDMI Out"), +}; + +static int evm_tda998x_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dapm_context *dapm = &rtd->codec->dapm; + struct snd_soc_card *soc_card = rtd->codec->card; + struct snd_soc_card_drvdata_davinci *drvdata = + (struct snd_soc_card_drvdata_davinci *) + snd_soc_card_get_drvdata(soc_card); + int ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, 0, 1); + if (ret < 0) + return ret; + + drvdata->rate_constraint = evm_tda998x_rate_constraint(soc_card); + + snd_soc_dapm_new_controls(dapm, tda998x_dapm_widgets, + ARRAY_SIZE(tda998x_dapm_widgets)); + + ret = snd_soc_of_parse_audio_routing(soc_card, "ti,audio-routing"); + + /* not connected */ + snd_soc_dapm_disable_pin(dapm, "RX"); + + /* always connected */ + snd_soc_dapm_enable_pin(dapm, "HDMI Out"); + + return 0; +} + /* davinci-evm digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link dm6446_evm_dai = { .name = "TLV320AIC3X", @@ -337,7 +486,7 @@ static struct snd_soc_card da850_snd_soc_card = { #if defined(CONFIG_OF) /* - * The struct is used as place holder. It will be completely + * The structs are used as place holders. They will be completely * filled with data from dt node. */ static struct snd_soc_dai_link evm_dai_tlv320aic3x = { @@ -350,10 +499,24 @@ static struct snd_soc_dai_link evm_dai_tlv320aic3x = { SND_SOC_DAIFMT_IB_NF, }; +static struct snd_soc_dai_link evm_dai_tda998x_hdmi = { + .name = "NXP TDA998x HDMI Chip", + .stream_name = "HDMI", + .codec_dai_name = "hdmi-hifi", + .ops = &evm_tda998x_ops, + .init = evm_tda998x_init, + .dai_fmt = (SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_IB_NF), +}; + static const struct of_device_id davinci_evm_dt_ids[] = { { .compatible = "ti,da830-evm-audio", - .data = (void *) &evm_dai_tlv320aic3x, + .data = &evm_dai_tlv320aic3x, + }, + { + .compatible = "ti,am33xx-beaglebone-black-audio", + .data = &evm_dai_tda998x_hdmi, }, { /* sentinel */ } };