Merge branch 'x86/for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip...
[pandora-kernel.git] / sound / soc / fsl / mpc8610_hpcd.c
1 /**
2  * Freescale MPC8610HPCD ALSA SoC Fabric driver
3  *
4  * Author: Timur Tabi <timur@freescale.com>
5  *
6  * Copyright 2007-2008 Freescale Semiconductor, Inc.  This file is licensed
7  * under the terms of the GNU General Public License version 2.  This
8  * program is licensed "as is" without any warranty of any kind, whether
9  * express or implied.
10  */
11
12 #include <linux/module.h>
13 #include <linux/interrupt.h>
14 #include <linux/of_device.h>
15 #include <linux/of_platform.h>
16 #include <sound/soc.h>
17 #include <asm/immap_86xx.h>
18
19 #include "../codecs/cs4270.h"
20 #include "fsl_dma.h"
21 #include "fsl_ssi.h"
22
23 /**
24  * mpc8610_hpcd_data: fabric-specific ASoC device data
25  *
26  * This structure contains data for a single sound platform device on an
27  * MPC8610 HPCD.  Some of the data is taken from the device tree.
28  */
29 struct mpc8610_hpcd_data {
30         struct snd_soc_device sound_devdata;
31         struct snd_soc_dai_link dai;
32         struct snd_soc_machine machine;
33         unsigned int dai_format;
34         unsigned int codec_clk_direction;
35         unsigned int cpu_clk_direction;
36         unsigned int clk_frequency;
37         struct ccsr_guts __iomem *guts;
38         struct ccsr_ssi __iomem *ssi;
39         unsigned int ssi_id;            /* 0 = SSI1, 1 = SSI2, etc */
40         unsigned int ssi_irq;
41         unsigned int dma_id;            /* 0 = DMA1, 1 = DMA2, etc */
42         unsigned int dma_irq[2];
43         struct ccsr_dma_channel __iomem *dma[2];
44         unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
45 };
46
47 /**
48  * mpc8610_hpcd_machine_probe: initalize the board
49  *
50  * This function is called when platform_device_add() is called.  It is used
51  * to initialize the board-specific hardware.
52  *
53  * Here we program the DMACR and PMUXCR registers.
54  */
55 static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
56 {
57         struct mpc8610_hpcd_data *machine_data =
58                 sound_device->dev.platform_data;
59
60         /* Program the signal routing between the SSI and the DMA */
61         guts_set_dmacr(machine_data->guts, machine_data->dma_id,
62                 machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI);
63         guts_set_dmacr(machine_data->guts, machine_data->dma_id,
64                 machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI);
65
66         guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
67                 machine_data->dma_channel_id[0], 0);
68         guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
69                 machine_data->dma_channel_id[1], 0);
70
71         guts_set_pmuxcr_dma(machine_data->guts, 1, 0, 0);
72         guts_set_pmuxcr_dma(machine_data->guts, 1, 3, 0);
73         guts_set_pmuxcr_dma(machine_data->guts, 0, 3, 0);
74
75         switch (machine_data->ssi_id) {
76         case 0:
77                 clrsetbits_be32(&machine_data->guts->pmuxcr,
78                         CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI);
79                 break;
80         case 1:
81                 clrsetbits_be32(&machine_data->guts->pmuxcr,
82                         CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI);
83                 break;
84         }
85
86         return 0;
87 }
88
89 /**
90  * mpc8610_hpcd_startup: program the board with various hardware parameters
91  *
92  * This function takes board-specific information, like clock frequencies
93  * and serial data formats, and passes that information to the codec and
94  * transport drivers.
95  */
96 static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
97 {
98         struct snd_soc_pcm_runtime *rtd = substream->private_data;
99         struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
100         struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
101         struct mpc8610_hpcd_data *machine_data =
102                 rtd->socdev->dev->platform_data;
103         int ret = 0;
104
105         /* Tell the CPU driver what the serial protocol is. */
106         ret = snd_soc_dai_set_fmt(cpu_dai, machine_data->dai_format);
107         if (ret < 0) {
108                 dev_err(substream->pcm->card->dev,
109                         "could not set CPU driver audio format\n");
110                 return ret;
111         }
112
113         /* Tell the codec driver what the serial protocol is. */
114         ret = snd_soc_dai_set_fmt(codec_dai, machine_data->dai_format);
115         if (ret < 0) {
116                 dev_err(substream->pcm->card->dev,
117                         "could not set codec driver audio format\n");
118                 return ret;
119         }
120
121         /*
122          * Tell the CPU driver what the clock frequency is, and whether it's a
123          * slave or master.
124          */
125         ret = snd_soc_dai_set_sysclk(cpu_dai, 0,
126                                         machine_data->clk_frequency,
127                                         machine_data->cpu_clk_direction);
128         if (ret < 0) {
129                 dev_err(substream->pcm->card->dev,
130                         "could not set CPU driver clock parameters\n");
131                 return ret;
132         }
133
134         /*
135          * Tell the codec driver what the MCLK frequency is, and whether it's
136          * a slave or master.
137          */
138         ret = snd_soc_dai_set_sysclk(codec_dai, 0,
139                                         machine_data->clk_frequency,
140                                         machine_data->codec_clk_direction);
141         if (ret < 0) {
142                 dev_err(substream->pcm->card->dev,
143                         "could not set codec driver clock params\n");
144                 return ret;
145         }
146
147         return 0;
148 }
149
150 /**
151  * mpc8610_hpcd_machine_remove: Remove the sound device
152  *
153  * This function is called to remove the sound device for one SSI.  We
154  * de-program the DMACR and PMUXCR register.
155  */
156 int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
157 {
158         struct mpc8610_hpcd_data *machine_data =
159                 sound_device->dev.platform_data;
160
161         /* Restore the signal routing */
162
163         guts_set_dmacr(machine_data->guts, machine_data->dma_id,
164                 machine_data->dma_channel_id[0], 0);
165         guts_set_dmacr(machine_data->guts, machine_data->dma_id,
166                 machine_data->dma_channel_id[1], 0);
167
168         switch (machine_data->ssi_id) {
169         case 0:
170                 clrsetbits_be32(&machine_data->guts->pmuxcr,
171                         CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
172                 break;
173         case 1:
174                 clrsetbits_be32(&machine_data->guts->pmuxcr,
175                         CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA);
176                 break;
177         }
178
179         return 0;
180 }
181
182 /**
183  * mpc8610_hpcd_ops: ASoC fabric driver operations
184  */
185 static struct snd_soc_ops mpc8610_hpcd_ops = {
186         .startup = mpc8610_hpcd_startup,
187 };
188
189 /**
190  * mpc8610_hpcd_machine: ASoC machine data
191  */
192 static struct snd_soc_machine mpc8610_hpcd_machine = {
193         .probe = mpc8610_hpcd_machine_probe,
194         .remove = mpc8610_hpcd_machine_remove,
195         .name = "MPC8610 HPCD",
196         .num_links = 1,
197 };
198
199 /**
200  * mpc8610_hpcd_probe: OF probe function for the fabric driver
201  *
202  * This function gets called when an SSI node is found in the device tree.
203  *
204  * Although this is a fabric driver, the SSI node is the "master" node with
205  * respect to audio hardware connections.  Therefore, we create a new ASoC
206  * device for each new SSI node that has a codec attached.
207  *
208  * FIXME: Currently, we only support one DMA controller, so if there are
209  * multiple SSI nodes with codecs, only the first will be supported.
210  *
211  * FIXME: Even if we did support multiple DMA controllers, we have no
212  * mechanism for assigning DMA controllers and channels to the individual
213  * SSI devices.  We also probably aren't compatible with the generic Elo DMA
214  * device driver.
215  */
216 static int mpc8610_hpcd_probe(struct of_device *ofdev,
217         const struct of_device_id *match)
218 {
219         struct device_node *np = ofdev->node;
220         struct device_node *codec_np = NULL;
221         struct device_node *guts_np = NULL;
222         struct device_node *dma_np = NULL;
223         struct device_node *dma_channel_np = NULL;
224         const phandle *codec_ph;
225         const char *sprop;
226         const u32 *iprop;
227         struct resource res;
228         struct platform_device *sound_device = NULL;
229         struct mpc8610_hpcd_data *machine_data;
230         struct fsl_ssi_info ssi_info;
231         struct fsl_dma_info dma_info;
232         int ret = -ENODEV;
233
234         machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL);
235         if (!machine_data)
236                 return -ENOMEM;
237
238         memset(&ssi_info, 0, sizeof(ssi_info));
239         memset(&dma_info, 0, sizeof(dma_info));
240
241         ssi_info.dev = &ofdev->dev;
242
243         /*
244          * We are only interested in SSIs with a codec phandle in them, so let's
245          * make sure this SSI has one.
246          */
247         codec_ph = of_get_property(np, "codec-handle", NULL);
248         if (!codec_ph)
249                 goto error;
250
251         codec_np = of_find_node_by_phandle(*codec_ph);
252         if (!codec_np)
253                 goto error;
254
255         /* The MPC8610 HPCD only knows about the CS4270 codec, so reject
256            anything else. */
257         if (!of_device_is_compatible(codec_np, "cirrus,cs4270"))
258                 goto error;
259
260         /* Get the device ID */
261         iprop = of_get_property(np, "cell-index", NULL);
262         if (!iprop) {
263                 dev_err(&ofdev->dev, "cell-index property not found\n");
264                 ret = -EINVAL;
265                 goto error;
266         }
267         machine_data->ssi_id = *iprop;
268         ssi_info.id = *iprop;
269
270         /* Get the serial format and clock direction. */
271         sprop = of_get_property(np, "fsl,mode", NULL);
272         if (!sprop) {
273                 dev_err(&ofdev->dev, "fsl,mode property not found\n");
274                 ret = -EINVAL;
275                 goto error;
276         }
277
278         if (strcasecmp(sprop, "i2s-slave") == 0) {
279                 machine_data->dai_format = SND_SOC_DAIFMT_I2S;
280                 machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
281                 machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
282
283                 /*
284                  * In i2s-slave mode, the codec has its own clock source, so we
285                  * need to get the frequency from the device tree and pass it to
286                  * the codec driver.
287                  */
288                 iprop = of_get_property(codec_np, "clock-frequency", NULL);
289                 if (!iprop || !*iprop) {
290                         dev_err(&ofdev->dev, "codec bus-frequency property "
291                                 "is missing or invalid\n");
292                         ret = -EINVAL;
293                         goto error;
294                 }
295                 machine_data->clk_frequency = *iprop;
296         } else if (strcasecmp(sprop, "i2s-master") == 0) {
297                 machine_data->dai_format = SND_SOC_DAIFMT_I2S;
298                 machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
299                 machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
300         } else if (strcasecmp(sprop, "lj-slave") == 0) {
301                 machine_data->dai_format = SND_SOC_DAIFMT_LEFT_J;
302                 machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
303                 machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
304         } else if (strcasecmp(sprop, "lj-master") == 0) {
305                 machine_data->dai_format = SND_SOC_DAIFMT_LEFT_J;
306                 machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
307                 machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
308         } else if (strcasecmp(sprop, "rj-slave") == 0) {
309                 machine_data->dai_format = SND_SOC_DAIFMT_RIGHT_J;
310                 machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
311                 machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
312         } else if (strcasecmp(sprop, "rj-master") == 0) {
313                 machine_data->dai_format = SND_SOC_DAIFMT_RIGHT_J;
314                 machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
315                 machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
316         } else if (strcasecmp(sprop, "ac97-slave") == 0) {
317                 machine_data->dai_format = SND_SOC_DAIFMT_AC97;
318                 machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT;
319                 machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN;
320         } else if (strcasecmp(sprop, "ac97-master") == 0) {
321                 machine_data->dai_format = SND_SOC_DAIFMT_AC97;
322                 machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
323                 machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT;
324         } else {
325                 dev_err(&ofdev->dev,
326                         "unrecognized fsl,mode property \"%s\"\n", sprop);
327                 ret = -EINVAL;
328                 goto error;
329         }
330
331         if (!machine_data->clk_frequency) {
332                 dev_err(&ofdev->dev, "unknown clock frequency\n");
333                 ret = -EINVAL;
334                 goto error;
335         }
336
337         /* Read the SSI information from the device tree */
338         ret = of_address_to_resource(np, 0, &res);
339         if (ret) {
340                 dev_err(&ofdev->dev, "could not obtain SSI address\n");
341                 goto error;
342         }
343         if (!res.start) {
344                 dev_err(&ofdev->dev, "invalid SSI address\n");
345                 goto error;
346         }
347         ssi_info.ssi_phys = res.start;
348
349         machine_data->ssi = ioremap(ssi_info.ssi_phys, sizeof(struct ccsr_ssi));
350         if (!machine_data->ssi) {
351                 dev_err(&ofdev->dev, "could not map SSI address %x\n",
352                         ssi_info.ssi_phys);
353                 ret = -EINVAL;
354                 goto error;
355         }
356         ssi_info.ssi = machine_data->ssi;
357
358
359         /* Get the IRQ of the SSI */
360         machine_data->ssi_irq = irq_of_parse_and_map(np, 0);
361         if (!machine_data->ssi_irq) {
362                 dev_err(&ofdev->dev, "could not get SSI IRQ\n");
363                 ret = -EINVAL;
364                 goto error;
365         }
366         ssi_info.irq = machine_data->ssi_irq;
367
368
369         /* Map the global utilities registers. */
370         guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
371         if (!guts_np) {
372                 dev_err(&ofdev->dev, "could not obtain address of GUTS\n");
373                 ret = -EINVAL;
374                 goto error;
375         }
376         machine_data->guts = of_iomap(guts_np, 0);
377         of_node_put(guts_np);
378         if (!machine_data->guts) {
379                 dev_err(&ofdev->dev, "could not map GUTS\n");
380                 ret = -EINVAL;
381                 goto error;
382         }
383
384         /* Find the DMA channels to use.  For now, we always use the first DMA
385            controller. */
386         for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") {
387                 iprop = of_get_property(dma_np, "cell-index", NULL);
388                 if (iprop && (*iprop == 0)) {
389                         of_node_put(dma_np);
390                         break;
391                 }
392         }
393         if (!dma_np) {
394                 dev_err(&ofdev->dev, "could not find DMA node\n");
395                 ret = -EINVAL;
396                 goto error;
397         }
398         machine_data->dma_id = *iprop;
399
400         /*
401          * Find the DMA channels to use.  For now, we always use DMA channel 0
402          * for playback, and DMA channel 1 for capture.
403          */
404         while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) {
405                 iprop = of_get_property(dma_channel_np, "cell-index", NULL);
406                 /* Is it DMA channel 0? */
407                 if (iprop && (*iprop == 0)) {
408                         /* dma_channel[0] and dma_irq[0] are for playback */
409                         dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0);
410                         dma_info.dma_irq[0] =
411                                 irq_of_parse_and_map(dma_channel_np, 0);
412                         machine_data->dma_channel_id[0] = *iprop;
413                         continue;
414                 }
415                 if (iprop && (*iprop == 1)) {
416                         /* dma_channel[1] and dma_irq[1] are for capture */
417                         dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0);
418                         dma_info.dma_irq[1] =
419                                 irq_of_parse_and_map(dma_channel_np, 0);
420                         machine_data->dma_channel_id[1] = *iprop;
421                         continue;
422                 }
423         }
424         if (!dma_info.dma_channel[0] || !dma_info.dma_channel[1] ||
425             !dma_info.dma_irq[0] || !dma_info.dma_irq[1]) {
426                 dev_err(&ofdev->dev, "could not find DMA channels\n");
427                 ret = -EINVAL;
428                 goto error;
429         }
430
431         dma_info.ssi_stx_phys = ssi_info.ssi_phys +
432                 offsetof(struct ccsr_ssi, stx0);
433         dma_info.ssi_srx_phys = ssi_info.ssi_phys +
434                 offsetof(struct ccsr_ssi, srx0);
435
436         /* We have the DMA information, so tell the DMA driver what it is */
437         if (!fsl_dma_configure(&dma_info)) {
438                 dev_err(&ofdev->dev, "could not instantiate DMA device\n");
439                 ret = -EBUSY;
440                 goto error;
441         }
442
443         /*
444          * Initialize our DAI data structure.  We should probably get this
445          * information from the device tree.
446          */
447         machine_data->dai.name = "CS4270";
448         machine_data->dai.stream_name = "CS4270";
449
450         machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info);
451         machine_data->dai.codec_dai = &cs4270_dai; /* The codec_dai we want */
452         machine_data->dai.ops = &mpc8610_hpcd_ops;
453
454         mpc8610_hpcd_machine.dai_link = &machine_data->dai;
455
456         /* Allocate a new audio platform device structure */
457         sound_device = platform_device_alloc("soc-audio", -1);
458         if (!sound_device) {
459                 dev_err(&ofdev->dev, "platform device allocation failed\n");
460                 ret = -ENOMEM;
461                 goto error;
462         }
463
464         machine_data->sound_devdata.machine = &mpc8610_hpcd_machine;
465         machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270;
466         machine_data->sound_devdata.platform = &fsl_soc_platform;
467
468         sound_device->dev.platform_data = machine_data;
469
470
471         /* Set the platform device and ASoC device to point to each other */
472         platform_set_drvdata(sound_device, &machine_data->sound_devdata);
473
474         machine_data->sound_devdata.dev = &sound_device->dev;
475
476
477         /* Tell ASoC to probe us.  This will call mpc8610_hpcd_machine.probe(),
478            if it exists. */
479         ret = platform_device_add(sound_device);
480
481         if (ret) {
482                 dev_err(&ofdev->dev, "platform device add failed\n");
483                 goto error;
484         }
485
486         dev_set_drvdata(&ofdev->dev, sound_device);
487
488         return 0;
489
490 error:
491         of_node_put(codec_np);
492         of_node_put(guts_np);
493         of_node_put(dma_np);
494         of_node_put(dma_channel_np);
495
496         if (sound_device)
497                 platform_device_unregister(sound_device);
498
499         if (machine_data->dai.cpu_dai)
500                 fsl_ssi_destroy_dai(machine_data->dai.cpu_dai);
501
502         if (ssi_info.ssi)
503                 iounmap(ssi_info.ssi);
504
505         if (ssi_info.irq)
506                 irq_dispose_mapping(ssi_info.irq);
507
508         if (dma_info.dma_channel[0])
509                 iounmap(dma_info.dma_channel[0]);
510
511         if (dma_info.dma_channel[1])
512                 iounmap(dma_info.dma_channel[1]);
513
514         if (dma_info.dma_irq[0])
515                 irq_dispose_mapping(dma_info.dma_irq[0]);
516
517         if (dma_info.dma_irq[1])
518                 irq_dispose_mapping(dma_info.dma_irq[1]);
519
520         if (machine_data->guts)
521                 iounmap(machine_data->guts);
522
523         kfree(machine_data);
524
525         return ret;
526 }
527
528 /**
529  * mpc8610_hpcd_remove: remove the OF device
530  *
531  * This function is called when the OF device is removed.
532  */
533 static int mpc8610_hpcd_remove(struct of_device *ofdev)
534 {
535         struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev);
536         struct mpc8610_hpcd_data *machine_data =
537                 sound_device->dev.platform_data;
538
539         platform_device_unregister(sound_device);
540
541         if (machine_data->dai.cpu_dai)
542                 fsl_ssi_destroy_dai(machine_data->dai.cpu_dai);
543
544         if (machine_data->ssi)
545                 iounmap(machine_data->ssi);
546
547         if (machine_data->dma[0])
548                 iounmap(machine_data->dma[0]);
549
550         if (machine_data->dma[1])
551                 iounmap(machine_data->dma[1]);
552
553         if (machine_data->dma_irq[0])
554                 irq_dispose_mapping(machine_data->dma_irq[0]);
555
556         if (machine_data->dma_irq[1])
557                 irq_dispose_mapping(machine_data->dma_irq[1]);
558
559         if (machine_data->guts)
560                 iounmap(machine_data->guts);
561
562         kfree(machine_data);
563         sound_device->dev.platform_data = NULL;
564
565         dev_set_drvdata(&ofdev->dev, NULL);
566
567         return 0;
568 }
569
570 static struct of_device_id mpc8610_hpcd_match[] = {
571         {
572                 .compatible = "fsl,mpc8610-ssi",
573         },
574         {}
575 };
576 MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match);
577
578 static struct of_platform_driver mpc8610_hpcd_of_driver = {
579         .owner          = THIS_MODULE,
580         .name           = "mpc8610_hpcd",
581         .match_table    = mpc8610_hpcd_match,
582         .probe          = mpc8610_hpcd_probe,
583         .remove         = mpc8610_hpcd_remove,
584 };
585
586 /**
587  * mpc8610_hpcd_init: fabric driver initialization.
588  *
589  * This function is called when this module is loaded.
590  */
591 static int __init mpc8610_hpcd_init(void)
592 {
593         int ret;
594
595         printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n");
596
597         ret = of_register_platform_driver(&mpc8610_hpcd_of_driver);
598
599         if (ret)
600                 printk(KERN_ERR
601                         "mpc8610-hpcd: failed to register platform driver\n");
602
603         return ret;
604 }
605
606 /**
607  * mpc8610_hpcd_exit: fabric driver exit
608  *
609  * This function is called when this driver is unloaded.
610  */
611 static void __exit mpc8610_hpcd_exit(void)
612 {
613         of_unregister_platform_driver(&mpc8610_hpcd_of_driver);
614 }
615
616 module_init(mpc8610_hpcd_init);
617 module_exit(mpc8610_hpcd_exit);
618
619 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
620 MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver");
621 MODULE_LICENSE("GPL");