From patchwork Tue Jul 17 15:43:00 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jerome Brunet X-Patchwork-Id: 142177 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp3820840ljj; Tue, 17 Jul 2018 08:43:31 -0700 (PDT) X-Google-Smtp-Source: AAOMgpeqj0WkiHAirMAHU45JK+YnpeQsqIIzvIB95SnN7a7mxr7cefvF3KgGTA7keZCs6BXY6koD X-Received: by 2002:a63:291:: with SMTP id 139-v6mr2094663pgc.365.1531842210990; Tue, 17 Jul 2018 08:43:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1531842210; cv=none; d=google.com; s=arc-20160816; b=XfkVgRnhlvWt5op7ZjxouaBpECAeYRuG4xP0Y/XL9m0l+kWccVF3n63HI5P5Ou+R4E lBhJCssCA3sZWUust6ISDV9OC2qJWt1yDSs1xIzrbxqdzBVQyFXxaXT+nrYWLixfx3ro pK2P3C0oBmiwYet3ExYfRLeykqjrRDbZ2QSsy2kxhKuF/18cyQpbDc1twsgSJ3lqVXVA OYRDkdqXq+JmtgAX4muGDQb2Pn5cJdk8A0s5+OjZDf/WIhB1j5GsrqSwSUok1FKB+bur MLmX50GKBBUUrsUyQPg1sWcKRAD0Sce832QHCajhcbGFT+o749vvK4szXykDib6PUnyL s7tQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=9Vq7WqNYq1Y6EqDZPVqrYIfFVBrINTzO3zRHsykCFCk=; b=I04WELFELxNhiycjQHgpnhFrgJ/sMDFU9Y5VbJhVsP3OKX65c68zbpNhtq/UaEAptQ SmHkVuq2N5VDXMKbECA+/6OnYkhUNB3ls/W3JTgSPwYzUBefs/WBEnl5ZEzn2si5GFkK b5zH331MArkyEO1Pl0r27iAStn/gre5/b/iys4OVLXJOIHx+Tj4vEnJf8lFEbOTB1xV6 fFMD/XRnmVWzPWUGiCHdO6m8iJlXgqf3YDsg5C6x3CTyx1q59ctLM0H9ox/v41ePHOWb aiJuPwWuguGJ+xFFnH8riI9OK/HcGZQI2BJJ1JNTpmxCbfKsf+uUV34PLJ3xEZbcEfUo dFRQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@baylibre-com.20150623.gappssmtp.com header.s=20150623 header.b=GFh3XZBe; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id j10-v6si1166935pfc.335.2018.07.17.08.43.30; Tue, 17 Jul 2018 08:43:30 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@baylibre-com.20150623.gappssmtp.com header.s=20150623 header.b=GFh3XZBe; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731608AbeGQQQm (ORCPT + 31 others); Tue, 17 Jul 2018 12:16:42 -0400 Received: from mail-wm0-f65.google.com ([74.125.82.65]:55285 "EHLO mail-wm0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731545AbeGQQQl (ORCPT ); Tue, 17 Jul 2018 12:16:41 -0400 Received: by mail-wm0-f65.google.com with SMTP id c14-v6so1993465wmb.4 for ; Tue, 17 Jul 2018 08:43:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=9Vq7WqNYq1Y6EqDZPVqrYIfFVBrINTzO3zRHsykCFCk=; b=GFh3XZBeqjVhELdr9jTXiQZ8iL3FqWyTwP3FweOqgLfCbOQ+D1R9+iKE4VIk9xwCyt OyaxvQMaC+2YcbOzyFCPjpHxliRGuwvrapdQQbREhJ86QflR5ObJ08pr11ykJrqVOnu1 Sjg0WJhAOnof/hkL3v4OGfPGo/4JZWx67JeUxspkfHg8RmjTX1/rMqQbchLK8q1zyq6x aVzY+MJqNbY/ImCj1+VBZMotZL8stOeW28oDf7QqryIF0EQ8GPaQ8OV/ajfWKxlYMe53 Kfo3QEKg7XbhIfnEj6IOZ1NSX7TmpEc5uEPDaQsDDSzgUjsIQ3iJtmA88pLqtBO/vF38 j5LA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=9Vq7WqNYq1Y6EqDZPVqrYIfFVBrINTzO3zRHsykCFCk=; b=bXhx/vx/d/PZA0Y7n4Dm6tey300ROVFJ+NN8g4Y8bwB7jv3quczBIhP9gEbQCQivzN rTtkOVlrLoefGx6RHf4wW+24aoppoWAwKCc06PD/nSaLfV8VJH4sfgCnPaSEOgYQEuoN Tor0tM0yVCwZrn2du71KKrd2eNzRJ6R0yWSiXfWmoLh1O71VyzOlcjdhuAhZuI2Mmz3M GOqCjtLESYO4T5Si7GAAM6H+qPY1/oE3ttxjfuYGeKdcb7GyQ43Y9/wORBYl6MoD+I5k 6A2mMnX4rDIxjTYsGzfMee5Y2ZPm9s/SxEYHM7WC3KrjfFFtz5xR5JnYDDDNRazcFkmg VaGw== X-Gm-Message-State: AOUpUlENMz2N3xFYQ27fbzYFYD4FXNwZ9VMx7ey4tQmKEffv64OXFlDg 4r+BUs5rouC3DNgMweex6kgEbQ== X-Received: by 2002:a1c:b9cf:: with SMTP id j198-v6mr1759703wmf.93.1531842205406; Tue, 17 Jul 2018 08:43:25 -0700 (PDT) Received: from boomer.baylibre.local ([90.63.244.31]) by smtp.googlemail.com with ESMTPSA id s12-v6sm17598252wmc.2.2018.07.17.08.43.24 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Tue, 17 Jul 2018 08:43:24 -0700 (PDT) From: Jerome Brunet To: Mark Brown , Liam Girdwood , Kevin Hilman , Carlo Caione Cc: Jerome Brunet , alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-amlogic@lists.infradead.org Subject: [PATCH RESEND 11/15] ASoC: meson: add tdm output driver Date: Tue, 17 Jul 2018 17:43:00 +0200 Message-Id: <20180717154304.9973-12-jbrunet@baylibre.com> X-Mailer: git-send-email 2.14.4 In-Reply-To: <20180717154304.9973-1-jbrunet@baylibre.com> References: <20180717154304.9973-1-jbrunet@baylibre.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add Amlogic's axg tdm output driver which pulls data from FRDDR fifo and produce the TDM signals for 4 output lanes. Signed-off-by: Jerome Brunet --- sound/soc/meson/Kconfig | 8 ++ sound/soc/meson/Makefile | 2 + sound/soc/meson/axg-tdmout.c | 259 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 sound/soc/meson/axg-tdmout.c -- 2.14.4 diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig index 869b359c2ce7..08a522b77749 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig @@ -27,6 +27,14 @@ config SND_MESON_AXG_TDM_INTERFACE tristate select SND_MESON_AXG_TDM_FORMATTER +config SND_MESON_AXG_TDMOUT + tristate "Amlogic AXG TDM Output Support" + select SND_MESON_AXG_TDM_FORMATTER + select SND_MESON_AXG_TDM_INTERFACE + help + Select Y or M to add support for TDM output formatter embedded + in the Amlogic AXG SoC family + config SND_MESON_AXG_SPDIFOUT tristate "Amlogic AXG SPDIF Output Support" imply SND_SOC_SPDIF diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile index 1a8eb77402e3..a665c6b76a95 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile @@ -5,6 +5,7 @@ snd-soc-meson-axg-frddr-objs := axg-frddr.o snd-soc-meson-axg-toddr-objs := axg-toddr.o snd-soc-meson-axg-tdm-formatter-objs := axg-tdm-formatter.o snd-soc-meson-axg-tdm-interface-objs := axg-tdm-interface.o +snd-soc-meson-axg-tdmout-objs := axg-tdmout.o snd-soc-meson-axg-spdifout-objs := axg-spdifout.o obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o @@ -12,4 +13,5 @@ obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o obj-$(CONFIG_SND_MESON_AXG_TDM_FORMATTER) += snd-soc-meson-axg-tdm-formatter.o obj-$(CONFIG_SND_MESON_AXG_TDM_INTERFACE) += snd-soc-meson-axg-tdm-interface.o +obj-$(CONFIG_SND_MESON_AXG_TDMOUT) += snd-soc-meson-axg-tdmout.o obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o diff --git a/sound/soc/meson/axg-tdmout.c b/sound/soc/meson/axg-tdmout.c new file mode 100644 index 000000000000..f73368ee1088 --- /dev/null +++ b/sound/soc/meson/axg-tdmout.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet + +#include +#include +#include +#include +#include + +#include "axg-tdm-formatter.h" + +#define TDMOUT_CTRL0 0x00 +#define TDMOUT_CTRL0_BITNUM_MASK GENMASK(4, 0) +#define TDMOUT_CTRL0_BITNUM(x) ((x) << 0) +#define TDMOUT_CTRL0_SLOTNUM_MASK GENMASK(9, 5) +#define TDMOUT_CTRL0_SLOTNUM(x) ((x) << 5) +#define TDMOUT_CTRL0_INIT_BITNUM_MASK GENMASK(19, 15) +#define TDMOUT_CTRL0_INIT_BITNUM(x) ((x) << 15) +#define TDMOUT_CTRL0_ENABLE BIT(31) +#define TDMOUT_CTRL0_RST_OUT BIT(29) +#define TDMOUT_CTRL0_RST_IN BIT(28) +#define TDMOUT_CTRL1 0x04 +#define TDMOUT_CTRL1_TYPE_MASK GENMASK(6, 4) +#define TDMOUT_CTRL1_TYPE(x) ((x) << 4) +#define TDMOUT_CTRL1_MSB_POS_MASK GENMASK(12, 8) +#define TDMOUT_CTRL1_MSB_POS(x) ((x) << 8) +#define TDMOUT_CTRL1_SEL_SHIFT 24 +#define TDMOUT_CTRL1_GAIN_EN 26 +#define TDMOUT_CTRL1_WS_INV BIT(28) +#define TDMOUT_SWAP 0x08 +#define TDMOUT_MASK0 0x0c +#define TDMOUT_MASK1 0x10 +#define TDMOUT_MASK2 0x14 +#define TDMOUT_MASK3 0x18 +#define TDMOUT_STAT 0x1c +#define TDMOUT_GAIN0 0x20 +#define TDMOUT_GAIN1 0x24 +#define TDMOUT_MUTE_VAL 0x28 +#define TDMOUT_MUTE0 0x2c +#define TDMOUT_MUTE1 0x30 +#define TDMOUT_MUTE2 0x34 +#define TDMOUT_MUTE3 0x38 +#define TDMOUT_MASK_VAL 0x3c + +static const struct regmap_config axg_tdmout_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = TDMOUT_MASK_VAL, +}; + +static const struct snd_kcontrol_new axg_tdmout_controls[] = { + SOC_DOUBLE("Lane 0 Volume", TDMOUT_GAIN0, 0, 8, 255, 0), + SOC_DOUBLE("Lane 1 Volume", TDMOUT_GAIN0, 16, 24, 255, 0), + SOC_DOUBLE("Lane 2 Volume", TDMOUT_GAIN1, 0, 8, 255, 0), + SOC_DOUBLE("Lane 3 Volume", TDMOUT_GAIN1, 16, 24, 255, 0), + SOC_SINGLE("Gain Enable Switch", TDMOUT_CTRL1, + TDMOUT_CTRL1_GAIN_EN, 1, 0), +}; + +static const char * const tdmout_sel_texts[] = { + "IN 0", "IN 1", "IN 2", +}; + +static SOC_ENUM_SINGLE_DECL(axg_tdmout_sel_enum, TDMOUT_CTRL1, + TDMOUT_CTRL1_SEL_SHIFT, tdmout_sel_texts); + +static const struct snd_kcontrol_new axg_tdmout_in_mux = + SOC_DAPM_ENUM("Input Source", axg_tdmout_sel_enum); + +static struct snd_soc_dai * +axg_tdmout_get_be(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dai *be; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (!p->connect) + continue; + + if (p->sink->id == snd_soc_dapm_dai_in) + return (struct snd_soc_dai *)p->sink->priv; + + be = axg_tdmout_get_be(p->sink); + if (be) + return be; + } + + return NULL; +} + +static struct axg_tdm_stream * +axg_tdmout_get_tdm_stream(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dai *be = axg_tdmout_get_be(w); + + if (!be) + return NULL; + + return be->playback_dma_data; +} + +static void axg_tdmout_enable(struct regmap *map) +{ + /* Apply both reset */ + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_RST_OUT | TDMOUT_CTRL0_RST_IN, 0); + + /* Clear out reset before in reset */ + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_RST_OUT, TDMOUT_CTRL0_RST_OUT); + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_RST_IN, TDMOUT_CTRL0_RST_IN); + + /* Actually enable tdmout */ + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_ENABLE, TDMOUT_CTRL0_ENABLE); +} + +static void axg_tdmout_disable(struct regmap *map) +{ + regmap_update_bits(map, TDMOUT_CTRL0, TDMOUT_CTRL0_ENABLE, 0); +} + +static int axg_tdmout_prepare(struct regmap *map, struct axg_tdm_stream *ts) +{ + unsigned int val = 0; + + /* Set the stream skew */ + switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + val |= TDMOUT_CTRL0_INIT_BITNUM(1); + break; + + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_DSP_B: + val |= TDMOUT_CTRL0_INIT_BITNUM(2); + break; + + default: + pr_err("Unsupported format: %u\n", + ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + /* Set the slot width */ + val |= TDMOUT_CTRL0_BITNUM(ts->iface->slot_width - 1); + + /* Set the slot number */ + val |= TDMOUT_CTRL0_SLOTNUM(ts->iface->slots - 1); + + regmap_update_bits(map, TDMOUT_CTRL0, + TDMOUT_CTRL0_INIT_BITNUM_MASK | + TDMOUT_CTRL0_BITNUM_MASK | + TDMOUT_CTRL0_SLOTNUM_MASK, val); + + /* Set the sample width */ + val = TDMOUT_CTRL1_MSB_POS(ts->width - 1); + + /* FIFO data are arranged in chunks of 64bits */ + switch (ts->physical_width) { + case 8: + /* 8 samples of 8 bits */ + val |= TDMOUT_CTRL1_TYPE(0); + break; + case 16: + /* 4 samples of 16 bits - right justified */ + val |= TDMOUT_CTRL1_TYPE(2); + break; + case 32: + /* 2 samples of 32 bits - right justified */ + val |= TDMOUT_CTRL1_TYPE(4); + break; + default: + pr_err("Unsupported physical width: %u\n", + ts->physical_width); + return -EINVAL; + } + + /* If the sample clock is inverted, invert it back for the formatter */ + if (axg_tdm_lrclk_invert(ts->iface->fmt)) + val |= TDMOUT_CTRL1_WS_INV; + + regmap_update_bits(map, TDMOUT_CTRL1, + (TDMOUT_CTRL1_TYPE_MASK | TDMOUT_CTRL1_MSB_POS_MASK | + TDMOUT_CTRL1_WS_INV), val); + + /* Set static swap mask configuration */ + regmap_write(map, TDMOUT_SWAP, 0x76543210); + + return axg_tdm_formatter_set_channel_masks(map, ts, TDMOUT_MASK0); +} + +static const struct snd_soc_dapm_widget axg_tdmout_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmout_in_mux), + SND_SOC_DAPM_PGA_E("ENC", SND_SOC_NOPM, 0, 0, NULL, 0, + axg_tdm_formatter_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)), + SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route axg_tdmout_dapm_routes[] = { + { "SRC SEL", "IN 0", "IN 0" }, + { "SRC SEL", "IN 1", "IN 1" }, + { "SRC SEL", "IN 2", "IN 2" }, + { "ENC", NULL, "SRC SEL" }, + { "OUT", NULL, "ENC" }, +}; + +static const struct snd_soc_component_driver axg_tdmout_component_drv = { + .controls = axg_tdmout_controls, + .num_controls = ARRAY_SIZE(axg_tdmout_controls), + .dapm_widgets = axg_tdmout_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(axg_tdmout_dapm_widgets), + .dapm_routes = axg_tdmout_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(axg_tdmout_dapm_routes), +}; + +static const struct axg_tdm_formatter_ops axg_tdmout_ops = { + .get_stream = axg_tdmout_get_tdm_stream, + .prepare = axg_tdmout_prepare, + .enable = axg_tdmout_enable, + .disable = axg_tdmout_disable, +}; + +static const struct axg_tdm_formatter_driver axg_tdmout_drv = { + .component_drv = &axg_tdmout_component_drv, + .regmap_cfg = &axg_tdmout_regmap_cfg, + .ops = &axg_tdmout_ops, + .invert_sclk = true, +}; + +static const struct of_device_id axg_tdmout_of_match[] = { + { + .compatible = "amlogic,axg-tdmout", + .data = &axg_tdmout_drv, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_tdmout_of_match); + +static struct platform_driver axg_tdmout_pdrv = { + .probe = axg_tdm_formatter_probe, + .driver = { + .name = "axg-tdmout", + .of_match_table = axg_tdmout_of_match, + }, +}; +module_platform_driver(axg_tdmout_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG TDM output formatter driver"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL v2");