From patchwork Mon Feb 19 15:00:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Olivier MOYSAN X-Patchwork-Id: 10227985 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 3E06960392 for ; Mon, 19 Feb 2018 15:05:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2E3DB2872D for ; Mon, 19 Feb 2018 15:05:15 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2286328A5C; Mon, 19 Feb 2018 15:05:15 +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=-1.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 60ACC2872D for ; Mon, 19 Feb 2018 15:05:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=dy6Li277CJhDqTGqKLfcJWhPsSz4NP3KNZXWliDRDoI=; b=e6zyQYE3hWW6e0 8xKeMoePMN4Fc6yRFFaUvFiQ1xeYWYzPP3BogRkrCpytpmOd78KV+pooAKfYBQLR+lSayR3gNqbpy hIoQrNIJEKWckUlWBtSggJicabkHmsRu86XgNfcqE85B7oq74oNGliFwjH9ZeuJmzTKb8ZrIbbHAj SL7venSfVsmt6fJ60NoTVAbjLmaBA23hJ7g+FlOsOrXpjy9sHARQFUKNMQnK3y/jSIWldon0zR2ZR 8po5ypk4uuDu5vsQWBjv29bkFWluB22invCrqpNX3QXeCopfPQ0gNXCxZRRy8tU2/RBNpqQDUPOUl sworRbtyGCF4vnfDwZvg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1enn01-0007CZ-Fu; Mon, 19 Feb 2018 15:05:01 +0000 Received: from mx08-00178001.pphosted.com ([91.207.212.93] helo=mx07-00178001.pphosted.com) by bombadil.infradead.org with esmtps (Exim 4.89 #1 (Red Hat Linux)) id 1enmy9-0006Cm-PX for linux-arm-kernel@lists.infradead.org; Mon, 19 Feb 2018 15:03:26 +0000 Received: from pps.filterd (m0046661.ppops.net [127.0.0.1]) by mx08-.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id w1JEwsmI005400; Mon, 19 Feb 2018 16:00:45 +0100 Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx08-00178001.pphosted.com with ESMTP id 2g6c5g9wmf-1 (version=TLSv1 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=NOT); Mon, 19 Feb 2018 16:00:45 +0100 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 2876D31; Mon, 19 Feb 2018 15:00:45 +0000 (GMT) Received: from Webmail-eu.st.com (Safex1hubcas22.st.com [10.75.90.92]) by zeta.dmz-eu.st.com (STMicroelectronics) with ESMTP id AA5E9552F; Mon, 19 Feb 2018 15:00:44 +0000 (GMT) Received: from SAFEX1HUBCAS24.st.com (10.75.90.94) by Safex1hubcas22.st.com (10.75.90.92) with Microsoft SMTP Server (TLS) id 14.3.352.0; Mon, 19 Feb 2018 16:00:44 +0100 Received: from localhost (10.201.23.16) by webmail-ga.st.com (10.75.90.48) with Microsoft SMTP Server (TLS) id 14.3.361.1; Mon, 19 Feb 2018 16:00:44 +0100 From: Olivier Moysan To: , , , , , , , , , , , , , Subject: [RFC PATCH 3/3] ASoC: stm32: sai: Add support of S/PDIF playback Date: Mon, 19 Feb 2018 16:00:37 +0100 Message-ID: <1519052437-24011-4-git-send-email-olivier.moysan@st.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1519052437-24011-1-git-send-email-olivier.moysan@st.com> References: <1519052437-24011-1-git-send-email-olivier.moysan@st.com> MIME-Version: 1.0 X-Originating-IP: [10.201.23.16] X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2018-02-19_07:, , signatures=0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180219_070306_195435_0FFA0843 X-CRM114-Status: GOOD ( 22.82 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: arnaud.pouliquen@st.com, benjamin.gaignard@st.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Add support of S/PDIF iec60958 playback on STM32 SAI. Signed-off-by: olivier moysan --- sound/soc/stm/stm32_sai.c | 2 + sound/soc/stm/stm32_sai.h | 2 + sound/soc/stm/stm32_sai_sub.c | 153 +++++++++++++++++++++++++++++++++++------- 3 files changed, 133 insertions(+), 24 deletions(-) diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index d743b7d..f226542 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -30,10 +30,12 @@ static const struct stm32_sai_conf stm32_sai_conf_f4 = { .version = SAI_STM32F4, + .has_spdif = false, }; static const struct stm32_sai_conf stm32_sai_conf_h7 = { .version = SAI_STM32H7, + .has_spdif = true, }; static const struct of_device_id stm32_sai_ids[] = { diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index bb062e7..f254221 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h @@ -248,9 +248,11 @@ enum stm32_sai_version { /** * struct stm32_sai_conf - SAI configuration * @version: SAI version + * @has_spdif: SAI S/PDIF support flag */ struct stm32_sai_conf { int version; + bool has_spdif; }; /** diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 08583b9..cfeb219 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include "stm32_sai.h" #define SAI_FREE_PROTOCOL 0x0 +#define SAI_SPDIF_PROTOCOL 0x1 #define SAI_SLOT_SIZE_AUTO 0x0 #define SAI_SLOT_SIZE_16 0x1 @@ -59,8 +61,13 @@ #define SAI_SYNC_INTERNAL 0x1 #define SAI_SYNC_EXTERNAL 0x2 +#define STM_SAI_PROTOCOL_IS_SPDIF(ip) ((ip)->spdif) +#define STM_SAI_HAS_SPDIF(x) ((x)->pdata->conf->has_spdif) #define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) +#define SAI_IEC60958_BLOCK_FRAMES 192 +#define SAI_IEC60958_STATUS_BYTES 24 + /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) * @pdev: device data pointer @@ -78,6 +85,7 @@ * @id: SAI sub block id corresponding to sub-block A or B * @dir: SAI block direction (playback or capture). set at init * @master: SAI block mode flag. (true=master, false=slave) set at init + * @spdif: SAI S/PDIF iec60958 mode flag. set at init * @fmt: SAI block format. relevant only for custom protocols. set at init * @sync: SAI block synchronization mode. (none, internal or external) * @synco: SAI block ext sync source (provider setting). (none, sub-block A/B) @@ -87,6 +95,8 @@ * @slot_width: rx or tx slot width in bits * @slot_mask: rx or tx active slots mask. set at init or at runtime * @data_size: PCM data width. corresponds to PCM substream width. + * @spdif_frm_cnt: S/PDIF playback frame counter + * @spdif_status_bits: S/PDIF status bits */ struct stm32_sai_sub_data { struct platform_device *pdev; @@ -104,6 +114,7 @@ struct stm32_sai_sub_data { unsigned int id; int dir; bool master; + bool spdif; int fmt; int sync; int synco; @@ -113,6 +124,8 @@ struct stm32_sai_sub_data { int slot_width; int slot_mask; int data_size; + unsigned int spdif_frm_cnt; + unsigned char spdif_status_bits[SAI_IEC60958_STATUS_BYTES]; }; enum stm32_sai_fifo_th { @@ -171,6 +184,10 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) } } +static const unsigned char default_status_bits[SAI_IEC60958_STATUS_BYTES] = { + 0, 0, 0, IEC958_AES3_CON_FS_48000, +}; + static const struct regmap_config stm32_sai_sub_regmap_config_f4 = { .reg_bits = 32, .reg_stride = 4, @@ -277,6 +294,11 @@ static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int slotr, slotr_mask, slot_size; + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + dev_warn(cpu_dai->dev, "Slot setting relevant only for TDM\n"); + return 0; + } + dev_dbg(cpu_dai->dev, "Masks tx/rx:%#x/%#x, slots:%d, width:%d\n", tx_mask, rx_mask, slots, slot_width); @@ -326,8 +348,17 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); - cr1_mask = SAI_XCR1_PRTCFG_MASK; - cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); + /* Do not generate master by default */ + cr1 = SAI_XCR1_NODIV; + cr1_mask = SAI_XCR1_NODIV; + + cr1_mask |= SAI_XCR1_PRTCFG_MASK; + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + cr1 |= SAI_XCR1_PRTCFG_SET(SAI_SPDIF_PROTOCOL); + goto conf_update; + } + + cr1 |= SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { /* SCK active high for all protocols */ @@ -409,10 +440,7 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) cr1_mask |= SAI_XCR1_SLAVE; - /* do not generate master by default */ - cr1 |= SAI_XCR1_NODIV; - cr1_mask |= SAI_XCR1_NODIV; - +conf_update: ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); if (ret < 0) { dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); @@ -478,6 +506,12 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF)); + /* DS bits in CR1 not set for SPDIF (size forced to 24 bits).*/ + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + sai->spdif_frm_cnt = 0; + return 0; + } + /* Mode, data format and channel config */ cr1_mask = SAI_XCR1_DS_MASK; switch (params_format(params)) { @@ -592,13 +626,14 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, int cr1, mask, div = 0; int sai_clk_rate, mclk_ratio, den, ret; int version = sai->pdata->conf->version; + unsigned int rate = params_rate(params); if (!sai->mclk_rate) { dev_err(cpu_dai->dev, "Mclk rate is null\n"); return -EINVAL; } - if (!(params_rate(params) % 11025)) + if (!(rate % 11025)) clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k); else clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); @@ -623,24 +658,28 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, * MCKDIV = sai_ck / (frl x ws) (NOMCK=1) * Note: NOMCK/NODIV correspond to same bit. */ - if (sai->mclk_rate) { - mclk_ratio = sai->mclk_rate / params_rate(params); - if (mclk_ratio != 256) { + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + div = DIV_ROUND_CLOSEST(sai_clk_rate, + (params_rate(params) * 128)); + } else { + if (sai->mclk_rate) { + mclk_ratio = sai->mclk_rate / rate; if (mclk_ratio == 512) { mask = SAI_XCR1_OSR; cr1 = SAI_XCR1_OSR; - } else { + } else if (mclk_ratio != 256) { dev_err(cpu_dai->dev, "Wrong mclk ratio %d\n", mclk_ratio); return -EINVAL; } + div = DIV_ROUND_CLOSEST(sai_clk_rate, + sai->mclk_rate); + } else { + /* mclk-fs not set, master clock not active */ + den = sai->fs_length * params_rate(params); + div = DIV_ROUND_CLOSEST(sai_clk_rate, den); } - div = DIV_ROUND_CLOSEST(sai_clk_rate, sai->mclk_rate); - } else { - /* mclk-fs not set, master clock not active. NOMCK=1 */ - den = sai->fs_length * params_rate(params); - div = DIV_ROUND_CLOSEST(sai_clk_rate, den); } } @@ -670,10 +709,12 @@ static int stm32_sai_hw_params(struct snd_pcm_substream *substream, sai->data_size = params_width(params); - ret = stm32_sai_set_slots(cpu_dai); - if (ret < 0) - return ret; - stm32_sai_set_frame(cpu_dai); + if (!STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + ret = stm32_sai_set_slots(cpu_dai); + if (ret < 0) + return ret; + stm32_sai_set_frame(cpu_dai); + } ret = stm32_sai_set_config(cpu_dai, substream, params); if (ret) @@ -723,6 +764,9 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, (unsigned int)~SAI_XCR1_DMAEN); if (ret < 0) dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); + + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) + sai->spdif_frm_cnt = 0; break; default: return -EINVAL; @@ -776,6 +820,10 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) sai->synco, sai->synci); } + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) + memcpy(sai->spdif_status_bits, default_status_bits, + sizeof(default_status_bits)); + cr1_mask |= SAI_XCR1_SYNCEN_MASK; cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); @@ -792,6 +840,42 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) .shutdown = stm32_sai_shutdown, }; +static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); + int *ptr = (int *)(runtime->dma_area + hwoff + + channel * (runtime->dma_bytes / runtime->channels)); + ssize_t cnt = bytes_to_samples(runtime, bytes); + unsigned int frm_cnt = sai->spdif_frm_cnt; + unsigned int byte; + unsigned int mask; + + do { + *ptr = ((*ptr >> 8) & 0x00ffffff); + + /* Set channel status bit */ + byte = frm_cnt >> 3; + mask = 1 << (frm_cnt - (byte << 3)); + if (sai->spdif_status_bits[byte] & mask) + *ptr |= 0x04000000; + ptr++; + + if (!(cnt % 2)) + frm_cnt++; + + if (frm_cnt == SAI_IEC60958_BLOCK_FRAMES) + frm_cnt = 0; + } while (--cnt); + sai->spdif_frm_cnt = frm_cnt; + + return 0; +} + static const struct snd_pcm_hardware stm32_sai_pcm_hw = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, .buffer_bytes_max = 8 * PAGE_SIZE, @@ -842,8 +926,14 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) }; static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = { - .pcm_hardware = &stm32_sai_pcm_hw, - .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .pcm_hardware = &stm32_sai_pcm_hw, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +}; + +static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config_spdif = { + .pcm_hardware = &stm32_sai_pcm_hw, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .process = stm32_sai_pcm_process_spdif, }; static const struct snd_soc_component_driver stm32_component = { @@ -900,6 +990,18 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, return -EINVAL; } + /* Get spdif iec60958 property */ + sai->spdif = false; + if (of_get_property(np, "st,iec60958", NULL)) { + if (!STM_SAI_HAS_SPDIF(sai) || + sai->dir == SNDRV_PCM_STREAM_CAPTURE) { + dev_err(&pdev->dev, "S/PDIF IEC60958 not supported\n"); + return -EINVAL; + } + sai->spdif = true; + sai->master = true; + } + /* Get synchronization property */ args.np = NULL; ret = of_parse_phandle_with_fixed_args(np, "st,sync", 1, 0, &args); @@ -999,6 +1101,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) { struct stm32_sai_sub_data *sai; const struct of_device_id *of_id; + const struct snd_dmaengine_pcm_config *conf = &stm32_sai_pcm_config; int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); @@ -1039,8 +1142,10 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) if (ret) return ret; - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, - &stm32_sai_pcm_config, 0); + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) + conf = &stm32_sai_pcm_config_spdif; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, conf, 0); if (ret) { dev_err(&pdev->dev, "Could not register pcm dma\n"); return ret;