brintos

brintos / linux-shallow public Read only

0
0
Text · 15.3 KiB · faad2f9 Raw
508 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for ADAU1381/ADAU1781 codec4 *5 * Copyright 2011-2013 Analog Devices Inc.6 * Author: Lars-Peter Clausen <lars@metafoo.de>7 */8 9#include <linux/module.h>10#include <linux/init.h>11#include <linux/i2c.h>12#include <linux/spi/spi.h>13#include <linux/slab.h>14#include <sound/core.h>15#include <sound/pcm.h>16#include <sound/pcm_params.h>17#include <sound/soc.h>18#include <sound/tlv.h>19#include <linux/platform_data/adau17x1.h>20 21#include "adau17x1.h"22#include "adau1781.h"23 24#define ADAU1781_DMIC_BEEP_CTRL		0x400825#define ADAU1781_LEFT_PGA		0x400e26#define ADAU1781_RIGHT_PGA		0x400f27#define ADAU1781_LEFT_PLAYBACK_MIXER	0x401c28#define ADAU1781_RIGHT_PLAYBACK_MIXER	0x401e29#define ADAU1781_MONO_PLAYBACK_MIXER	0x401f30#define ADAU1781_LEFT_LINEOUT		0x402531#define ADAU1781_RIGHT_LINEOUT		0x402632#define ADAU1781_SPEAKER		0x402733#define ADAU1781_BEEP_ZC		0x402834#define ADAU1781_DEJITTER		0x403235#define ADAU1781_DIG_PWDN0		0x408036#define ADAU1781_DIG_PWDN1		0x408137 38#define ADAU1781_INPUT_DIFFERNTIAL BIT(3)39 40#define ADAU1381_FIRMWARE "adau1381.bin"41#define ADAU1781_FIRMWARE "adau1781.bin"42 43static const struct reg_default adau1781_reg_defaults[] = {44	{ ADAU1781_DMIC_BEEP_CTRL,		0x00 },45	{ ADAU1781_LEFT_PGA,			0xc7 },46	{ ADAU1781_RIGHT_PGA,			0xc7 },47	{ ADAU1781_LEFT_PLAYBACK_MIXER,		0x00 },48	{ ADAU1781_RIGHT_PLAYBACK_MIXER,	0x00 },49	{ ADAU1781_MONO_PLAYBACK_MIXER,		0x00 },50	{ ADAU1781_LEFT_LINEOUT,		0x00 },51	{ ADAU1781_RIGHT_LINEOUT,		0x00 },52	{ ADAU1781_SPEAKER,			0x00 },53	{ ADAU1781_BEEP_ZC,			0x19 },54	{ ADAU1781_DEJITTER,			0x60 },55	{ ADAU1781_DIG_PWDN1,			0x0c },56	{ ADAU1781_DIG_PWDN1,			0x00 },57	{ ADAU17X1_CLOCK_CONTROL,		0x00 },58	{ ADAU17X1_PLL_CONTROL,			0x00 },59	{ ADAU17X1_REC_POWER_MGMT,		0x00 },60	{ ADAU17X1_MICBIAS,			0x04 },61	{ ADAU17X1_SERIAL_PORT0,		0x00 },62	{ ADAU17X1_SERIAL_PORT1,		0x00 },63	{ ADAU17X1_CONVERTER0,			0x00 },64	{ ADAU17X1_CONVERTER1,			0x00 },65	{ ADAU17X1_LEFT_INPUT_DIGITAL_VOL,	0x00 },66	{ ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,	0x00 },67	{ ADAU17X1_ADC_CONTROL,			0x00 },68	{ ADAU17X1_PLAY_POWER_MGMT,		0x00 },69	{ ADAU17X1_DAC_CONTROL0,		0x00 },70	{ ADAU17X1_DAC_CONTROL1,		0x00 },71	{ ADAU17X1_DAC_CONTROL2,		0x00 },72	{ ADAU17X1_SERIAL_PORT_PAD,		0x00 },73	{ ADAU17X1_CONTROL_PORT_PAD0,		0x00 },74	{ ADAU17X1_CONTROL_PORT_PAD1,		0x00 },75	{ ADAU17X1_DSP_SAMPLING_RATE,		0x01 },76	{ ADAU17X1_SERIAL_INPUT_ROUTE,		0x00 },77	{ ADAU17X1_SERIAL_OUTPUT_ROUTE,		0x00 },78	{ ADAU17X1_DSP_ENABLE,			0x00 },79	{ ADAU17X1_DSP_RUN,			0x00 },80	{ ADAU17X1_SERIAL_SAMPLING_RATE,	0x00 },81};82 83static const DECLARE_TLV_DB_SCALE(adau1781_speaker_tlv, 0, 200, 0);84 85static const DECLARE_TLV_DB_RANGE(adau1781_pga_tlv,86	0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),87	2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),88	4, 4, TLV_DB_SCALE_ITEM(1700, 0, 0),89	5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)90);91 92static const DECLARE_TLV_DB_RANGE(adau1781_beep_tlv,93	0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),94	2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),95	4, 4, TLV_DB_SCALE_ITEM(-2300, 0, 0),96	5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)97);98 99static const DECLARE_TLV_DB_SCALE(adau1781_sidetone_tlv, -1800, 300, 1);100 101static const char * const adau1781_speaker_bias_select_text[] = {102	"Normal operation", "Power saving", "Enhanced performance",103};104 105static const char * const adau1781_bias_select_text[] = {106	"Normal operation", "Extreme power saving", "Power saving",107	"Enhanced performance",108};109 110static SOC_ENUM_SINGLE_DECL(adau1781_adc_bias_enum,111		ADAU17X1_REC_POWER_MGMT, 3, adau1781_bias_select_text);112static SOC_ENUM_SINGLE_DECL(adau1781_speaker_bias_enum,113		ADAU17X1_PLAY_POWER_MGMT, 6, adau1781_speaker_bias_select_text);114static SOC_ENUM_SINGLE_DECL(adau1781_dac_bias_enum,115		ADAU17X1_PLAY_POWER_MGMT, 4, adau1781_bias_select_text);116static SOC_ENUM_SINGLE_DECL(adau1781_playback_bias_enum,117		ADAU17X1_PLAY_POWER_MGMT, 2, adau1781_bias_select_text);118static SOC_ENUM_SINGLE_DECL(adau1781_capture_bias_enum,119		ADAU17X1_REC_POWER_MGMT, 1, adau1781_bias_select_text);120 121static const struct snd_kcontrol_new adau1781_controls[] = {122	SOC_SINGLE_TLV("Beep Capture Volume", ADAU1781_DMIC_BEEP_CTRL, 0, 7, 0,123		adau1781_beep_tlv),124	SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAU1781_LEFT_PGA,125		ADAU1781_RIGHT_PGA, 5, 7, 0, adau1781_pga_tlv),126	SOC_DOUBLE_R("PGA Capture Switch", ADAU1781_LEFT_PGA,127		ADAU1781_RIGHT_PGA, 1, 1, 0),128 129	SOC_DOUBLE_R("Lineout Playback Switch", ADAU1781_LEFT_LINEOUT,130		ADAU1781_RIGHT_LINEOUT, 1, 1, 0),131	SOC_SINGLE("Beep ZC Switch", ADAU1781_BEEP_ZC, 0, 1, 0),132 133	SOC_SINGLE("Mono Playback Switch", ADAU1781_MONO_PLAYBACK_MIXER,134		0, 1, 0),135	SOC_SINGLE_TLV("Mono Playback Volume", ADAU1781_SPEAKER, 6, 3, 0,136		adau1781_speaker_tlv),137 138	SOC_ENUM("ADC Bias", adau1781_adc_bias_enum),139	SOC_ENUM("DAC Bias", adau1781_dac_bias_enum),140	SOC_ENUM("Capture Bias", adau1781_capture_bias_enum),141	SOC_ENUM("Playback Bias", adau1781_playback_bias_enum),142	SOC_ENUM("Speaker Bias", adau1781_speaker_bias_enum),143};144 145static const struct snd_kcontrol_new adau1781_beep_mixer_controls[] = {146	SOC_DAPM_SINGLE("Beep Capture Switch", ADAU1781_DMIC_BEEP_CTRL,147		3, 1, 0),148};149 150static const struct snd_kcontrol_new adau1781_left_mixer_controls[] = {151	SOC_DAPM_SINGLE_AUTODISABLE("Switch",152		ADAU1781_LEFT_PLAYBACK_MIXER, 5, 1, 0),153	SOC_DAPM_SINGLE_TLV("Beep Playback Volume",154		ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),155};156 157static const struct snd_kcontrol_new adau1781_right_mixer_controls[] = {158	SOC_DAPM_SINGLE_AUTODISABLE("Switch",159		ADAU1781_RIGHT_PLAYBACK_MIXER, 6, 1, 0),160	SOC_DAPM_SINGLE_TLV("Beep Playback Volume",161		ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),162};163 164static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {165	SOC_DAPM_SINGLE_AUTODISABLE("Left Switch",166		ADAU1781_MONO_PLAYBACK_MIXER, 7, 1, 0),167	SOC_DAPM_SINGLE_AUTODISABLE("Right Switch",168		 ADAU1781_MONO_PLAYBACK_MIXER, 6, 1, 0),169	SOC_DAPM_SINGLE_TLV("Beep Playback Volume",170		ADAU1781_MONO_PLAYBACK_MIXER, 2, 8, 0, adau1781_sidetone_tlv),171};172 173static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,174	struct snd_kcontrol *kcontrol, int event)175{176	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);177	struct adau *adau = snd_soc_component_get_drvdata(component);178 179	/* After any power changes have been made the dejitter circuit180	 * has to be reinitialized. */181	regmap_write(adau->regmap, ADAU1781_DEJITTER, 0);182	if (!adau->master)183		regmap_write(adau->regmap, ADAU1781_DEJITTER, 5);184 185	return 0;186}187 188static const struct snd_soc_dapm_widget adau1781_dapm_widgets[] = {189	SND_SOC_DAPM_PGA("Left PGA", ADAU1781_LEFT_PGA, 0, 0, NULL, 0),190	SND_SOC_DAPM_PGA("Right PGA", ADAU1781_RIGHT_PGA, 0, 0, NULL, 0),191 192	SND_SOC_DAPM_OUT_DRV("Speaker", ADAU1781_SPEAKER, 0, 0, NULL, 0),193 194	SOC_MIXER_NAMED_CTL_ARRAY("Beep Mixer", ADAU17X1_MICBIAS, 4, 0,195		adau1781_beep_mixer_controls),196 197	SOC_MIXER_ARRAY("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,198		adau1781_left_mixer_controls),199	SOC_MIXER_ARRAY("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,200		adau1781_right_mixer_controls),201	SOC_MIXER_ARRAY("Mono Mixer", SND_SOC_NOPM, 0, 0,202		adau1781_mono_mixer_controls),203 204	SND_SOC_DAPM_SUPPLY("Serial Input Routing", ADAU1781_DIG_PWDN0,205		2, 0, NULL, 0),206	SND_SOC_DAPM_SUPPLY("Serial Output Routing", ADAU1781_DIG_PWDN0,207		3, 0, NULL, 0),208	SND_SOC_DAPM_SUPPLY("Clock Domain Transfer", ADAU1781_DIG_PWDN0,209		5, 0, NULL, 0),210	SND_SOC_DAPM_SUPPLY("Serial Ports", ADAU1781_DIG_PWDN0, 4, 0, NULL, 0),211	SND_SOC_DAPM_SUPPLY("ADC Engine", ADAU1781_DIG_PWDN0, 7, 0, NULL, 0),212	SND_SOC_DAPM_SUPPLY("DAC Engine", ADAU1781_DIG_PWDN1, 0, 0, NULL, 0),213	SND_SOC_DAPM_SUPPLY("Digital Mic", ADAU1781_DIG_PWDN1, 1, 0, NULL, 0),214 215	SND_SOC_DAPM_SUPPLY("Sound Engine", ADAU1781_DIG_PWDN0, 0, 0, NULL, 0),216	SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, ADAU1781_DIG_PWDN0, 1, 0, NULL, 0),217 218	SND_SOC_DAPM_SUPPLY("Zero Crossing Detector", ADAU1781_DIG_PWDN1, 2, 0,219		NULL, 0),220 221	SND_SOC_DAPM_POST("Dejitter fixup", adau1781_dejitter_fixup),222 223	SND_SOC_DAPM_INPUT("BEEP"),224 225	SND_SOC_DAPM_OUTPUT("AOUTL"),226	SND_SOC_DAPM_OUTPUT("AOUTR"),227	SND_SOC_DAPM_OUTPUT("SP"),228	SND_SOC_DAPM_INPUT("LMIC"),229	SND_SOC_DAPM_INPUT("RMIC"),230};231 232static const struct snd_soc_dapm_route adau1781_dapm_routes[] = {233	{ "Left Lineout Mixer", NULL, "Left Playback Enable" },234	{ "Right Lineout Mixer", NULL, "Right Playback Enable" },235 236	{ "Left Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },237	{ "Left Lineout Mixer", "Switch", "Left DAC" },238 239	{ "Right Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },240	{ "Right Lineout Mixer", "Switch", "Right DAC" },241 242	{ "Mono Mixer", "Beep Playback Volume", "Beep Mixer" },243	{ "Mono Mixer", "Right Switch", "Right DAC" },244	{ "Mono Mixer", "Left Switch", "Left DAC" },245	{ "Speaker", NULL, "Mono Mixer" },246 247	{ "Mono Mixer", NULL, "SYSCLK" },248	{ "Left Lineout Mixer", NULL, "SYSCLK" },249	{ "Left Lineout Mixer", NULL, "SYSCLK" },250 251	{ "Beep Mixer", "Beep Capture Switch", "BEEP" },252	{ "Beep Mixer", NULL, "Zero Crossing Detector" },253 254	{ "Left DAC", NULL, "DAC Engine" },255	{ "Right DAC", NULL, "DAC Engine" },256 257	{ "Sound Engine", NULL, "SYSCLK" },258	{ "DSP", NULL, "Sound Engine" },259 260	{ "Left Decimator", NULL, "ADC Engine" },261	{ "Right Decimator", NULL, "ADC Engine" },262 263	{ "AIFCLK", NULL, "SYSCLK" },264 265	{ "Playback", NULL, "Serial Input Routing" },266	{ "Playback", NULL, "Serial Ports" },267	{ "Playback", NULL, "Clock Domain Transfer" },268	{ "Capture", NULL, "Serial Output Routing" },269	{ "Capture", NULL, "Serial Ports" },270	{ "Capture", NULL, "Clock Domain Transfer" },271 272	{ "AOUTL", NULL, "Left Lineout Mixer" },273	{ "AOUTR", NULL, "Right Lineout Mixer" },274	{ "SP", NULL, "Speaker" },275};276 277static const struct snd_soc_dapm_route adau1781_adc_dapm_routes[] = {278	{ "Left PGA", NULL, "LMIC" },279	{ "Right PGA", NULL, "RMIC" },280 281	{ "Left Decimator", NULL, "Left PGA" },282	{ "Right Decimator", NULL, "Right PGA" },283};284 285static const char * const adau1781_dmic_select_text[] = {286	"DMIC1", "DMIC2",287};288 289static SOC_ENUM_SINGLE_VIRT_DECL(adau1781_dmic_select_enum,290	adau1781_dmic_select_text);291 292static const struct snd_kcontrol_new adau1781_dmic_mux =293	SOC_DAPM_ENUM("DMIC Select", adau1781_dmic_select_enum);294 295static const struct snd_soc_dapm_widget adau1781_dmic_dapm_widgets[] = {296	SND_SOC_DAPM_MUX("DMIC Select", SND_SOC_NOPM, 0, 0, &adau1781_dmic_mux),297 298	SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1781_DMIC_BEEP_CTRL, 4, 0),299	SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1781_DMIC_BEEP_CTRL, 5, 0),300};301 302static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {303	{ "DMIC1", NULL, "LMIC" },304	{ "DMIC2", NULL, "RMIC" },305 306	{ "DMIC1", NULL, "Digital Mic" },307	{ "DMIC2", NULL, "Digital Mic" },308 309	{ "DMIC Select", "DMIC1", "DMIC1" },310	{ "DMIC Select", "DMIC2", "DMIC2" },311 312	{ "Left Decimator", NULL, "DMIC Select" },313	{ "Right Decimator", NULL, "DMIC Select" },314};315 316static int adau1781_set_bias_level(struct snd_soc_component *component,317		enum snd_soc_bias_level level)318{319	struct adau *adau = snd_soc_component_get_drvdata(component);320 321	switch (level) {322	case SND_SOC_BIAS_ON:323		break;324	case SND_SOC_BIAS_PREPARE:325		break;326	case SND_SOC_BIAS_STANDBY:327		regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,328			ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,329			ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);330 331		/* Precharge */332		regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0x8, 0x8);333		break;334	case SND_SOC_BIAS_OFF:335		regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0xc, 0x0);336		regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,337			ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);338		break;339	}340 341	return 0;342}343 344static bool adau1781_readable_register(struct device *dev, unsigned int reg)345{346	switch (reg) {347	case ADAU1781_DMIC_BEEP_CTRL:348	case ADAU1781_LEFT_PGA:349	case ADAU1781_RIGHT_PGA:350	case ADAU1781_LEFT_PLAYBACK_MIXER:351	case ADAU1781_RIGHT_PLAYBACK_MIXER:352	case ADAU1781_MONO_PLAYBACK_MIXER:353	case ADAU1781_LEFT_LINEOUT:354	case ADAU1781_RIGHT_LINEOUT:355	case ADAU1781_SPEAKER:356	case ADAU1781_BEEP_ZC:357	case ADAU1781_DEJITTER:358	case ADAU1781_DIG_PWDN0:359	case ADAU1781_DIG_PWDN1:360		return true;361	default:362		break;363	}364 365	return adau17x1_readable_register(dev, reg);366}367 368static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,369	bool differential)370{371	unsigned int val;372 373	if (differential)374		val = ADAU1781_INPUT_DIFFERNTIAL;375	else376		val = 0;377 378	return regmap_update_bits(adau->regmap, reg,379		ADAU1781_INPUT_DIFFERNTIAL, val);380}381 382static int adau1781_component_probe(struct snd_soc_component *component)383{384	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);385	struct adau1781_platform_data *pdata = dev_get_platdata(component->dev);386	struct adau *adau = snd_soc_component_get_drvdata(component);387	int ret;388 389	ret = adau17x1_add_widgets(component);390	if (ret)391		return ret;392 393	if (pdata) {394		ret = adau1781_set_input_mode(adau, ADAU1781_LEFT_PGA,395			pdata->left_input_differential);396		if (ret)397			return ret;398		ret = adau1781_set_input_mode(adau, ADAU1781_RIGHT_PGA,399			pdata->right_input_differential);400		if (ret)401			return ret;402	}403 404	if (pdata && pdata->use_dmic) {405		ret = snd_soc_dapm_new_controls(dapm,406			adau1781_dmic_dapm_widgets,407			ARRAY_SIZE(adau1781_dmic_dapm_widgets));408		if (ret)409			return ret;410		ret = snd_soc_dapm_add_routes(dapm, adau1781_dmic_dapm_routes,411			ARRAY_SIZE(adau1781_dmic_dapm_routes));412		if (ret)413			return ret;414	} else {415		ret = snd_soc_dapm_add_routes(dapm, adau1781_adc_dapm_routes,416			ARRAY_SIZE(adau1781_adc_dapm_routes));417		if (ret)418			return ret;419	}420 421	ret = adau17x1_add_routes(component);422	if (ret < 0)423		return ret;424 425	return 0;426}427 428static const struct snd_soc_component_driver adau1781_component_driver = {429	.probe			= adau1781_component_probe,430	.resume			= adau17x1_resume,431	.set_bias_level		= adau1781_set_bias_level,432	.controls		= adau1781_controls,433	.num_controls		= ARRAY_SIZE(adau1781_controls),434	.dapm_widgets		= adau1781_dapm_widgets,435	.num_dapm_widgets	= ARRAY_SIZE(adau1781_dapm_widgets),436	.dapm_routes		= adau1781_dapm_routes,437	.num_dapm_routes	= ARRAY_SIZE(adau1781_dapm_routes),438	.suspend_bias_off	= 1,439	.idle_bias_on		= 1,440	.use_pmdown_time	= 1,441	.endianness		= 1,442};443 444#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \445	SNDRV_PCM_FMTBIT_S32_LE)446 447static struct snd_soc_dai_driver adau1781_dai_driver = {448	.name = "adau-hifi",449	.playback = {450		.stream_name = "Playback",451		.channels_min = 2,452		.channels_max = 8,453		.rates = SNDRV_PCM_RATE_8000_96000,454		.formats = ADAU1781_FORMATS,455	},456	.capture = {457		.stream_name = "Capture",458		.channels_min = 2,459		.channels_max = 8,460		.rates = SNDRV_PCM_RATE_8000_96000,461		.formats = ADAU1781_FORMATS,462	},463	.ops = &adau17x1_dai_ops,464};465 466const struct regmap_config adau1781_regmap_config = {467	.val_bits		= 8,468	.reg_bits		= 16,469	.max_register		= 0x40f8,470	.reg_defaults		= adau1781_reg_defaults,471	.num_reg_defaults	= ARRAY_SIZE(adau1781_reg_defaults),472	.readable_reg		= adau1781_readable_register,473	.volatile_reg		= adau17x1_volatile_register,474	.precious_reg		= adau17x1_precious_register,475	.cache_type		= REGCACHE_MAPLE,476};477EXPORT_SYMBOL_GPL(adau1781_regmap_config);478 479int adau1781_probe(struct device *dev, struct regmap *regmap,480	enum adau17x1_type type, void (*switch_mode)(struct device *dev))481{482	const char *firmware_name;483	int ret;484 485	switch (type) {486	case ADAU1381:487		firmware_name = ADAU1381_FIRMWARE;488		break;489	case ADAU1781:490		firmware_name = ADAU1781_FIRMWARE;491		break;492	default:493		return -EINVAL;494	}495 496	ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);497	if (ret)498		return ret;499 500	return devm_snd_soc_register_component(dev, &adau1781_component_driver,501		&adau1781_dai_driver, 1);502}503EXPORT_SYMBOL_GPL(adau1781_probe);504 505MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 driver");506MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");507MODULE_LICENSE("GPL");508