From patchwork Thu Nov 11 15:22:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Nazzareno Trimarchi X-Patchwork-Id: 1744 Return-Path: X-Original-To: linux-amarula@patchwork.amarulasolutions.com Delivered-To: linux-amarula@patchwork.amarulasolutions.com Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by ganimede.amarulasolutions.com (Postfix) with ESMTPS id 45DA03F07B for ; Thu, 11 Nov 2021 16:23:07 +0100 (CET) Received: by mail-wm1-f71.google.com with SMTP id r6-20020a1c4406000000b0033119c22fdbsf2881354wma.4 for ; Thu, 11 Nov 2021 07:23:07 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1636644187; cv=pass; d=google.com; s=arc-20160816; b=D/vB5JaBh7m0cRFrHnq2U955l7rkdSlG/39xMDnCZC4VZ8xD0jdTuSuVMdisNfBaY5 HcIrKP4Bk72o7TLxcjPdprP2JJJsAatWOAhyIF1UZkUvz7afKvZxD+xQ45sVmEFqfKkR xHuBGRl7RwRPAp67Cvky8HSVZ3R2MVa8AbYy1GGX2QuH7+OblkTViFIY5vS6r0oBlq4n oLINNLM0KjznOIxAQbISQQSzod0s8mRmQUZCDJUVNFKzdwvCLiDXPEwLJrcGWGpdao9c Q53A8J1RsGDlPxAvAbhER+OdAqlFKE9OM2a3ibs01OY1ky/ALY+yV/vKyTHar48kmJJO 9caQ== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-unsubscribe:list-archive:list-help:list-post:list-id :mailing-list:precedence:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=UZAND2P0f8ueuCfx9B+vIkjO629NAR7zp4sz5zNhdDY=; b=tj0gbtnrUcm9HslqVb7Knidw8tmn+YdwKzAB4/LeP+YADGmnm59rbELvJNg1ZB4U9g SAXJHzJh6MhjXvubrDV6dBniMP1IhgVWZ56U6DWDMuFSNDzaRt3Ubt41Z3IG2BPS6rFZ BmJ7kYMhGMwnSCaus7bZobXw4Ael/cQAWmqXQ0vAJjhHsBB7dY9BGAcU8wNGpyGjldi4 hbzDp6q7Y/1fKfTpKdp4inRQB/uyCyYZJnaJ92ggauLAi2DH8bgOQLtdYbZQZUrR0hEX swEQ+TUF4UbN9M605vfd/g66WsNwqMWfa+bZbHu9Qfa+Hz4Jx4RoCVH/J7C/flY6haFm /4zw== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@amarulasolutions.com header.s=google header.b=Gans+dQh; spf=pass (google.com: domain of michael@amarulasolutions.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=michael@amarulasolutions.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amarulasolutions.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-unsubscribe; bh=UZAND2P0f8ueuCfx9B+vIkjO629NAR7zp4sz5zNhdDY=; b=fxrgWAiKougzKSdBcp3i8AVOPKbgMGLwMcnXvmeZwm3ADTc6i0VIUKwV6+F8JiDahH 8aE7+uavIeVhujtcq5oBOEwh/ckTwYn3VN7DjwRu+9DIjybTi0OuL2Cia99JByhnPiJ3 PAxeZNQe0JErHqiXU8MOSE1RhzTVKRPY9SgoE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :x-spam-checked-in-group:list-post:list-help:list-archive :list-unsubscribe; bh=UZAND2P0f8ueuCfx9B+vIkjO629NAR7zp4sz5zNhdDY=; b=Cr8jUqqQMviAGkSq6OtJfi6W6tL+5muvMMgShnPBOh2Bl76Y81aZlE+6/m3UKA6p0d dEyDZ0qeHnXzbexEEnU8NtM2jKqcpv9R5gWGcfrWnaaCaO8aaJEjiehVTsB+LHA0P9t7 Qshn8VJxE6sHVI/s7oyEOQ7F01vFlXHoyuG3SoiLK6/lQNbKmBrzeUI4iBowBPG3vtgT 082NIBfJvlQ6yS82sw1IK0JLjmh73wL1ht+A71Hg+m8+W7kRgCrLLGs68lnhsXDKtVZ0 lDm3oKTOPH9g4TDCk6vlx/pQTh02rubTgitXCsgkMAG2Wamg05WTRVgWXTy7IWmre3s+ 15pw== X-Gm-Message-State: AOAM533mFZzPJqT/5P599Ox2o+Ao4PBZi2/Rg9KUaAY3MBklGdi4We4n DVWo8um8DzbQhr0LBE8wS+im9F53 X-Google-Smtp-Source: ABdhPJxWo7b1+8z7C5sjreFY8mgZ4g1piopO5FqKxHUB3MTw06kE9AT9kS3PSwdL7MJQqe1+cBrmrA== X-Received: by 2002:a7b:c7cd:: with SMTP id z13mr25335204wmk.172.1636644186949; Thu, 11 Nov 2021 07:23:06 -0800 (PST) X-BeenThere: linux-amarula@amarulasolutions.com Received: by 2002:a5d:4092:: with SMTP id o18ls2187778wrp.1.gmail; Thu, 11 Nov 2021 07:23:06 -0800 (PST) X-Received: by 2002:a05:6000:184e:: with SMTP id c14mr9713661wri.241.1636644185925; Thu, 11 Nov 2021 07:23:05 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1636644185; cv=none; d=google.com; s=arc-20160816; b=M7oYrFRG1YWiu29VQcFG6qyXZMXz58tPB1Yb0FQgldu8OTc2IRhrKLXKhg+EbPt+Vn YegWItwzlANuY5ocTasUwWimDtm5OG1sE/jXkDreSdWEUbkFPmPMPD4FFw1wJl1f9QgK EqzDeqoSudbJI5m6wpa6nfxyIwMQvYA6K20wbZ/Cf70mmqbJSA7aklHJYd7Y5OFrGSWY Pbzb5y/g/xLqwkuHUcWPf3QY6mgHjTuaKT2YJFapJzIL6W9IDI+ebc0cnFuvES9ooMIB pMr45Y6HBFG2iaGrVUj3OzPi6DTEsv6pts2pSiGMTp6hQ1tPyPg8NrXvJk77o4YvfSnf yvIQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=AkxPQ+zGtzjWUEvEDMFx6AuIxHT4FxOagW5HLsqAaLc=; b=RI+BCcUbpfKXX2lMfjUQzo75nlMOVZZfO58MSQIzkPM8rELW8cCSChFwXjdpQhLyHx AeiMIvzGUNaruzClt30AJmSxEPpi56fxm+rzCA9TPab4FbFHWIVXty7dwTNtM5kPeOf+ +pBH4i7DHw97OxC29XgNgoFqpkdu4Fdjlx67AY881YIr6hSyfbA7MVBkTfAtRmE4qOyE SHVb2n+tGILAdOURC/ospbJGmpgUYVZP/scOjZe83hG74slNJomDkQ6clVT0a+YhL8E7 y1oCvUVnK/nuVwnW7nN8NqD3Q+PxgdjUnZHu1nv9mqPDxPqhJOZDuTy+IvAIdDr9DWWG ymTA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@amarulasolutions.com header.s=google header.b=Gans+dQh; spf=pass (google.com: domain of michael@amarulasolutions.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=michael@amarulasolutions.com Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41]) by mx.google.com with SMTPS id o22sor2228705wra.45.2021.11.11.07.23.05 for (Google Transport Security); Thu, 11 Nov 2021 07:23:05 -0800 (PST) Received-SPF: pass (google.com: domain of michael@amarulasolutions.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41; X-Received: by 2002:a5d:4cd1:: with SMTP id c17mr9781450wrt.31.1636644185490; Thu, 11 Nov 2021 07:23:05 -0800 (PST) Received: from panicking.pdxnet.pdxeng.ch ([5.171.215.77]) by smtp.gmail.com with ESMTPSA id f3sm8973683wmb.12.2021.11.11.07.23.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Nov 2021 07:23:04 -0800 (PST) From: Michael Trimarchi To: Ariel D'Alessandro Cc: linux-amarula@amarulasolutions.com, Anthony Brandon Subject: [PATCH 04/11] ASoC: fsl: add smm-s2-pro audio driver Date: Thu, 11 Nov 2021 16:22:51 +0100 Message-Id: <20211111152258.26131-4-michael@amarulasolutions.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211111152258.26131-1-michael@amarulasolutions.com> References: <20211111152258.26131-1-michael@amarulasolutions.com> MIME-Version: 1.0 X-Original-Sender: michael@amarulasolutions.com X-Original-Authentication-Results: mx.google.com; dkim=pass header.i=@amarulasolutions.com header.s=google header.b=Gans+dQh; spf=pass (google.com: domain of michael@amarulasolutions.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=michael@amarulasolutions.com Content-Type: text/plain; charset="UTF-8" Precedence: list Mailing-list: list linux-amarula@amarulasolutions.com; contact linux-amarula+owners@amarulasolutions.com List-ID: X-Spam-Checked-In-Group: linux-amarula@amarulasolutions.com X-Google-Group-Id: 476853432473 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , The smm-tlv320aic31xx driver implement audio support of smm-s2-pro BSH system master. Signed-off-by: Michael Trimarchi --- sound/soc/fsl/Kconfig | 11 ++ sound/soc/fsl/Makefile | 2 + sound/soc/fsl/smm-tlv320aic31xx.c | 308 ++++++++++++++++++++++++++++++ 3 files changed, 321 insertions(+) create mode 100644 sound/soc/fsl/smm-tlv320aic31xx.c diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 8e05d092790e..74430bc1e640 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -364,6 +364,17 @@ config SND_SOC_IMX_CARD with OF-graph DT bindings. It also support DPCM of single CPU multi Codec ststem. +config SND_SOC_SMM2_CARD + tristate "SoC Audio Card for SMM2 pro BSH device" + depends on OF && I2C + select SND_SOC_FSL_EASRC + select SND_SOC_TLV320AIC31XX + select SND_SOC_IMX_PCM_DMA + select SND_SOC_FSL_SAI + select SND_SIMPLE_CARD_UTILS + help + This option enables audio sound card support for BSH SMM2 Pro + endif # SND_IMX_SOC endmenu diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index b54beb1a66fa..e85d7f6be709 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -72,6 +72,7 @@ snd-soc-imx-audmix-objs := imx-audmix.o snd-soc-imx-hdmi-objs := imx-hdmi.o snd-soc-imx-rpmsg-objs := imx-rpmsg.o snd-soc-imx-card-objs := imx-card.o +snd-soc-smm-tlv320aic31xx-objs := smm-tlv320aic31xx.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o @@ -81,3 +82,4 @@ obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o obj-$(CONFIG_SND_SOC_IMX_CARD) += snd-soc-imx-card.o +obj-$(CONFIG_SND_SOC_SMM2_CARD) += snd-soc-smm-tlv320aic31xx.o diff --git a/sound/soc/fsl/smm-tlv320aic31xx.c b/sound/soc/fsl/smm-tlv320aic31xx.c new file mode 100644 index 000000000000..3aa71031d859 --- /dev/null +++ b/sound/soc/fsl/smm-tlv320aic31xx.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// BSH smm2-pro audio card +// +// Copyright (C) 2021 BSH Hausgeraete GmbH +// +// Author: Michael Trimarchi + +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_sai.h" +#include "../codecs/tlv320aic31xx.h" + +/** + * struct smm_card_priv - Freescale Generic ASOC card private data + * @dai_link: DAI link structure including normal one and DPCM link + * @pdev: platform device pointer + * @card: ASoC card structure + * @sample_rate: Current sample rate + * @sample_format: Current sample format + * @asrc_rate: ASRC sample rate used by Back-Ends + * @asrc_format: ASRC sample format used by Back-Ends + * @dai_fmt: DAI format between CPU and CODEC + * @name: Card name + */ + +struct smm_card_priv { + struct snd_soc_dai_link dai_link[2]; + struct platform_device *pdev; + struct snd_soc_card card; + u32 sample_rate; + snd_pcm_format_t sample_format; + u32 asrc_rate; + snd_pcm_format_t asrc_format; + u32 dai_fmt; + char name[32]; +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* 1st half -- Normal DAPM routes */ + {"Playback", NULL, "CPU-Playback"}, + /* 2nd half -- ASRC DAPM routes */ + {"CPU-Playback", NULL, "ASRC-Playback"}, +}; + +/* Add all possible widgets into here without being redundant */ +static const struct snd_soc_dapm_widget smm_card_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static int smm_card_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct smm_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct device *dev = rtd->card->dev; + int ret; + + priv->sample_rate = params_rate(params); + priv->sample_format = params_format(params); + + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 32); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set TDM slot for cpu dai\n"); + goto fail; + } + + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), AIC31XX_PLL_CLKIN_BCKL, + priv->sample_rate * 256, SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set SYSCLK: %d\n", ret); + goto fail; + } + + return 0; + +fail: + return ret; +} + +static const struct snd_soc_ops smm_card_ops = { + .hw_params = smm_card_hw_params, +}; + +static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct smm_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_interval *rate; + struct snd_mask *mask; + + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + rate->max = rate->min = priv->asrc_rate; + + mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + snd_mask_none(mask); + snd_mask_set_format(mask, priv->asrc_format); + + return 0; +} + +SND_SOC_DAILINK_DEFS(hifi_fe, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(hifi_be, + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_EMPTY()), + DAILINK_COMP_ARRAY(COMP_DUMMY())); + +static struct snd_soc_dai_link smm_card_dai[] = { + /* DPCM Link between Front-End and Back-End */ + { + .name = "HiFi-ASRC-FE", + .stream_name = "HiFi-ASRC-FE", + .dpcm_playback = 1, + .dpcm_capture = 0, + .dynamic = 1, + SND_SOC_DAILINK_REG(hifi_fe), + }, + { + .name = "HiFi-ASRC-BE", + .stream_name = "HiFi-ASRC-BE", + .be_hw_params_fixup = be_hw_params_fixup, + .ops = &smm_card_ops, + .dpcm_playback = 1, + .dpcm_capture = 0, + .no_pcm = 1, + SND_SOC_DAILINK_REG(hifi_be), + }, +}; + +static int smm_card_probe(struct platform_device *pdev) +{ + struct device_node *cpu_np, *codec_np, *asrc_np; + struct device_node *np = pdev->dev.of_node; + struct platform_device *asrc_pdev = NULL; + struct platform_device *cpu_pdev; + struct smm_card_priv *priv; + struct device *codec_dev = NULL; + const char *codec_dai_name; + const char *codec_dev_name; + u32 width; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + cpu_np = of_parse_phandle(np, "audio-cpu", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "CPU phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + cpu_pdev = of_find_device_by_node(cpu_np); + if (!cpu_pdev) { + dev_err(&pdev->dev, "failed to find CPU DAI device\n"); + ret = -EINVAL; + goto fail; + } + + codec_np = of_parse_phandle(np, "audio-codec", 0); + if (codec_np) { + struct platform_device *codec_pdev; + struct i2c_client *codec_i2c; + + codec_i2c = of_find_i2c_device_by_node(codec_np); + if (codec_i2c) { + codec_dev = &codec_i2c->dev; + codec_dev_name = codec_i2c->name; + } + if (!codec_dev) { + codec_pdev = of_find_device_by_node(codec_np); + if (codec_pdev) { + codec_dev = &codec_pdev->dev; + codec_dev_name = codec_pdev->name; + } + } + } + + asrc_np = of_parse_phandle(np, "audio-asrc", 0); + if (asrc_np) + asrc_pdev = of_find_device_by_node(asrc_np); + + if (!asrc_pdev) { + dev_err(&pdev->dev, "Unable to register the ASRC DAI device\n"); + ret = -EINVAL; + goto fail; + } + + /* Default sample rate and format, will be updated in hw_params() */ + priv->sample_rate = 48000; + priv->sample_format = SNDRV_PCM_FORMAT_S32_LE; + priv->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + + memcpy(priv->dai_link, smm_card_dai, + sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); + + codec_dai_name = "tlv320dac31xx-hifi"; + priv->card.dapm_routes = audio_map; + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); + + /* Initialize sound card */ + priv->pdev = pdev; + priv->card.dev = &pdev->dev; + priv->card.owner = THIS_MODULE; + ret = snd_soc_of_parse_card_name(&priv->card, "model"); + if (ret) { + snprintf(priv->name, sizeof(priv->name), "%s-audio", codec_dev_name); + priv->card.name = priv->name; + } + priv->card.dai_link = priv->dai_link; + priv->card.dapm_widgets = smm_card_dapm_widgets; + priv->card.num_dapm_widgets = ARRAY_SIZE(smm_card_dapm_widgets); + + if (of_property_read_bool(np, "audio-routing")) { + ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing"); + if (ret) { + dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); + goto asrc_fail; + } + } + + /* DPCM DAI Links */ + priv->dai_link[0].cpus->of_node = asrc_np; + priv->dai_link[0].platforms->of_node = asrc_np; + priv->dai_link[1].codecs->dai_name = codec_dai_name; + priv->dai_link[1].codecs->of_node = codec_np; + priv->dai_link[1].cpus->of_node = cpu_np; + priv->dai_link[1].dai_fmt = priv->dai_fmt; + priv->card.num_links = 2; + + ret = of_property_read_u32(asrc_np, "fsl,asrc-rate", + &priv->asrc_rate); + if (ret) { + dev_err(&pdev->dev, "failed to get output rate\n"); + ret = -EINVAL; + goto asrc_fail; + } + + ret = of_property_read_u32(asrc_np, "fsl,asrc-format", + &priv->asrc_format); + if (ret) { + /* Fallback to old binding; translate to asrc_format */ + ret = of_property_read_u32(asrc_np, "fsl,asrc-width", + &width); + if (ret) { + dev_err(&pdev->dev, + "failed to decide output format\n"); + goto asrc_fail; + } + + if (width == 24) + priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE; + else + priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE; + } + + /* Finish card registering */ + platform_set_drvdata(pdev, priv); + snd_soc_card_set_drvdata(&priv->card, priv); + + ret = devm_snd_soc_register_card(&pdev->dev, &priv->card); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + goto asrc_fail; + } + +asrc_fail: + of_node_put(asrc_np); + of_node_put(codec_np); + put_device(&cpu_pdev->dev); +fail: + of_node_put(cpu_np); + + return ret; +} + +static const struct of_device_id smm_card_dt_ids[] = { + { .compatible = "bsh,imx-audio-tlv320aic31xx", }, + {} +}; +MODULE_DEVICE_TABLE(of, smm_card_dt_ids); + +static struct platform_driver smm_bsh_card_driver = { + .probe = smm_card_probe, + .driver = { + .name = "smm-tlv320aic31xx", + .pm = &snd_soc_pm_ops, + .of_match_table = smm_card_dt_ids, + }, +}; +module_platform_driver(smm_bsh_card_driver); + +MODULE_DESCRIPTION("BSH smm2-pro audio card"); +MODULE_AUTHOR("Michael Trimarchi "); +MODULE_ALIAS("platform:smm-tlv320aic31xx"); +MODULE_LICENSE("GPL");