cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

uda134x.c (15938B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * uda134x.c  --  UDA134X ALSA SoC Codec driver
      4 *
      5 * Modifications by Christian Pellegrin <chripell@evolware.org>
      6 *
      7 * Copyright 2007 Dension Audio Systems Ltd.
      8 * Author: Zoltan Devai
      9 *
     10 * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
     11 */
     12
     13#include <linux/module.h>
     14#include <linux/delay.h>
     15#include <linux/slab.h>
     16#include <sound/pcm.h>
     17#include <sound/pcm_params.h>
     18#include <sound/soc.h>
     19#include <sound/initval.h>
     20
     21#include <sound/uda134x.h>
     22#include <sound/l3.h>
     23
     24#include "uda134x.h"
     25
     26
     27#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
     28#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
     29		SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
     30
     31struct uda134x_priv {
     32	int sysclk;
     33	int dai_fmt;
     34
     35	struct snd_pcm_substream *master_substream;
     36	struct snd_pcm_substream *slave_substream;
     37
     38	struct regmap *regmap;
     39	struct uda134x_platform_data *pd;
     40};
     41
     42static const struct reg_default uda134x_reg_defaults[] = {
     43	{ UDA134X_EA000, 0x04 },
     44	{ UDA134X_EA001, 0x04 },
     45	{ UDA134X_EA010, 0x04 },
     46	{ UDA134X_EA011, 0x00 },
     47	{ UDA134X_EA100, 0x00 },
     48	{ UDA134X_EA101, 0x00 },
     49	{ UDA134X_EA110, 0x00 },
     50	{ UDA134X_EA111, 0x00 },
     51	{ UDA134X_STATUS0, 0x00 },
     52	{ UDA134X_STATUS1, 0x03 },
     53	{ UDA134X_DATA000, 0x00 },
     54	{ UDA134X_DATA001, 0x00 },
     55	{ UDA134X_DATA010, 0x00 },
     56	{ UDA134X_DATA011, 0x00 },
     57	{ UDA134X_DATA1, 0x00 },
     58};
     59
     60/*
     61 * Write to the uda134x registers
     62 *
     63 */
     64static int uda134x_regmap_write(void *context, unsigned int reg,
     65	unsigned int value)
     66{
     67	struct uda134x_platform_data *pd = context;
     68	int ret;
     69	u8 addr;
     70	u8 data = value;
     71
     72	switch (reg) {
     73	case UDA134X_STATUS0:
     74	case UDA134X_STATUS1:
     75		addr = UDA134X_STATUS_ADDR;
     76		data |= (reg - UDA134X_STATUS0) << 7;
     77		break;
     78	case UDA134X_DATA000:
     79	case UDA134X_DATA001:
     80	case UDA134X_DATA010:
     81	case UDA134X_DATA011:
     82		addr = UDA134X_DATA0_ADDR;
     83		data |= (reg - UDA134X_DATA000) << 6;
     84		break;
     85	case UDA134X_DATA1:
     86		addr = UDA134X_DATA1_ADDR;
     87		break;
     88	default:
     89		/* It's an extended address register */
     90		addr =  (reg | UDA134X_EXTADDR_PREFIX);
     91
     92		ret = l3_write(&pd->l3,
     93			       UDA134X_DATA0_ADDR, &addr, 1);
     94		if (ret != 1)
     95			return -EIO;
     96
     97		addr = UDA134X_DATA0_ADDR;
     98		data = (value | UDA134X_EXTDATA_PREFIX);
     99		break;
    100	}
    101
    102	ret = l3_write(&pd->l3,
    103		       addr, &data, 1);
    104	if (ret != 1)
    105		return -EIO;
    106
    107	return 0;
    108}
    109
    110static inline void uda134x_reset(struct snd_soc_component *component)
    111{
    112	struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
    113	unsigned int mask = 1<<6;
    114
    115	regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, mask);
    116	msleep(1);
    117	regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, 0);
    118}
    119
    120static int uda134x_mute(struct snd_soc_dai *dai, int mute, int direction)
    121{
    122	struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(dai->component);
    123	unsigned int mask = 1<<2;
    124	unsigned int val;
    125
    126	pr_debug("%s mute: %d\n", __func__, mute);
    127
    128	if (mute)
    129		val = mask;
    130	else
    131		val = 0;
    132
    133	return regmap_update_bits(uda134x->regmap, UDA134X_DATA010, mask, val);
    134}
    135
    136static int uda134x_startup(struct snd_pcm_substream *substream,
    137	struct snd_soc_dai *dai)
    138{
    139	struct snd_soc_component *component = dai->component;
    140	struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
    141	struct snd_pcm_runtime *master_runtime;
    142
    143	if (uda134x->master_substream) {
    144		master_runtime = uda134x->master_substream->runtime;
    145
    146		pr_debug("%s constraining to %d bits at %d\n", __func__,
    147			 master_runtime->sample_bits,
    148			 master_runtime->rate);
    149
    150		snd_pcm_hw_constraint_single(substream->runtime,
    151					     SNDRV_PCM_HW_PARAM_RATE,
    152					     master_runtime->rate);
    153
    154		snd_pcm_hw_constraint_single(substream->runtime,
    155					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
    156					     master_runtime->sample_bits);
    157
    158		uda134x->slave_substream = substream;
    159	} else
    160		uda134x->master_substream = substream;
    161
    162	return 0;
    163}
    164
    165static void uda134x_shutdown(struct snd_pcm_substream *substream,
    166	struct snd_soc_dai *dai)
    167{
    168	struct snd_soc_component *component = dai->component;
    169	struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
    170
    171	if (uda134x->master_substream == substream)
    172		uda134x->master_substream = uda134x->slave_substream;
    173
    174	uda134x->slave_substream = NULL;
    175}
    176
    177static int uda134x_hw_params(struct snd_pcm_substream *substream,
    178	struct snd_pcm_hw_params *params,
    179	struct snd_soc_dai *dai)
    180{
    181	struct snd_soc_component *component = dai->component;
    182	struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
    183	unsigned int hw_params = 0;
    184
    185	if (substream == uda134x->slave_substream) {
    186		pr_debug("%s ignoring hw_params for slave substream\n",
    187			 __func__);
    188		return 0;
    189	}
    190
    191	pr_debug("%s sysclk: %d, rate:%d\n", __func__,
    192		 uda134x->sysclk, params_rate(params));
    193
    194	/* set SYSCLK / fs ratio */
    195	switch (uda134x->sysclk / params_rate(params)) {
    196	case 512:
    197		break;
    198	case 384:
    199		hw_params |= (1<<4);
    200		break;
    201	case 256:
    202		hw_params |= (1<<5);
    203		break;
    204	default:
    205		printk(KERN_ERR "%s unsupported fs\n", __func__);
    206		return -EINVAL;
    207	}
    208
    209	pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__,
    210		 uda134x->dai_fmt, params_format(params));
    211
    212	/* set DAI format and word length */
    213	switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    214	case SND_SOC_DAIFMT_I2S:
    215		break;
    216	case SND_SOC_DAIFMT_RIGHT_J:
    217		switch (params_width(params)) {
    218		case 16:
    219			hw_params |= (1<<1);
    220			break;
    221		case 18:
    222			hw_params |= (1<<2);
    223			break;
    224		case 20:
    225			hw_params |= ((1<<2) | (1<<1));
    226			break;
    227		default:
    228			printk(KERN_ERR "%s unsupported format (right)\n",
    229			       __func__);
    230			return -EINVAL;
    231		}
    232		break;
    233	case SND_SOC_DAIFMT_LEFT_J:
    234		hw_params |= (1<<3);
    235		break;
    236	default:
    237		printk(KERN_ERR "%s unsupported format\n", __func__);
    238		return -EINVAL;
    239	}
    240
    241	return regmap_update_bits(uda134x->regmap, UDA134X_STATUS0,
    242		STATUS0_SYSCLK_MASK | STATUS0_DAIFMT_MASK, hw_params);
    243}
    244
    245static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
    246				  int clk_id, unsigned int freq, int dir)
    247{
    248	struct snd_soc_component *component = codec_dai->component;
    249	struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
    250
    251	pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__,
    252		 clk_id, freq, dir);
    253
    254	/* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
    255	   because the codec is slave. Of course limitations of the clock
    256	   master (the IIS controller) apply.
    257	   We'll error out on set_hw_params if it's not OK */
    258	if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
    259		uda134x->sysclk = freq;
    260		return 0;
    261	}
    262
    263	printk(KERN_ERR "%s unsupported sysclk\n", __func__);
    264	return -EINVAL;
    265}
    266
    267static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
    268			       unsigned int fmt)
    269{
    270	struct snd_soc_component *component = codec_dai->component;
    271	struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
    272
    273	pr_debug("%s fmt: %08X\n", __func__, fmt);
    274
    275	/* codec supports only full consumer mode */
    276	if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
    277		printk(KERN_ERR "%s unsupported clocking mode\n", __func__);
    278		return -EINVAL;
    279	}
    280
    281	/* no support for clock inversion */
    282	if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
    283		printk(KERN_ERR "%s unsupported clock inversion\n", __func__);
    284		return -EINVAL;
    285	}
    286
    287	/* We can't setup DAI format here as it depends on the word bit num */
    288	/* so let's just store the value for later */
    289	uda134x->dai_fmt = fmt;
    290
    291	return 0;
    292}
    293
    294static int uda134x_set_bias_level(struct snd_soc_component *component,
    295				  enum snd_soc_bias_level level)
    296{
    297	struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
    298	struct uda134x_platform_data *pd = uda134x->pd;
    299	pr_debug("%s bias level %d\n", __func__, level);
    300
    301	switch (level) {
    302	case SND_SOC_BIAS_ON:
    303		break;
    304	case SND_SOC_BIAS_PREPARE:
    305		/* power on */
    306		if (pd->power) {
    307			pd->power(1);
    308			regcache_sync(uda134x->regmap);
    309		}
    310		break;
    311	case SND_SOC_BIAS_STANDBY:
    312		break;
    313	case SND_SOC_BIAS_OFF:
    314		/* power off */
    315		if (pd->power) {
    316			pd->power(0);
    317			regcache_mark_dirty(uda134x->regmap);
    318		}
    319		break;
    320	}
    321	return 0;
    322}
    323
    324static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1",
    325					    "Minimum2", "Maximum"};
    326static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
    327static const char *uda134x_mixmode[] = {"Differential", "Analog1",
    328					"Analog2", "Both"};
    329
    330static const struct soc_enum uda134x_mixer_enum[] = {
    331SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting),
    332SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph),
    333SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode),
    334};
    335
    336static const struct snd_kcontrol_new uda1341_snd_controls[] = {
    337SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
    338SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0),
    339SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1),
    340SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1),
    341
    342SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0),
    343SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0),
    344
    345SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
    346SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
    347
    348SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
    349SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
    350SOC_ENUM("Input Mux", uda134x_mixer_enum[2]),
    351
    352SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0),
    353SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1),
    354SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0),
    355
    356SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0),
    357SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0),
    358SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0),
    359SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0),
    360SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0),
    361SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
    362};
    363
    364static const struct snd_kcontrol_new uda1340_snd_controls[] = {
    365SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
    366
    367SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
    368SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
    369
    370SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
    371SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
    372
    373SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
    374};
    375
    376static const struct snd_kcontrol_new uda1345_snd_controls[] = {
    377SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
    378
    379SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
    380
    381SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
    382};
    383
    384/* UDA1341 has the DAC/ADC power down in STATUS1 */
    385static const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = {
    386	SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0),
    387	SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0),
    388};
    389
    390/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */
    391static const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = {
    392	SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0),
    393	SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0),
    394};
    395
    396/* Common DAPM widgets */
    397static const struct snd_soc_dapm_widget uda134x_dapm_widgets[] = {
    398	SND_SOC_DAPM_INPUT("VINL1"),
    399	SND_SOC_DAPM_INPUT("VINR1"),
    400	SND_SOC_DAPM_INPUT("VINL2"),
    401	SND_SOC_DAPM_INPUT("VINR2"),
    402	SND_SOC_DAPM_OUTPUT("VOUTL"),
    403	SND_SOC_DAPM_OUTPUT("VOUTR"),
    404};
    405
    406static const struct snd_soc_dapm_route uda134x_dapm_routes[] = {
    407	{ "ADC", NULL, "VINL1" },
    408	{ "ADC", NULL, "VINR1" },
    409	{ "ADC", NULL, "VINL2" },
    410	{ "ADC", NULL, "VINR2" },
    411	{ "VOUTL", NULL, "DAC" },
    412	{ "VOUTR", NULL, "DAC" },
    413};
    414
    415static const struct snd_soc_dai_ops uda134x_dai_ops = {
    416	.startup	= uda134x_startup,
    417	.shutdown	= uda134x_shutdown,
    418	.hw_params	= uda134x_hw_params,
    419	.mute_stream	= uda134x_mute,
    420	.set_sysclk	= uda134x_set_dai_sysclk,
    421	.set_fmt	= uda134x_set_dai_fmt,
    422	.no_capture_mute = 1,
    423};
    424
    425static struct snd_soc_dai_driver uda134x_dai = {
    426	.name = "uda134x-hifi",
    427	/* playback capabilities */
    428	.playback = {
    429		.stream_name = "Playback",
    430		.channels_min = 1,
    431		.channels_max = 2,
    432		.rates = UDA134X_RATES,
    433		.formats = UDA134X_FORMATS,
    434	},
    435	/* capture capabilities */
    436	.capture = {
    437		.stream_name = "Capture",
    438		.channels_min = 1,
    439		.channels_max = 2,
    440		.rates = UDA134X_RATES,
    441		.formats = UDA134X_FORMATS,
    442	},
    443	/* pcm operations */
    444	.ops = &uda134x_dai_ops,
    445};
    446
    447static int uda134x_soc_probe(struct snd_soc_component *component)
    448{
    449	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
    450	struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
    451	struct uda134x_platform_data *pd = uda134x->pd;
    452	const struct snd_soc_dapm_widget *widgets;
    453	unsigned num_widgets;
    454	int ret;
    455
    456	printk(KERN_INFO "UDA134X SoC Audio Codec\n");
    457
    458	switch (pd->model) {
    459	case UDA134X_UDA1340:
    460	case UDA134X_UDA1341:
    461	case UDA134X_UDA1344:
    462	case UDA134X_UDA1345:
    463		break;
    464	default:
    465		printk(KERN_ERR "UDA134X SoC codec: "
    466		       "unsupported model %d\n",
    467			pd->model);
    468		return -EINVAL;
    469	}
    470
    471	if (pd->power)
    472		pd->power(1);
    473
    474	uda134x_reset(component);
    475
    476	if (pd->model == UDA134X_UDA1341) {
    477		widgets = uda1341_dapm_widgets;
    478		num_widgets = ARRAY_SIZE(uda1341_dapm_widgets);
    479	} else {
    480		widgets = uda1340_dapm_widgets;
    481		num_widgets = ARRAY_SIZE(uda1340_dapm_widgets);
    482	}
    483
    484	ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets);
    485	if (ret) {
    486		printk(KERN_ERR "%s failed to register dapm controls: %d",
    487			__func__, ret);
    488		return ret;
    489	}
    490
    491	switch (pd->model) {
    492	case UDA134X_UDA1340:
    493	case UDA134X_UDA1344:
    494		ret = snd_soc_add_component_controls(component, uda1340_snd_controls,
    495					ARRAY_SIZE(uda1340_snd_controls));
    496	break;
    497	case UDA134X_UDA1341:
    498		ret = snd_soc_add_component_controls(component, uda1341_snd_controls,
    499					ARRAY_SIZE(uda1341_snd_controls));
    500	break;
    501	case UDA134X_UDA1345:
    502		ret = snd_soc_add_component_controls(component, uda1345_snd_controls,
    503					ARRAY_SIZE(uda1345_snd_controls));
    504	break;
    505	default:
    506		printk(KERN_ERR "%s unknown codec type: %d",
    507			__func__, pd->model);
    508		return -EINVAL;
    509	}
    510
    511	if (ret < 0) {
    512		printk(KERN_ERR "UDA134X: failed to register controls\n");
    513		return ret;
    514	}
    515
    516	return 0;
    517}
    518
    519static const struct snd_soc_component_driver soc_component_dev_uda134x = {
    520	.probe			= uda134x_soc_probe,
    521	.set_bias_level		= uda134x_set_bias_level,
    522	.dapm_widgets		= uda134x_dapm_widgets,
    523	.num_dapm_widgets	= ARRAY_SIZE(uda134x_dapm_widgets),
    524	.dapm_routes		= uda134x_dapm_routes,
    525	.num_dapm_routes	= ARRAY_SIZE(uda134x_dapm_routes),
    526	.suspend_bias_off	= 1,
    527	.idle_bias_on		= 1,
    528	.use_pmdown_time	= 1,
    529	.endianness		= 1,
    530	.non_legacy_dai_naming	= 1,
    531};
    532
    533static const struct regmap_config uda134x_regmap_config = {
    534	.reg_bits = 8,
    535	.val_bits = 8,
    536	.max_register = UDA134X_DATA1,
    537	.reg_defaults = uda134x_reg_defaults,
    538	.num_reg_defaults = ARRAY_SIZE(uda134x_reg_defaults),
    539	.cache_type = REGCACHE_RBTREE,
    540
    541	.reg_write = uda134x_regmap_write,
    542};
    543
    544static int uda134x_codec_probe(struct platform_device *pdev)
    545{
    546	struct uda134x_platform_data *pd = pdev->dev.platform_data;
    547	struct uda134x_priv *uda134x;
    548	int ret;
    549
    550	if (!pd) {
    551		dev_err(&pdev->dev, "Missing L3 bitbang function\n");
    552		return -ENODEV;
    553	}
    554
    555	uda134x = devm_kzalloc(&pdev->dev, sizeof(*uda134x), GFP_KERNEL);
    556	if (!uda134x)
    557		return -ENOMEM;
    558
    559	uda134x->pd = pd;
    560	platform_set_drvdata(pdev, uda134x);
    561
    562	if (pd->l3.use_gpios) {
    563		ret = l3_set_gpio_ops(&pdev->dev, &uda134x->pd->l3);
    564		if (ret < 0)
    565			return ret;
    566	}
    567
    568	uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd,
    569		&uda134x_regmap_config);
    570	if (IS_ERR(uda134x->regmap))
    571		return PTR_ERR(uda134x->regmap);
    572
    573	return devm_snd_soc_register_component(&pdev->dev,
    574			&soc_component_dev_uda134x, &uda134x_dai, 1);
    575}
    576
    577static struct platform_driver uda134x_codec_driver = {
    578	.driver = {
    579		.name = "uda134x-codec",
    580	},
    581	.probe = uda134x_codec_probe,
    582};
    583
    584module_platform_driver(uda134x_codec_driver);
    585
    586MODULE_DESCRIPTION("UDA134X ALSA soc codec driver");
    587MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
    588MODULE_LICENSE("GPL");