linux/sound/sh/aica.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3*
   4* Copyright Adrian McMenamin 2005, 2006, 2007
   5* <adrian@mcmen.demon.co.uk>
   6* Requires firmware (BSD licenced) available from:
   7* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/
   8* or the maintainer
   9*/
  10
  11#include <linux/init.h>
  12#include <linux/jiffies.h>
  13#include <linux/slab.h>
  14#include <linux/time.h>
  15#include <linux/wait.h>
  16#include <linux/module.h>
  17#include <linux/platform_device.h>
  18#include <linux/firmware.h>
  19#include <linux/timer.h>
  20#include <linux/delay.h>
  21#include <linux/workqueue.h>
  22#include <linux/io.h>
  23#include <sound/core.h>
  24#include <sound/control.h>
  25#include <sound/pcm.h>
  26#include <sound/initval.h>
  27#include <sound/info.h>
  28#include <asm/dma.h>
  29#include <mach/sysasic.h>
  30#include "aica.h"
  31
  32MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
  33MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");
  34MODULE_LICENSE("GPL");
  35MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}");
  36MODULE_FIRMWARE("aica_firmware.bin");
  37
  38/* module parameters */
  39#define CARD_NAME "AICA"
  40static int index = -1;
  41static char *id;
  42static bool enable = 1;
  43module_param(index, int, 0444);
  44MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
  45module_param(id, charp, 0444);
  46MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
  47module_param(enable, bool, 0644);
  48MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
  49
  50/* Simple platform device */
  51static struct platform_device *pd;
  52static struct resource aica_memory_space[2] = {
  53        {
  54         .name = "AICA ARM CONTROL",
  55         .start = ARM_RESET_REGISTER,
  56         .flags = IORESOURCE_MEM,
  57         .end = ARM_RESET_REGISTER + 3,
  58         },
  59        {
  60         .name = "AICA Sound RAM",
  61         .start = SPU_MEMORY_BASE,
  62         .flags = IORESOURCE_MEM,
  63         .end = SPU_MEMORY_BASE + 0x200000 - 1,
  64         },
  65};
  66
  67/* SPU specific functions */
  68/* spu_write_wait - wait for G2-SH FIFO to clear */
  69static void spu_write_wait(void)
  70{
  71        int time_count;
  72        time_count = 0;
  73        while (1) {
  74                if (!(readl(G2_FIFO) & 0x11))
  75                        break;
  76                /* To ensure hardware failure doesn't wedge kernel */
  77                time_count++;
  78                if (time_count > 0x10000) {
  79                        snd_printk
  80                            ("WARNING: G2 FIFO appears to be blocked.\n");
  81                        break;
  82                }
  83        }
  84}
  85
  86/* spu_memset - write to memory in SPU address space */
  87static void spu_memset(u32 toi, u32 what, int length)
  88{
  89        int i;
  90        unsigned long flags;
  91        if (snd_BUG_ON(length % 4))
  92                return;
  93        for (i = 0; i < length; i++) {
  94                if (!(i % 8))
  95                        spu_write_wait();
  96                local_irq_save(flags);
  97                writel(what, toi + SPU_MEMORY_BASE);
  98                local_irq_restore(flags);
  99                toi++;
 100        }
 101}
 102
 103/* spu_memload - write to SPU address space */
 104static void spu_memload(u32 toi, const void *from, int length)
 105{
 106        unsigned long flags;
 107        const u32 *froml = from;
 108        u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
 109        int i;
 110        u32 val;
 111        length = DIV_ROUND_UP(length, 4);
 112        spu_write_wait();
 113        for (i = 0; i < length; i++) {
 114                if (!(i % 8))
 115                        spu_write_wait();
 116                val = *froml;
 117                local_irq_save(flags);
 118                writel(val, to);
 119                local_irq_restore(flags);
 120                froml++;
 121                to++;
 122        }
 123}
 124
 125/* spu_disable - set spu registers to stop sound output */
 126static void spu_disable(void)
 127{
 128        int i;
 129        unsigned long flags;
 130        u32 regval;
 131        spu_write_wait();
 132        regval = readl(ARM_RESET_REGISTER);
 133        regval |= 1;
 134        spu_write_wait();
 135        local_irq_save(flags);
 136        writel(regval, ARM_RESET_REGISTER);
 137        local_irq_restore(flags);
 138        for (i = 0; i < 64; i++) {
 139                spu_write_wait();
 140                regval = readl(SPU_REGISTER_BASE + (i * 0x80));
 141                regval = (regval & ~0x4000) | 0x8000;
 142                spu_write_wait();
 143                local_irq_save(flags);
 144                writel(regval, SPU_REGISTER_BASE + (i * 0x80));
 145                local_irq_restore(flags);
 146        }
 147}
 148
 149/* spu_enable - set spu registers to enable sound output */
 150static void spu_enable(void)
 151{
 152        unsigned long flags;
 153        u32 regval = readl(ARM_RESET_REGISTER);
 154        regval &= ~1;
 155        spu_write_wait();
 156        local_irq_save(flags);
 157        writel(regval, ARM_RESET_REGISTER);
 158        local_irq_restore(flags);
 159}
 160
 161/* 
 162 * Halt the sound processor, clear the memory,
 163 * load some default ARM7 code, and then restart ARM7
 164*/
 165static void spu_reset(void)
 166{
 167        unsigned long flags;
 168        spu_disable();
 169        spu_memset(0, 0, 0x200000 / 4);
 170        /* Put ARM7 in endless loop */
 171        local_irq_save(flags);
 172        __raw_writel(0xea000002, SPU_MEMORY_BASE);
 173        local_irq_restore(flags);
 174        spu_enable();
 175}
 176
 177/* aica_chn_start - write to spu to start playback */
 178static void aica_chn_start(void)
 179{
 180        unsigned long flags;
 181        spu_write_wait();
 182        local_irq_save(flags);
 183        writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);
 184        local_irq_restore(flags);
 185}
 186
 187/* aica_chn_halt - write to spu to halt playback */
 188static void aica_chn_halt(void)
 189{
 190        unsigned long flags;
 191        spu_write_wait();
 192        local_irq_save(flags);
 193        writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);
 194        local_irq_restore(flags);
 195}
 196
 197/* ALSA code below */
 198static const struct snd_pcm_hardware snd_pcm_aica_playback_hw = {
 199        .info = (SNDRV_PCM_INFO_NONINTERLEAVED),
 200        .formats =
 201            (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
 202             SNDRV_PCM_FMTBIT_IMA_ADPCM),
 203        .rates = SNDRV_PCM_RATE_8000_48000,
 204        .rate_min = 8000,
 205        .rate_max = 48000,
 206        .channels_min = 1,
 207        .channels_max = 2,
 208        .buffer_bytes_max = AICA_BUFFER_SIZE,
 209        .period_bytes_min = AICA_PERIOD_SIZE,
 210        .period_bytes_max = AICA_PERIOD_SIZE,
 211        .periods_min = AICA_PERIOD_NUMBER,
 212        .periods_max = AICA_PERIOD_NUMBER,
 213};
 214
 215static int aica_dma_transfer(int channels, int buffer_size,
 216                             struct snd_pcm_substream *substream)
 217{
 218        int q, err, period_offset;
 219        struct snd_card_aica *dreamcastcard;
 220        struct snd_pcm_runtime *runtime;
 221        unsigned long flags;
 222        err = 0;
 223        dreamcastcard = substream->pcm->private_data;
 224        period_offset = dreamcastcard->clicks;
 225        period_offset %= (AICA_PERIOD_NUMBER / channels);
 226        runtime = substream->runtime;
 227        for (q = 0; q < channels; q++) {
 228                local_irq_save(flags);
 229                err = dma_xfer(AICA_DMA_CHANNEL,
 230                               (unsigned long) (runtime->dma_area +
 231                                                (AICA_BUFFER_SIZE * q) /
 232                                                channels +
 233                                                AICA_PERIOD_SIZE *
 234                                                period_offset),
 235                               AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
 236                               AICA_PERIOD_SIZE * period_offset,
 237                               buffer_size / channels, AICA_DMA_MODE);
 238                if (unlikely(err < 0)) {
 239                        local_irq_restore(flags);
 240                        break;
 241                }
 242                dma_wait_for_completion(AICA_DMA_CHANNEL);
 243                local_irq_restore(flags);
 244        }
 245        return err;
 246}
 247
 248static void startup_aica(struct snd_card_aica *dreamcastcard)
 249{
 250        spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
 251                    dreamcastcard->channel, sizeof(struct aica_channel));
 252        aica_chn_start();
 253}
 254
 255static void run_spu_dma(struct work_struct *work)
 256{
 257        int buffer_size;
 258        struct snd_pcm_runtime *runtime;
 259        struct snd_card_aica *dreamcastcard;
 260        dreamcastcard =
 261            container_of(work, struct snd_card_aica, spu_dma_work);
 262        runtime = dreamcastcard->substream->runtime;
 263        if (unlikely(dreamcastcard->dma_check == 0)) {
 264                buffer_size =
 265                    frames_to_bytes(runtime, runtime->buffer_size);
 266                if (runtime->channels > 1)
 267                        dreamcastcard->channel->flags |= 0x01;
 268                aica_dma_transfer(runtime->channels, buffer_size,
 269                                  dreamcastcard->substream);
 270                startup_aica(dreamcastcard);
 271                dreamcastcard->clicks =
 272                    buffer_size / (AICA_PERIOD_SIZE * runtime->channels);
 273                return;
 274        } else {
 275                aica_dma_transfer(runtime->channels,
 276                                  AICA_PERIOD_SIZE * runtime->channels,
 277                                  dreamcastcard->substream);
 278                snd_pcm_period_elapsed(dreamcastcard->substream);
 279                dreamcastcard->clicks++;
 280                if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
 281                        dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
 282                mod_timer(&dreamcastcard->timer, jiffies + 1);
 283        }
 284}
 285
 286static void aica_period_elapsed(struct timer_list *t)
 287{
 288        struct snd_card_aica *dreamcastcard = from_timer(dreamcastcard,
 289                                                              t, timer);
 290        struct snd_pcm_substream *substream = dreamcastcard->substream;
 291        /*timer function - so cannot sleep */
 292        int play_period;
 293        struct snd_pcm_runtime *runtime;
 294        runtime = substream->runtime;
 295        dreamcastcard = substream->pcm->private_data;
 296        /* Have we played out an additional period? */
 297        play_period =
 298            frames_to_bytes(runtime,
 299                            readl
 300                            (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) /
 301            AICA_PERIOD_SIZE;
 302        if (play_period == dreamcastcard->current_period) {
 303                /* reschedule the timer */
 304                mod_timer(&(dreamcastcard->timer), jiffies + 1);
 305                return;
 306        }
 307        if (runtime->channels > 1)
 308                dreamcastcard->current_period = play_period;
 309        if (unlikely(dreamcastcard->dma_check == 0))
 310                dreamcastcard->dma_check = 1;
 311        schedule_work(&(dreamcastcard->spu_dma_work));
 312}
 313
 314static void spu_begin_dma(struct snd_pcm_substream *substream)
 315{
 316        struct snd_card_aica *dreamcastcard;
 317        struct snd_pcm_runtime *runtime;
 318        runtime = substream->runtime;
 319        dreamcastcard = substream->pcm->private_data;
 320        /*get the queue to do the work */
 321        schedule_work(&(dreamcastcard->spu_dma_work));
 322        mod_timer(&dreamcastcard->timer, jiffies + 4);
 323}
 324
 325static int snd_aicapcm_pcm_open(struct snd_pcm_substream
 326                                *substream)
 327{
 328        struct snd_pcm_runtime *runtime;
 329        struct aica_channel *channel;
 330        struct snd_card_aica *dreamcastcard;
 331        if (!enable)
 332                return -ENOENT;
 333        dreamcastcard = substream->pcm->private_data;
 334        channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL);
 335        if (!channel)
 336                return -ENOMEM;
 337        /* set defaults for channel */
 338        channel->sfmt = SM_8BIT;
 339        channel->cmd = AICA_CMD_START;
 340        channel->vol = dreamcastcard->master_volume;
 341        channel->pan = 0x80;
 342        channel->pos = 0;
 343        channel->flags = 0;     /* default to mono */
 344        dreamcastcard->channel = channel;
 345        runtime = substream->runtime;
 346        runtime->hw = snd_pcm_aica_playback_hw;
 347        spu_enable();
 348        dreamcastcard->clicks = 0;
 349        dreamcastcard->current_period = 0;
 350        dreamcastcard->dma_check = 0;
 351        return 0;
 352}
 353
 354static int snd_aicapcm_pcm_close(struct snd_pcm_substream
 355                                 *substream)
 356{
 357        struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
 358        flush_work(&(dreamcastcard->spu_dma_work));
 359        del_timer(&dreamcastcard->timer);
 360        dreamcastcard->substream = NULL;
 361        kfree(dreamcastcard->channel);
 362        spu_disable();
 363        return 0;
 364}
 365
 366static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
 367                                   *substream)
 368{
 369        struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
 370        if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE)
 371                dreamcastcard->channel->sfmt = SM_16BIT;
 372        dreamcastcard->channel->freq = substream->runtime->rate;
 373        dreamcastcard->substream = substream;
 374        return 0;
 375}
 376
 377static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream
 378                                   *substream, int cmd)
 379{
 380        switch (cmd) {
 381        case SNDRV_PCM_TRIGGER_START:
 382                spu_begin_dma(substream);
 383                break;
 384        case SNDRV_PCM_TRIGGER_STOP:
 385                aica_chn_halt();
 386                break;
 387        default:
 388                return -EINVAL;
 389        }
 390        return 0;
 391}
 392
 393static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
 394                                             *substream)
 395{
 396        return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);
 397}
 398
 399static const struct snd_pcm_ops snd_aicapcm_playback_ops = {
 400        .open = snd_aicapcm_pcm_open,
 401        .close = snd_aicapcm_pcm_close,
 402        .prepare = snd_aicapcm_pcm_prepare,
 403        .trigger = snd_aicapcm_pcm_trigger,
 404        .pointer = snd_aicapcm_pcm_pointer,
 405};
 406
 407/* TO DO: set up to handle more than one pcm instance */
 408static int __init snd_aicapcmchip(struct snd_card_aica
 409                                  *dreamcastcard, int pcm_index)
 410{
 411        struct snd_pcm *pcm;
 412        int err;
 413        /* AICA has no capture ability */
 414        err =
 415            snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0,
 416                        &pcm);
 417        if (unlikely(err < 0))
 418                return err;
 419        pcm->private_data = dreamcastcard;
 420        strcpy(pcm->name, "AICA PCM");
 421        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
 422                        &snd_aicapcm_playback_ops);
 423        /* Allocate the DMA buffers */
 424        snd_pcm_set_managed_buffer_all(pcm,
 425                                       SNDRV_DMA_TYPE_CONTINUOUS,
 426                                       NULL,
 427                                       AICA_BUFFER_SIZE,
 428                                       AICA_BUFFER_SIZE);
 429        return 0;
 430}
 431
 432/* Mixer controls */
 433#define aica_pcmswitch_info             snd_ctl_boolean_mono_info
 434
 435static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol,
 436                              struct snd_ctl_elem_value *ucontrol)
 437{
 438        ucontrol->value.integer.value[0] = 1;   /* TO DO: Fix me */
 439        return 0;
 440}
 441
 442static int aica_pcmswitch_put(struct snd_kcontrol *kcontrol,
 443                              struct snd_ctl_elem_value *ucontrol)
 444{
 445        if (ucontrol->value.integer.value[0] == 1)
 446                return 0;       /* TO DO: Fix me */
 447        else
 448                aica_chn_halt();
 449        return 0;
 450}
 451
 452static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol,
 453                               struct snd_ctl_elem_info *uinfo)
 454{
 455        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 456        uinfo->count = 1;
 457        uinfo->value.integer.min = 0;
 458        uinfo->value.integer.max = 0xFF;
 459        return 0;
 460}
 461
 462static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,
 463                              struct snd_ctl_elem_value *ucontrol)
 464{
 465        struct snd_card_aica *dreamcastcard;
 466        dreamcastcard = kcontrol->private_data;
 467        if (unlikely(!dreamcastcard->channel))
 468                return -ETXTBSY;        /* we've not yet been set up */
 469        ucontrol->value.integer.value[0] = dreamcastcard->channel->vol;
 470        return 0;
 471}
 472
 473static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
 474                              struct snd_ctl_elem_value *ucontrol)
 475{
 476        struct snd_card_aica *dreamcastcard;
 477        unsigned int vol;
 478        dreamcastcard = kcontrol->private_data;
 479        if (unlikely(!dreamcastcard->channel))
 480                return -ETXTBSY;
 481        vol = ucontrol->value.integer.value[0];
 482        if (vol > 0xff)
 483                return -EINVAL;
 484        if (unlikely(dreamcastcard->channel->vol == vol))
 485                return 0;
 486        dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
 487        dreamcastcard->master_volume = ucontrol->value.integer.value[0];
 488        spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
 489                    dreamcastcard->channel, sizeof(struct aica_channel));
 490        return 1;
 491}
 492
 493static const struct snd_kcontrol_new snd_aica_pcmswitch_control = {
 494        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 495        .name = "PCM Playback Switch",
 496        .index = 0,
 497        .info = aica_pcmswitch_info,
 498        .get = aica_pcmswitch_get,
 499        .put = aica_pcmswitch_put
 500};
 501
 502static const struct snd_kcontrol_new snd_aica_pcmvolume_control = {
 503        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 504        .name = "PCM Playback Volume",
 505        .index = 0,
 506        .info = aica_pcmvolume_info,
 507        .get = aica_pcmvolume_get,
 508        .put = aica_pcmvolume_put
 509};
 510
 511static int load_aica_firmware(void)
 512{
 513        int err;
 514        const struct firmware *fw_entry;
 515        spu_reset();
 516        err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev);
 517        if (unlikely(err))
 518                return err;
 519        /* write firmware into memory */
 520        spu_disable();
 521        spu_memload(0, fw_entry->data, fw_entry->size);
 522        spu_enable();
 523        release_firmware(fw_entry);
 524        return err;
 525}
 526
 527static int add_aicamixer_controls(struct snd_card_aica *dreamcastcard)
 528{
 529        int err;
 530        err = snd_ctl_add
 531            (dreamcastcard->card,
 532             snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard));
 533        if (unlikely(err < 0))
 534                return err;
 535        err = snd_ctl_add
 536            (dreamcastcard->card,
 537             snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard));
 538        if (unlikely(err < 0))
 539                return err;
 540        return 0;
 541}
 542
 543static int snd_aica_remove(struct platform_device *devptr)
 544{
 545        struct snd_card_aica *dreamcastcard;
 546        dreamcastcard = platform_get_drvdata(devptr);
 547        if (unlikely(!dreamcastcard))
 548                return -ENODEV;
 549        snd_card_free(dreamcastcard->card);
 550        kfree(dreamcastcard);
 551        return 0;
 552}
 553
 554static int snd_aica_probe(struct platform_device *devptr)
 555{
 556        int err;
 557        struct snd_card_aica *dreamcastcard;
 558        dreamcastcard = kzalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
 559        if (unlikely(!dreamcastcard))
 560                return -ENOMEM;
 561        err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER,
 562                           THIS_MODULE, 0, &dreamcastcard->card);
 563        if (unlikely(err < 0)) {
 564                kfree(dreamcastcard);
 565                return err;
 566        }
 567        strcpy(dreamcastcard->card->driver, "snd_aica");
 568        strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
 569        strcpy(dreamcastcard->card->longname,
 570               "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
 571        /* Prepare to use the queue */
 572        INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
 573        timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0);
 574        /* Load the PCM 'chip' */
 575        err = snd_aicapcmchip(dreamcastcard, 0);
 576        if (unlikely(err < 0))
 577                goto freedreamcast;
 578        /* Add basic controls */
 579        err = add_aicamixer_controls(dreamcastcard);
 580        if (unlikely(err < 0))
 581                goto freedreamcast;
 582        /* Register the card with ALSA subsystem */
 583        err = snd_card_register(dreamcastcard->card);
 584        if (unlikely(err < 0))
 585                goto freedreamcast;
 586        platform_set_drvdata(devptr, dreamcastcard);
 587        snd_printk
 588            ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
 589        return 0;
 590      freedreamcast:
 591        snd_card_free(dreamcastcard->card);
 592        kfree(dreamcastcard);
 593        return err;
 594}
 595
 596static struct platform_driver snd_aica_driver = {
 597        .probe = snd_aica_probe,
 598        .remove = snd_aica_remove,
 599        .driver = {
 600                .name = SND_AICA_DRIVER,
 601        },
 602};
 603
 604static int __init aica_init(void)
 605{
 606        int err;
 607        err = platform_driver_register(&snd_aica_driver);
 608        if (unlikely(err < 0))
 609                return err;
 610        pd = platform_device_register_simple(SND_AICA_DRIVER, -1,
 611                                             aica_memory_space, 2);
 612        if (IS_ERR(pd)) {
 613                platform_driver_unregister(&snd_aica_driver);
 614                return PTR_ERR(pd);
 615        }
 616        /* Load the firmware */
 617        return load_aica_firmware();
 618}
 619
 620static void __exit aica_exit(void)
 621{
 622        platform_device_unregister(pd);
 623        platform_driver_unregister(&snd_aica_driver);
 624        /* Kill any sound still playing and reset ARM7 to safe state */
 625        spu_reset();
 626}
 627
 628module_init(aica_init);
 629module_exit(aica_exit);
 630