From patchwork Mon May 11 03:16:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Sia, Jee Heng" X-Patchwork-Id: 192774 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E9341C47255 for ; Mon, 11 May 2020 03:30:07 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 6BE9B20746 for ; Mon, 11 May 2020 03:30:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=alsa-project.org header.i=@alsa-project.org header.b="S2qHghkI" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 6BE9B20746 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=intel.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=alsa-devel-bounces@alsa-project.org Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 952F586F; Mon, 11 May 2020 05:29:15 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 952F586F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1589167805; bh=1iTd4HYwB+zRY8TNsIa95kTPJfnKeNONIPpv9iZnd2A=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=S2qHghkIjWvgJGXEte2SnYQCDs31nPL27XyYT1J3tdb7tM9yPQXxjoF1xYUNHi4bv flybY8JwDBVfkN1zzVjZ5cdSvv/rSLMdYKqN3zoZ1AO4BBUO1BnQLUMU2Kjqj6K8yj FgDUZ8deHkb4aXH/upZedPJpxWAUh8Op/9Vi86B8= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id BDFF4F80157; Mon, 11 May 2020 05:29:14 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 9C400F8027B; Mon, 11 May 2020 05:29:03 +0200 (CEST) Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id C5A7EF800E3 for ; Mon, 11 May 2020 05:28:55 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz C5A7EF800E3 IronPort-SDR: BQ31iyxns0UOdf764OU0pTiLeK8q1CIMAb/bodBWdlHflhron9ErbVv43Q5nrIPZiO6aeeFP+E 0khTD2K6X8RA== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 10 May 2020 20:28:50 -0700 IronPort-SDR: 8MKo8Tf+hUCRRqRzYWGK1hPt/sGhme58PqJPqMF8gBjdNDaxZeKX+mN3xXy+urF6K0V5lyKAkk MDGn6bWrkASg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.73,378,1583222400"; d="scan'208";a="265024233" Received: from unknown (HELO jsia-HP-Z620-Workstation.png.intel.com) ([10.221.118.135]) by orsmga006.jf.intel.com with ESMTP; 10 May 2020 20:28:48 -0700 From: Sia Jee Heng To: Subject: [PATCH 1/4] ASoC: Intel: Add KeemBay platform driver Date: Mon, 11 May 2020 11:16:01 +0800 Message-Id: <1589166964-8985-2-git-send-email-jee.heng.sia@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1589166964-8985-1-git-send-email-jee.heng.sia@intel.com> References: <1589166964-8985-1-git-send-email-jee.heng.sia@intel.com> Cc: liam.r.girdwood@linux.intel.com, broonie@kernel.org, tiwai@suse.com, pierre-louis.bossart@linux.intel.com X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" Add KeemBay ASoC platform driver which initialize the i2s controller and uses i2s to capture and transmit pcm data to external codec. The i2s is running in polling mode. Signed-off-by: Michael Sit Wei Hong Signed-off-by: Sia Jee Heng --- sound/soc/intel/keembay/Makefile | 4 + sound/soc/intel/keembay/kmb_platform.c | 746 +++++++++++++++++++++++++++++++++ sound/soc/intel/keembay/kmb_platform.h | 145 +++++++ 3 files changed, 895 insertions(+) create mode 100644 sound/soc/intel/keembay/Makefile create mode 100644 sound/soc/intel/keembay/kmb_platform.c create mode 100644 sound/soc/intel/keembay/kmb_platform.h diff --git a/sound/soc/intel/keembay/Makefile b/sound/soc/intel/keembay/Makefile new file mode 100644 index 0000000..9084e8c --- /dev/null +++ b/sound/soc/intel/keembay/Makefile @@ -0,0 +1,4 @@ +snd-soc-kmb_platform-objs := \ + kmb_platform.o + +obj-$(CONFIG_SND_SOC_INTEL_KEEMBAY) += snd-soc-kmb_platform.o diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c new file mode 100644 index 0000000..808a1fd --- /dev/null +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -0,0 +1,746 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel KeemBay Platform driver + * + * Copyright (C) 2020 Intel Corporation. + * + */ + +#include +#include +#include +#include +#include +#include +#include "kmb_platform.h" + +#define PERIODS_MIN 2 +#define PERIODS_MAX 48 +#define PERIOD_BYTES_MIN 4096 +#define BUFFER_BYTES_MAX (PERIODS_MAX * PERIOD_BYTES_MIN) +#define TDM_OPERATION 1 +#define I2S_OPERATION 0 +#define DATA_WIDTH_CONFIG_BIT 6 +#define TDM_CHANNEL_CONFIG_BIT 3 +#define I2S_SAMPLE_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000) + +/* Array subscript */ +#define I2S_MASTER_MODE 0 +#define I2S_SLAVE_MODE 1 + +static const char * const i2s_mode_name[] = {"master", "slave"}; + +/* Maximum bit resolution of a channel - not uniformly spaced */ +static const u32 fifo_width[COMP_MAX_WORDSIZE] = {12, 16, 20, 24, 32,}; + +/* PCM format to support channel resolution */ +static const u32 formats[COMP_MAX_WORDSIZE] = { + SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S32_LE, +}; + +static const struct snd_pcm_hardware kmb_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .rates = I2S_SAMPLE_RATES, + .rate_min = 16000, + .rate_max = 48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = BUFFER_BYTES_MAX, + .period_bytes_min = PERIOD_BYTES_MIN, + .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN, + .periods_min = PERIODS_MIN, + .periods_max = PERIODS_MAX, + .fifo_size = 16, +}; + +static unsigned int pcm_tx_fn(struct kmb_i2s_info *dev, + struct snd_pcm_runtime *runtime, + unsigned int tx_ptr, bool *period_elapsed) +{ + unsigned int period_pos = tx_ptr % runtime->period_size; + u16(*p16)[2]; + u32(*p32)[2]; + int i; + + if (dev->config.data_width == 16) + p16 = (void *)runtime->dma_area; + else + p32 = (void *)runtime->dma_area; + /* KMB i2s uses two separate L/R FIFO */ + for (i = 0; i < dev->fifo_th; i++) { + if (dev->config.data_width == 16) { + writel(p16[tx_ptr][0], dev->i2s_base + LRBR_LTHR(0)); + writel(p16[tx_ptr][1], dev->i2s_base + RRBR_RTHR(0)); + } else { + writel(p32[tx_ptr][0], dev->i2s_base + LRBR_LTHR(0)); + writel(p32[tx_ptr][1], dev->i2s_base + RRBR_RTHR(0)); + } + + period_pos++; + + if (++tx_ptr >= runtime->buffer_size) + tx_ptr = 0; + } + + *period_elapsed = period_pos >= runtime->period_size; + + return tx_ptr; +} + +static unsigned int pcm_rx_fn(struct kmb_i2s_info *dev, + struct snd_pcm_runtime *runtime, + unsigned int rx_ptr, bool *period_elapsed) +{ + unsigned int period_pos = rx_ptr % runtime->period_size; + u16(*p16)[2]; + u32(*p32)[2]; + int i; + + if (dev->config.data_width == 16) + p16 = (void *)runtime->dma_area; + else + p32 = (void *)runtime->dma_area; + /* KMB i2s uses two separate L/R FIFO */ + for (i = 0; i < dev->fifo_th; i++) { + if (dev->config.data_width == 16) { + p16[rx_ptr][0] = readl(dev->i2s_base + LRBR_LTHR(0)); + p16[rx_ptr][1] = readl(dev->i2s_base + RRBR_RTHR(0)); + } else { + p32[rx_ptr][0] = readl(dev->i2s_base + LRBR_LTHR(0)); + p32[rx_ptr][1] = readl(dev->i2s_base + RRBR_RTHR(0)); + } + + period_pos++; + + if (++rx_ptr >= runtime->buffer_size) + rx_ptr = 0; + } + + *period_elapsed = period_pos >= runtime->period_size; + + return rx_ptr; +} + +static inline void i2s_disable_channels(struct kmb_i2s_info *kmb_i2s, + u32 stream) +{ + struct i2s_clk_config_data *config = &kmb_i2s->config; + u32 i; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < config->chan_nr / 2; i++) + writel(0, kmb_i2s->i2s_base + TER(i)); + } else { + for (i = 0; i < config->chan_nr / 2; i++) + writel(0, kmb_i2s->i2s_base + RER(i)); + } +} + +static inline void i2s_clear_irqs(struct kmb_i2s_info *kmb_i2s, u32 stream) +{ + struct i2s_clk_config_data *config = &kmb_i2s->config; + u32 i; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < config->chan_nr / 2; i++) + readl(kmb_i2s->i2s_base + TOR(i)); + } else { + for (i = 0; i < config->chan_nr / 2; i++) + readl(kmb_i2s->i2s_base + ROR(i)); + } +} + +static inline void i2s_irq_trigger(struct kmb_i2s_info *kmb_i2s, u32 stream, + int chan_nr, bool trigger) +{ + u32 i, irq; + u32 flag; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + flag = TX_INT_FLAG; + else + flag = RX_INT_FLAG; + + for (i = 0; i < chan_nr / 2; i++) { + irq = readl(kmb_i2s->i2s_base + IMR(i)); + irq = trigger ? irq & ~flag : irq | flag; + writel(irq, kmb_i2s->i2s_base + IMR(i)); + } +} + +static void pcm_operation(struct kmb_i2s_info *kmb_i2s, bool playback) +{ + struct snd_pcm_substream *substream; + bool active, period_elapsed; + + if (playback) + substream = kmb_i2s->tx_substream; + else + substream = kmb_i2s->rx_substream; + + active = substream && snd_pcm_running(substream); + + if (active) { + unsigned int ptr; + unsigned int new_ptr; + + if (playback) { + ptr = kmb_i2s->tx_ptr; + new_ptr = pcm_tx_fn(kmb_i2s, substream->runtime, + ptr, &period_elapsed); + cmpxchg(&kmb_i2s->tx_ptr, ptr, new_ptr); + } else { + ptr = kmb_i2s->rx_ptr; + new_ptr = pcm_rx_fn(kmb_i2s, substream->runtime, + ptr, &period_elapsed); + cmpxchg(&kmb_i2s->rx_ptr, ptr, new_ptr); + } + + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } +} + +static int kmb_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct kmb_i2s_info *kmb_i2s; + + kmb_i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + snd_soc_set_runtime_hwparams(substream, &kmb_pcm_hardware); + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + runtime->private_data = kmb_i2s; + + return 0; +} + +static int kmb_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct kmb_i2s_info *kmb_i2s = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + kmb_i2s->tx_ptr = 0; + kmb_i2s->tx_substream = substream; + } else { + kmb_i2s->rx_ptr = 0; + kmb_i2s->rx_substream = substream; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + kmb_i2s->tx_substream = NULL; + else + kmb_i2s->rx_substream = NULL; + break; + default: + return -EINVAL; + } + + return 0; +} + +static irqreturn_t i2s_irq_handler(int irq, void *dev_id) +{ + struct kmb_i2s_info *kmb_i2s = dev_id; + struct i2s_clk_config_data *config = &kmb_i2s->config; + irqreturn_t ret = IRQ_NONE; + u32 isr[4]; + int i; + + for (i = 0; i < config->chan_nr / 2; i++) + isr[i] = readl(kmb_i2s->i2s_base + ISR(i)); + + i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK); + i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE); + + for (i = 0; i < config->chan_nr / 2; i++) { + /* + * Check if TX fifo is empty. If empty fill FIFO with samples + */ + if ((isr[i] & ISR_TXFE)) { + pcm_operation(kmb_i2s, true); + ret = IRQ_HANDLED; + } + /* + * Data available. Retrieve samples from FIFO + */ + if ((isr[i] & ISR_RXDA)) { + pcm_operation(kmb_i2s, false); + ret = IRQ_HANDLED; + } + /* Error Handling: TX */ + if (isr[i] & ISR_TXFO) { + dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i); + ret = IRQ_HANDLED; + } + /* Error Handling: RX */ + if (isr[i] & ISR_RXFO) { + dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i); + ret = IRQ_HANDLED; + } + } + + return ret; +} + +static int kmb_platform_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *soc_runtime) +{ + size_t size = kmb_pcm_hardware.buffer_bytes_max; + /* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */ + snd_pcm_set_managed_buffer_all(soc_runtime->pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + NULL, size, size); + return 0; +} + +static snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct kmb_i2s_info *kmb_i2s = runtime->private_data; + snd_pcm_uframes_t pos; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pos = kmb_i2s->tx_ptr; + else + pos = kmb_i2s->rx_ptr; + + return pos < runtime->buffer_size ? pos : 0; +} + +static const struct snd_soc_component_driver kmb_component = { + .name = "kmb", + .pcm_construct = kmb_platform_pcm_new, + .open = kmb_pcm_open, + .trigger = kmb_pcm_trigger, + .pointer = kmb_pcm_pointer, +}; + +static void i2s_start(struct kmb_i2s_info *kmb_i2s, + struct snd_pcm_substream *substream) +{ + struct i2s_clk_config_data *config = &kmb_i2s->config; + + /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ + writel(1, kmb_i2s->i2s_base + IER); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + writel(1, kmb_i2s->i2s_base + ITER); + else + writel(1, kmb_i2s->i2s_base + IRER); + + i2s_irq_trigger(kmb_i2s, substream->stream, config->chan_nr, true); + + if (kmb_i2s->master) + writel(1, kmb_i2s->i2s_base + CER); + else + writel(0, kmb_i2s->i2s_base + CER); +} + +static void i2s_stop(struct kmb_i2s_info *kmb_i2s, + struct snd_pcm_substream *substream) +{ + /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ + i2s_clear_irqs(kmb_i2s, substream->stream); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + writel(0, kmb_i2s->i2s_base + ITER); + else + writel(0, kmb_i2s->i2s_base + IRER); + + i2s_irq_trigger(kmb_i2s, substream->stream, 8, false); + + if (!kmb_i2s->active) { + writel(0, kmb_i2s->i2s_base + CER); + writel(0, kmb_i2s->i2s_base + IER); + } +} + +static int kmb_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *cpu_dai) +{ + struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* Keep track of i2s activity before turn off + * the i2s interface + */ + kmb_i2s->active++; + i2s_start(kmb_i2s, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + kmb_i2s->active--; + i2s_stop(kmb_i2s, substream); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void i2s_config(struct kmb_i2s_info *kmb_i2s, int stream) +{ + struct i2s_clk_config_data *config = &kmb_i2s->config; + u32 ch_reg; + + i2s_disable_channels(kmb_i2s, stream); + + for (ch_reg = 0; ch_reg < config->chan_nr / 2; ch_reg++) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + writel(kmb_i2s->xfer_resolution, + kmb_i2s->i2s_base + TCR(ch_reg)); + + writel(kmb_i2s->fifo_th - 1, + kmb_i2s->i2s_base + TFCR(ch_reg)); + + writel(1, kmb_i2s->i2s_base + TER(ch_reg)); + } else { + writel(kmb_i2s->xfer_resolution, + kmb_i2s->i2s_base + RCR(ch_reg)); + + writel(kmb_i2s->fifo_th - 1, + kmb_i2s->i2s_base + RFCR(ch_reg)); + + writel(1, kmb_i2s->i2s_base + RER(ch_reg)); + } + } +} + +static int kmb_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, + struct snd_soc_dai *cpu_dai) +{ + struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); + struct i2s_clk_config_data *config = &kmb_i2s->config; + u32 register_val, write_val; + int ret; + + switch (params_format(hw_params)) { + case SNDRV_PCM_FORMAT_S16_LE: + config->data_width = 16; + kmb_i2s->ccr = 0x00; + kmb_i2s->xfer_resolution = 0x02; + break; + case SNDRV_PCM_FORMAT_S24_LE: + config->data_width = 24; + kmb_i2s->ccr = 0x08; + kmb_i2s->xfer_resolution = 0x04; + break; + case SNDRV_PCM_FORMAT_S32_LE: + config->data_width = 32; + kmb_i2s->ccr = 0x10; + kmb_i2s->xfer_resolution = 0x05; + break; + default: + dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt"); + return -EINVAL; + } + + config->chan_nr = params_channels(hw_params); + + switch (config->chan_nr) { + /* TODO: This switch case will handle up to TDM8 in the near future */ + case TWO_CHANNEL_SUPPORT: + write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) | + (config->data_width << DATA_WIDTH_CONFIG_BIT) | + MASTER_MODE | I2S_OPERATION; + + writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); + + register_val = readl(kmb_i2s->pss_base + I2S_GEN_CFG_0); + dev_dbg(kmb_i2s->dev, "pss register = 0x%X", register_val); + break; + default: + dev_dbg(kmb_i2s->dev, "channel not supported\n"); + return -EINVAL; + } + + i2s_config(kmb_i2s, substream->stream); + + writel(kmb_i2s->ccr, kmb_i2s->i2s_base + CCR); + + config->sample_rate = params_rate(hw_params); + + if (kmb_i2s->master) { + /* Only 2 ch supported in Master mode */ + u32 bitclk = config->sample_rate * config->data_width * 2; + + ret = clk_set_rate(kmb_i2s->clk_i2s, bitclk); + if (ret) { + dev_err(kmb_i2s->dev, + "Can't set I2S clock rate: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int kmb_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + writel(1, kmb_i2s->i2s_base + TXFFR); + else + writel(1, kmb_i2s->i2s_base + RXFFR); + + return 0; +} + +static struct snd_soc_dai_ops kmb_dai_ops = { + .trigger = kmb_dai_trigger, + .hw_params = kmb_dai_hw_params, + .prepare = kmb_dai_prepare, +}; + +static struct snd_soc_dai_driver intel_kmb_platform_dai[] = { + { + .name = "kmb-plat-dai", + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = I2S_SAMPLE_RATES, + .rate_min = 16000, + .rate_max = 48000, + .formats = (SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S16_LE), + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = I2S_SAMPLE_RATES, + .rate_min = 16000, + .rate_max = 48000, + .formats = (SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S16_LE), + }, + .ops = &kmb_dai_ops, + }, +}; + +static int kmb_configure_dai(struct kmb_i2s_info *kmb_i2s, + struct snd_soc_dai_driver *kmb_i2s_dai, + unsigned int rates) +{ + /* + * Read component parameter registers to extract + * the I2S block's configuration. + */ + u32 comp1 = readl(kmb_i2s->i2s_base + kmb_i2s->i2s_reg_comp1); + u32 comp2 = readl(kmb_i2s->i2s_base + kmb_i2s->i2s_reg_comp2); + u32 fifo_depth = 1 << COMP1_FIFO_DEPTH(comp1); + u32 idx; + + if (COMP1_TX_ENABLED(comp1)) { + idx = COMP1_TX_WORDSIZE_0(comp1); + kmb_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; + kmb_i2s_dai->playback.channels_max = + 1 << COMP1_TX_CHANNELS(comp1); + kmb_i2s_dai->playback.formats = formats[idx]; + kmb_i2s_dai->playback.rates = rates; + } + + if (COMP1_RX_ENABLED(comp1)) { + idx = COMP2_RX_WORDSIZE_0(comp2); + kmb_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; + kmb_i2s_dai->capture.channels_max = + 1 << COMP1_RX_CHANNELS(comp1); + kmb_i2s_dai->capture.formats = formats[idx]; + kmb_i2s_dai->capture.rates = rates; + } + + if (COMP1_MODE_EN(comp1)) { + dev_dbg(kmb_i2s->dev, "kmb: i2s master mode supported\n"); + kmb_i2s->capability |= DW_I2S_MASTER; + } else { + dev_dbg(kmb_i2s->dev, "kmb: i2s slave mode supported\n"); + kmb_i2s->capability |= DW_I2S_SLAVE; + } + + kmb_i2s->fifo_th = fifo_depth / 2; + + return 0; +} + +static int kmb_configure_dai_by_dt(struct kmb_i2s_info *kmb_i2s, + struct snd_soc_dai_driver *kmb_i2s_dai) +{ + u32 comp1 = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1); + + if (COMP1_TX_ENABLED(comp1)) + kmb_i2s->capability |= DWC_I2S_PLAY; + + if (COMP1_RX_ENABLED(comp1)) + kmb_i2s->capability |= DWC_I2S_RECORD; + + return kmb_configure_dai(kmb_i2s, kmb_i2s_dai, + I2S_SAMPLE_RATES); +} + +static void kmb_disable_clk(void *data) +{ + clk_disable_unprepare(data); +} + +static int kmb_plat_dai_probe(struct platform_device *pdev) +{ + struct snd_soc_dai_driver *kmb_i2s_dai; + struct device *dev = &pdev->dev; + struct kmb_i2s_info *kmb_i2s; + const char *i2s_mode; + int ret, irq; + + kmb_i2s = devm_kzalloc(dev, sizeof(*kmb_i2s), GFP_KERNEL); + if (!kmb_i2s) + return -ENOMEM; + + kmb_i2s_dai = devm_kzalloc(dev, sizeof(*kmb_i2s_dai), GFP_KERNEL); + if (!kmb_i2s_dai) + return -ENOMEM; + + kmb_i2s_dai->ops = &kmb_dai_ops; + + kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk"); + if (IS_ERR(kmb_i2s->clk_apb)) { + dev_err(dev, "Failed to get apb clock\n"); + return PTR_ERR(kmb_i2s->clk_apb); + } + + /* Enable apb clk prior to access to the registers */ + ret = clk_prepare_enable(kmb_i2s->clk_apb); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb); + if (ret) { + dev_err(dev, "Failed to add clk_apb reset action\n"); + return ret; + } + + kmb_i2s->i2s_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(kmb_i2s->i2s_base)) + return PTR_ERR(kmb_i2s->i2s_base); + + kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(kmb_i2s->pss_base)) + return PTR_ERR(kmb_i2s->pss_base); + + kmb_i2s->dev = &pdev->dev; + + irq = platform_get_irq_optional(pdev, 0); + if (irq > 0) { + ret = devm_request_irq(dev, irq, i2s_irq_handler, 0, + pdev->name, kmb_i2s); + if (ret < 0) { + dev_err(dev, "failed to request irq\n"); + return ret; + } + } + + ret = of_property_read_string(dev->of_node, "mode", &i2s_mode); + if (ret < 0) { + dev_err(dev, "Couldn't find the entry\n"); + return -EINVAL; + } + + dev_dbg(kmb_i2s->dev, "Mode = %s", i2s_mode); + + ret = match_string(i2s_mode_name, ARRAY_SIZE(i2s_mode_name), i2s_mode); + + switch (ret) { + case I2S_MASTER_MODE: + kmb_i2s->master = true; + /* Set the mode prior to access to the rest of the register */ + writel(MASTER_MODE, kmb_i2s->pss_base + I2S_GEN_CFG_0); + break; + case I2S_SLAVE_MODE: + kmb_i2s->master = false; + break; + default: + dev_err(dev, "invalid i2s mode '%s'\n", i2s_mode); + return ret; + } + + kmb_i2s->i2s_reg_comp1 = I2S_COMP_PARAM_1; + kmb_i2s->i2s_reg_comp2 = I2S_COMP_PARAM_2; + + ret = kmb_configure_dai_by_dt(kmb_i2s, kmb_i2s_dai); + if (ret < 0) + return ret; + + ret = devm_snd_soc_register_component(dev, &kmb_component, + intel_kmb_platform_dai, + ARRAY_SIZE(intel_kmb_platform_dai)); + if (ret) { + dev_err(dev, "not able to register dai\n"); + return ret; + } + + if (kmb_i2s->master) { + kmb_i2s->clk_i2s = devm_clk_get(dev, "osc"); + if (IS_ERR(kmb_i2s->clk_i2s)) { + dev_err(dev, "Failed to get osc clock\n"); + return PTR_ERR(kmb_i2s->clk_i2s); + } + + ret = clk_prepare_enable(kmb_i2s->clk_i2s); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(dev, kmb_disable_clk, + kmb_i2s->clk_i2s); + if (ret) { + dev_err(dev, "Failed to add clk_i2s reset action\n"); + return ret; + } + } + + dev_set_drvdata(dev, kmb_i2s); + + return ret; +} + +static const struct of_device_id kmb_plat_of_match[] = { + { .compatible = "snps,designware-i2s", }, + {} +}; + +static struct platform_driver kmb_plat_dai_driver = { + .driver = { + .name = "kmb-plat-dai", + .of_match_table = kmb_plat_of_match, + }, + .probe = kmb_plat_dai_probe, +}; + +module_platform_driver(kmb_plat_dai_driver); + +MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver"); +MODULE_AUTHOR("Sia Jee Heng "); +MODULE_AUTHOR("Sit, Michael Wei Hong "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kmb_platform"); diff --git a/sound/soc/intel/keembay/kmb_platform.h b/sound/soc/intel/keembay/kmb_platform.h new file mode 100644 index 0000000..2960065 --- /dev/null +++ b/sound/soc/intel/keembay/kmb_platform.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Intel KeemBay Platform driver + * + * Copyright (C) 2020 Intel Corporation. + * + */ + +#ifndef KMB_PLATFORM_H_ +#define KMB_PLATFORMP_H_ + +#include +#include +#include + +/* Register values with reference to KMB databook v1.1 */ +/* common register for all channel */ +#define IER 0x000 +#define IRER 0x004 +#define ITER 0x008 +#define CER 0x00C +#define CCR 0x010 +#define RXFFR 0x014 +#define TXFFR 0x018 + +/* Interrupt status register fields */ +#define ISR_TXFO BIT(5) +#define ISR_TXFE BIT(4) +#define ISR_RXFO BIT(1) +#define ISR_RXDA BIT(0) + +/* I2S Tx Rx Registers for all channels */ +#define LRBR_LTHR(x) (0x40 * (x) + 0x020) +#define RRBR_RTHR(x) (0x40 * (x) + 0x024) +#define RER(x) (0x40 * (x) + 0x028) +#define TER(x) (0x40 * (x) + 0x02C) +#define RCR(x) (0x40 * (x) + 0x030) +#define TCR(x) (0x40 * (x) + 0x034) +#define ISR(x) (0x40 * (x) + 0x038) +#define IMR(x) (0x40 * (x) + 0x03C) +#define ROR(x) (0x40 * (x) + 0x040) +#define TOR(x) (0x40 * (x) + 0x044) +#define RFCR(x) (0x40 * (x) + 0x048) +#define TFCR(x) (0x40 * (x) + 0x04C) +#define RFF(x) (0x40 * (x) + 0x050) +#define TFF(x) (0x40 * (x) + 0x054) + +/* I2S COMP Registers */ +#define I2S_COMP_PARAM_2 0x01F0 +#define I2S_COMP_PARAM_1 0x01F4 +#define I2S_COMP_VERSION 0x01F8 +#define I2S_COMP_TYPE 0x01FC + +/* PSS_GEN_CTRL_I2S_GEN_CFG_0 Registers */ +#define I2S_GEN_CFG_0 0x000 +#define PSS_CPR_RST_EN 0x010 +#define PSS_CPR_RST_SET 0x014 +#define PSS_CPR_CLK_CLR 0x000 +#define PSS_CPR_AUX_RST_EN 0x070 + +#define MASTER_MODE BIT(13) + +/* Interrupt Flag */ +#define TX_INT_FLAG GENMASK(5, 4) +#define RX_INT_FLAG GENMASK(1, 0) +/* + * Component parameter register fields - define the I2S block's + * configuration. + */ +#define COMP1_TX_WORDSIZE_3(r) FIELD_GET(GENMASK(27, 25), (r)) +#define COMP1_TX_WORDSIZE_2(r) FIELD_GET(GENMASK(24, 22), (r)) +#define COMP1_TX_WORDSIZE_1(r) FIELD_GET(GENMASK(21, 19), (r)) +#define COMP1_TX_WORDSIZE_0(r) FIELD_GET(GENMASK(18, 16), (r)) +#define COMP1_RX_ENABLED(r) FIELD_GET(BIT(6), (r)) +#define COMP1_TX_ENABLED(r) FIELD_GET(BIT(5), (r)) +#define COMP1_MODE_EN(r) FIELD_GET(BIT(4), (r)) +#define COMP1_APB_DATA_WIDTH(r) FIELD_GET(GENMASK(1, 0), (r)) +#define COMP2_RX_WORDSIZE_3(r) FIELD_GET(GENMASK(12, 10), (r)) +#define COMP2_RX_WORDSIZE_2(r) FIELD_GET(GENMASK(9, 7), (r)) +#define COMP2_RX_WORDSIZE_1(r) FIELD_GET(GENMASK(5, 3), (r)) +#define COMP2_RX_WORDSIZE_0(r) FIELD_GET(GENMASK(2, 0), (r)) + +/* Add 1 to the below registers to indicate the actual size */ +#define COMP1_TX_CHANNELS(r) (FIELD_GET(GENMASK(10, 9), (r)) + 1) +#define COMP1_RX_CHANNELS(r) (FIELD_GET(GENMASK(8, 7), (r)) + 1) +#define COMP1_FIFO_DEPTH(r) (FIELD_GET(GENMASK(3, 2), (r)) + 1) + +/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */ +#define COMP_MAX_WORDSIZE 8 /* 3 bits register width */ + +#define MAX_CHANNEL_NUM 8 +#define MIN_CHANNEL_NUM 2 + +#define TWO_CHANNEL_SUPPORT 2 /* up to 2.0 */ +#define FOUR_CHANNEL_SUPPORT 4 /* up to 3.1 */ +#define SIX_CHANNEL_SUPPORT 6 /* up to 5.1 */ +#define EIGHT_CHANNEL_SUPPORT 8 /* up to 7.1 */ + +#define DWC_I2S_PLAY BIT(0) +#define DWC_I2S_RECORD BIT(1) +#define DW_I2S_SLAVE BIT(2) +#define DW_I2S_MASTER BIT(3) + +#define I2S_RXDMA 0x01C0 +#define I2S_TXDMA 0x01C8 + +/* + * struct i2s_clk_config_data - represent i2s clk configuration data + * @chan_nr: number of channel + * @data_width: number of bits per sample (8/16/24/32 bit) + * @sample_rate: sampling frequency (8Khz, 16Khz, 48Khz) + */ +struct i2s_clk_config_data { + int chan_nr; + u32 data_width; + u32 sample_rate; +}; + +struct kmb_i2s_info { + void __iomem *i2s_base; + void __iomem *pss_base; + struct clk *clk_i2s; + struct clk *clk_apb; + int active; + unsigned int capability; + unsigned int i2s_reg_comp1; + unsigned int i2s_reg_comp2; + struct device *dev; + u32 ccr; + u32 xfer_resolution; + u32 fifo_th; + bool master; + + struct i2s_clk_config_data config; + int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); + + /* data related to PIO transfers */ + bool use_pio; + struct snd_pcm_substream *tx_substream; + struct snd_pcm_substream *rx_substream; + unsigned int tx_ptr; + unsigned int rx_ptr; +}; + +#endif /* KMB_PLATFORM_H_ */ From patchwork Mon May 11 03:16:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Sia, Jee Heng" X-Patchwork-Id: 192773 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3A622C54E4A for ; Mon, 11 May 2020 03:31:49 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id BAB3120746 for ; Mon, 11 May 2020 03:31:48 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=alsa-project.org header.i=@alsa-project.org header.b="sVAtLxl6" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org BAB3120746 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=intel.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=alsa-devel-bounces@alsa-project.org Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 15C0E15F2; Mon, 11 May 2020 05:30:57 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 15C0E15F2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1589167907; bh=OCnoVpjZuzeyPykHwK9AlEwcRpV7E9uMuT6f2cxq7+4=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=sVAtLxl6WAqstXytQCV8TkpsdzE6XHBgNSsJ8BXpSFVZGGO9znhN5O38pTJLdj6Bo ErGIRQgzh+iBH9LFwrzIIYm/Kj9CsrMsSRpKSigi7Xc9VkLZ7pmB1kEYVq9yiGBVc7 jcAUdz/oDJx/clWM+PFrKWvz9fejEoUHKObcv+Ns= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id A80E0F8029B; Mon, 11 May 2020 05:29:24 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 9991FF8014C; Mon, 11 May 2020 05:29:04 +0200 (CEST) Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 55A2EF8014C for ; Mon, 11 May 2020 05:28:56 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 55A2EF8014C IronPort-SDR: 2eGdvmG56gdSIeLNAt/RUKYPlsZiYQ3Zcyk+ARjEJRyRPNQnpA+Dc0KM6SrcbPM549aZeqY5JF pO1bMCMNSKJA== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 10 May 2020 20:28:53 -0700 IronPort-SDR: cYyaAsNLwz6zYN56IzkafeCqjkeayc0OiFBTO72rVXq11EEmdbCZszrX7TYxBY+YevG9VmW7BM ZxL/9LvCuCCA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.73,378,1583222400"; d="scan'208";a="265024245" Received: from unknown (HELO jsia-HP-Z620-Workstation.png.intel.com) ([10.221.118.135]) by orsmga006.jf.intel.com with ESMTP; 10 May 2020 20:28:51 -0700 From: Sia Jee Heng To: Subject: [PATCH 2/4] ASoC: Intel: Boards: Add KeemBay machine driver Date: Mon, 11 May 2020 11:16:02 +0800 Message-Id: <1589166964-8985-3-git-send-email-jee.heng.sia@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1589166964-8985-1-git-send-email-jee.heng.sia@intel.com> References: <1589166964-8985-1-git-send-email-jee.heng.sia@intel.com> Cc: liam.r.girdwood@linux.intel.com, broonie@kernel.org, tiwai@suse.com, pierre-louis.bossart@linux.intel.com X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" Add KeemBay machine driver which glues the tlv320aic3204 codec driver and kmb_platform driver to form the asoc sound driver. Signed-off-by: Michael Sit Wei Hong Signed-off-by: Sia Jee Heng --- sound/soc/intel/boards/kmb_tlv3204.c | 144 +++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 sound/soc/intel/boards/kmb_tlv3204.c diff --git a/sound/soc/intel/boards/kmb_tlv3204.c b/sound/soc/intel/boards/kmb_tlv3204.c new file mode 100644 index 0000000..813c291 --- /dev/null +++ b/sound/soc/intel/boards/kmb_tlv3204.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* KeemBay ASOC Machine driver + * + * Copyright (C) 2020 Intel Corporation. + * + */ + +#include +#include +#include +#include "../../codecs/tlv320aic32x4.h" + +static unsigned int channels[] = { + 2, +}; + +static struct snd_pcm_hw_constraint_list constraints_ch = { + .count = ARRAY_SIZE(channels), + .list = channels, +}; + +static unsigned int rates[] = { + 16000, + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, +}; + +static int kmb_mach_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 *codec_dai = asoc_rtd_to_codec(rtd, 0); + int ret; + unsigned int sysclk; + + /* As per codec datasheet Sysclk = 256 * fs */ + sysclk = 12288000; + + /* set the codec system clock */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, sysclk, SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + + return ret; +} + +static int kmb_mach_dai_link_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *str_runtime; + + str_runtime = substream->runtime; + + snd_pcm_hw_constraint_list(str_runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_ch); + + snd_pcm_hw_constraint_list(str_runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops kmb_mach_dai_link_ops = { + .startup = kmb_mach_dai_link_startup, + .hw_params = kmb_mach_hw_params, +}; + +static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + SND_SOC_DAPM_MIC("External Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), +}; + +static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + {"Headphone", NULL, "HPL"}, + {"Headphone", NULL, "HPR"}, + {"IN3_R", NULL, "External Mic"}, + {"IN3_L", NULL, "External Mic"}, +}; + +/* Linking platform to the codec-drivers */ +SND_SOC_DAILINK_DEFS(link1, + DAILINK_COMP_ARRAY(COMP_CPU("20140000.i2s")), + DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic32x4.2-0018", + "tlv320aic32x4-hifi")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("20140000.i2s"))); + +/* kmb digital audio interface glue */ +static struct snd_soc_dai_link kmb_mach_dais[] = { + { + .name = "tlv320aic32x4", + .stream_name = "TLV320AIC32X4", + .ops = &kmb_mach_dai_link_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAILINK_REG(link1), + }, +}; + +/* kmb audio machine driver */ +static struct snd_soc_card kmb_mach = { + .name = "kmb_audio_card", + .dai_link = kmb_mach_dais, + .num_links = ARRAY_SIZE(kmb_mach_dais), + .dapm_routes = aic32x4_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), + .dapm_widgets = aic32x4_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), + .fully_routed = true, +}; + +static int kmb_mach_audio_probe(struct platform_device *pdev) +{ + kmb_mach.dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, &kmb_mach); +} + +static const struct of_device_id kmb_mach_of_match[] = { + { .compatible = "intel,kmb-snd-asoc", }, + {} +}; + +static struct platform_driver kmb_mach_audio = { + .probe = kmb_mach_audio_probe, + .driver = { + .name = "kmb_tlv3204", + .of_match_table = kmb_mach_of_match, + }, +}; + +module_platform_driver(kmb_mach_audio) + +/* Module information */ +MODULE_DESCRIPTION("Intel Audio tlv3204 machine driver for KeemBay"); +MODULE_AUTHOR("Sia Jee Heng "); +MODULE_AUTHOR("Sit, Michael Wei Hong "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kmb_tlv3204"); From patchwork Mon May 11 03:16:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Sia, Jee Heng" X-Patchwork-Id: 192772 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A7C56C47255 for ; Mon, 11 May 2020 03:32:47 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 2DDFC20746 for ; Mon, 11 May 2020 03:32:47 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=alsa-project.org header.i=@alsa-project.org header.b="R774vb0k" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2DDFC20746 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=intel.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=alsa-devel-bounces@alsa-project.org Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 8A7E01607; Mon, 11 May 2020 05:31:55 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 8A7E01607 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1589167965; bh=wgtV58CgVrAhWp1/Sl8bx6uIdQUbsWhZ1skPMEEHBHo=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=R774vb0kzM8ppbwSJQvzOSe75kJu2gF1Q7stcYxxZ1sHQyInuuaTh6cCW5bv4YBj3 an2tLraiy0EdEpyJ/0epSnv25/DoDnAKS8G6Fe5/FYZCHhIX4P8zIAkVAmQ/b9LQnX WDFaKLsFPX6MusHsk6+NejEVnLKSGibIsaSGsRq8= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 485E3F802A7; Mon, 11 May 2020 05:30:08 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 0415FF802A8; Mon, 11 May 2020 05:30:00 +0200 (CEST) Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 10019F801F2 for ; Mon, 11 May 2020 05:28:56 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 10019F801F2 IronPort-SDR: XvoUgjKt/454FKAk9HQSgqW56k9buJTEyWd1bB6nubLkUtSuBRDdIH8m0+28DnpD8ub2ebbDEb qFxsZYoeLLaw== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 10 May 2020 20:28:55 -0700 IronPort-SDR: yC8r2tBV5ROuMOeSXZxfwtj4MP29tz64nAcD0TaUEOHotyyUl2/VwMHPtcwR5MWeilMXiT9lD/ E4GUtUSfZxLQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.73,378,1583222400"; d="scan'208";a="265024252" Received: from unknown (HELO jsia-HP-Z620-Workstation.png.intel.com) ([10.221.118.135]) by orsmga006.jf.intel.com with ESMTP; 10 May 2020 20:28:54 -0700 From: Sia Jee Heng To: Subject: [PATCH 3/4] ASoC: Intel: Add makefiles and kconfig changes for KeemBay Date: Mon, 11 May 2020 11:16:03 +0800 Message-Id: <1589166964-8985-4-git-send-email-jee.heng.sia@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1589166964-8985-1-git-send-email-jee.heng.sia@intel.com> References: <1589166964-8985-1-git-send-email-jee.heng.sia@intel.com> Cc: liam.r.girdwood@linux.intel.com, broonie@kernel.org, tiwai@suse.com, pierre-louis.bossart@linux.intel.com X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" Add makefile and kconfig changes for KeemBay tlv320aic3204 machine driver and kmb_platform driver. Signed-off-by: Michael Sit Wei Hong Signed-off-by: Sia Jee Heng --- sound/soc/intel/Kconfig | 7 +++++++ sound/soc/intel/Makefile | 1 + sound/soc/intel/boards/Kconfig | 15 +++++++++++++++ sound/soc/intel/boards/Makefile | 4 ++++ 4 files changed, 27 insertions(+) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index c8de0bb..bc93448 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -244,6 +244,13 @@ config SND_SOC_ACPI_INTEL_MATCH endif ## SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL_TOPLEVEL +config SND_SOC_INTEL_KEEMBAY + tristate "Keembay Platforms" + depends on OF && (ARM64 || COMPILE_TEST) + depends on COMMON_CLK + help + If you have a Intel Keembay platform then enable this option + by saying Y or m. # ASoC codec drivers source "sound/soc/intel/boards/Kconfig" diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 8160520..f5aa32b 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/ obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += atom/ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/ +obj-$(CONFIG_SND_SOC_INTEL_KEEMBAY) += keembay/ # Machine support obj-$(CONFIG_SND_SOC) += boards/ diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 556c310..45f9fe5 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -549,3 +549,18 @@ endif endif ## SND_SOC_INTEL_MACH + +if SND_SOC_INTEL_KEEMBAY + +config SND_SOC_INTEL_KEEMBAY_TLV320AIC3204_MACH + tristate "Keembay with TLV320AIC3204 codec" + depends on ARM64 || COMPILE_TEST + depends on I2C + select SND_SOC_TLV320AIC32X4 + select SND_SOC_TLV320AIC32X4_I2C + help + This adds support for ASoC machine driver for Intel Keembay platforms + with TLV320AIC3204 codec. + Say Y if you have such a device. + If unsure select "N". +endif ## SND_SOC_INTEL_KEEMBAY diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 1ef6e60..7201d07 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -69,3 +69,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ss obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o obj-$(CONFIG_SND_SOC_INTEL_SOF_DA7219_MAX98373_MACH) += snd-soc-sof_da7219_max98373.o obj-$(CONFIG_SND_SOC_INTEL_SOUNDWIRE_SOF_MACH) += snd-soc-sof-sdw.o + +# Intel KeemBay Machine +snd-soc-keembay_tlv3204-objs := kmb_tlv3204.o +obj-$(CONFIG_SND_SOC_INTEL_KEEMBAY_TLV320AIC3204_MACH) += snd-soc-keembay_tlv3204.o