linux/sound/pci/cs46xx/dsp_spos_scb_lib.c
<<
>>
Prefs
   1/*
   2 *
   3 *   This program is free software; you can redistribute it and/or modify
   4 *   it under the terms of the GNU General Public License as published by
   5 *   the Free Software Foundation; either version 2 of the License, or
   6 *   (at your option) any later version.
   7 *
   8 *   This program is distributed in the hope that it will be useful,
   9 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 *   GNU General Public License for more details.
  12 *
  13 *   You should have received a copy of the GNU General Public License
  14 *   along with this program; if not, write to the Free Software
  15 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  16 *
  17 */
  18
  19/*
  20 * 2002-07 Benny Sjostrand benny@hostmobility.com
  21 */
  22
  23
  24#include <asm/io.h>
  25#include <linux/delay.h>
  26#include <linux/pm.h>
  27#include <linux/init.h>
  28#include <linux/slab.h>
  29#include <linux/mutex.h>
  30
  31#include <sound/core.h>
  32#include <sound/control.h>
  33#include <sound/info.h>
  34#include <sound/cs46xx.h>
  35
  36#include "cs46xx_lib.h"
  37#include "dsp_spos.h"
  38
  39struct proc_scb_info {
  40        struct dsp_scb_descriptor * scb_desc;
  41        struct snd_cs46xx *chip;
  42};
  43
  44static void remove_symbol (struct snd_cs46xx * chip, struct dsp_symbol_entry * symbol)
  45{
  46        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
  47        int symbol_index = (int)(symbol - ins->symbol_table.symbols);
  48
  49        if (snd_BUG_ON(ins->symbol_table.nsymbols <= 0))
  50                return;
  51        if (snd_BUG_ON(symbol_index < 0 ||
  52                       symbol_index >= ins->symbol_table.nsymbols))
  53                return;
  54
  55        ins->symbol_table.symbols[symbol_index].deleted = 1;
  56
  57        if (symbol_index < ins->symbol_table.highest_frag_index) {
  58                ins->symbol_table.highest_frag_index = symbol_index;
  59        }
  60  
  61        if (symbol_index == ins->symbol_table.nsymbols - 1)
  62                ins->symbol_table.nsymbols --;
  63
  64        if (ins->symbol_table.highest_frag_index > ins->symbol_table.nsymbols) {
  65                ins->symbol_table.highest_frag_index = ins->symbol_table.nsymbols;
  66        }
  67
  68}
  69
  70#ifdef CONFIG_PROC_FS
  71static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry,
  72                                           struct snd_info_buffer *buffer)
  73{
  74        struct proc_scb_info * scb_info  = entry->private_data;
  75        struct dsp_scb_descriptor * scb = scb_info->scb_desc;
  76        struct dsp_spos_instance * ins;
  77        struct snd_cs46xx *chip = scb_info->chip;
  78        int j,col;
  79        void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
  80
  81        ins = chip->dsp_spos_instance;
  82
  83        mutex_lock(&chip->spos_mutex);
  84        snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name);
  85
  86        for (col = 0,j = 0;j < 0x10; j++,col++) {
  87                if (col == 4) {
  88                        snd_iprintf(buffer,"\n");
  89                        col = 0;
  90                }
  91                snd_iprintf(buffer,"%08x ",readl(dst + (scb->address + j) * sizeof(u32)));
  92        }
  93  
  94        snd_iprintf(buffer,"\n");
  95
  96        if (scb->parent_scb_ptr != NULL) {
  97                snd_iprintf(buffer,"parent [%s:%04x] ", 
  98                            scb->parent_scb_ptr->scb_name,
  99                            scb->parent_scb_ptr->address);
 100        } else snd_iprintf(buffer,"parent [none] ");
 101  
 102        snd_iprintf(buffer,"sub_list_ptr [%s:%04x]\nnext_scb_ptr [%s:%04x]  task_entry [%s:%04x]\n",
 103                    scb->sub_list_ptr->scb_name,
 104                    scb->sub_list_ptr->address,
 105                    scb->next_scb_ptr->scb_name,
 106                    scb->next_scb_ptr->address,
 107                    scb->task_entry->symbol_name,
 108                    scb->task_entry->address);
 109
 110        snd_iprintf(buffer,"index [%d] ref_count [%d]\n",scb->index,scb->ref_count);  
 111        mutex_unlock(&chip->spos_mutex);
 112}
 113#endif
 114
 115static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb)
 116{
 117        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 118        unsigned long flags;
 119
 120        if ( scb->parent_scb_ptr ) {
 121                /* unlink parent SCB */
 122                if (snd_BUG_ON(scb->parent_scb_ptr->sub_list_ptr != scb &&
 123                               scb->parent_scb_ptr->next_scb_ptr != scb))
 124                        return;
 125  
 126                if (scb->parent_scb_ptr->sub_list_ptr == scb) {
 127
 128                        if (scb->next_scb_ptr == ins->the_null_scb) {
 129                                /* last and only node in parent sublist */
 130                                scb->parent_scb_ptr->sub_list_ptr = scb->sub_list_ptr;
 131
 132                                if (scb->sub_list_ptr != ins->the_null_scb) {
 133                                        scb->sub_list_ptr->parent_scb_ptr = scb->parent_scb_ptr;
 134                                }
 135                                scb->sub_list_ptr = ins->the_null_scb;
 136                        } else {
 137                                /* first node in parent sublist */
 138                                scb->parent_scb_ptr->sub_list_ptr = scb->next_scb_ptr;
 139
 140                                if (scb->next_scb_ptr != ins->the_null_scb) {
 141                                        /* update next node parent ptr. */
 142                                        scb->next_scb_ptr->parent_scb_ptr = scb->parent_scb_ptr;
 143                                }
 144                                scb->next_scb_ptr = ins->the_null_scb;
 145                        }
 146                } else {
 147                        scb->parent_scb_ptr->next_scb_ptr = scb->next_scb_ptr;
 148
 149                        if (scb->next_scb_ptr != ins->the_null_scb) {
 150                                /* update next node parent ptr. */
 151                                scb->next_scb_ptr->parent_scb_ptr = scb->parent_scb_ptr;
 152                        }
 153                        scb->next_scb_ptr = ins->the_null_scb;
 154                }
 155
 156                spin_lock_irqsave(&chip->reg_lock, flags);    
 157
 158                /* update parent first entry in DSP RAM */
 159                cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
 160
 161                /* then update entry in DSP RAM */
 162                cs46xx_dsp_spos_update_scb(chip,scb);
 163
 164                scb->parent_scb_ptr = NULL;
 165                spin_unlock_irqrestore(&chip->reg_lock, flags);
 166        }
 167}
 168
 169static void _dsp_clear_sample_buffer (struct snd_cs46xx *chip, u32 sample_buffer_addr,
 170                                      int dword_count) 
 171{
 172        void __iomem *dst = chip->region.idx[2].remap_addr + sample_buffer_addr;
 173        int i;
 174  
 175        for (i = 0; i < dword_count ; ++i ) {
 176                writel(0, dst);
 177                dst += 4;
 178        }  
 179}
 180
 181void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb)
 182{
 183        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 184        unsigned long flags;
 185
 186        /* check integrety */
 187        if (snd_BUG_ON(scb->index < 0 ||
 188                       scb->index >= ins->nscb ||
 189                       (ins->scbs + scb->index) != scb))
 190                return;
 191
 192#if 0
 193        /* can't remove a SCB with childs before 
 194           removing childs first  */
 195        if (snd_BUG_ON(scb->sub_list_ptr != ins->the_null_scb ||
 196                       scb->next_scb_ptr != ins->the_null_scb))
 197                goto _end;
 198#endif
 199
 200        spin_lock_irqsave(&scb->lock, flags);
 201        _dsp_unlink_scb (chip,scb);
 202        spin_unlock_irqrestore(&scb->lock, flags);
 203
 204        cs46xx_dsp_proc_free_scb_desc(scb);
 205        if (snd_BUG_ON(!scb->scb_symbol))
 206                return;
 207        remove_symbol (chip,scb->scb_symbol);
 208
 209        ins->scbs[scb->index].deleted = 1;
 210
 211        if (scb->index < ins->scb_highest_frag_index)
 212                ins->scb_highest_frag_index = scb->index;
 213
 214        if (scb->index == ins->nscb - 1) {
 215                ins->nscb --;
 216        }
 217
 218        if (ins->scb_highest_frag_index > ins->nscb) {
 219                ins->scb_highest_frag_index = ins->nscb;
 220        }
 221
 222#if 0
 223        /* !!!! THIS IS A PIECE OF SHIT MADE BY ME !!! */
 224        for(i = scb->index + 1;i < ins->nscb; ++i) {
 225                ins->scbs[i - 1].index = i - 1;
 226        }
 227#endif
 228}
 229
 230
 231#ifdef CONFIG_PROC_FS
 232void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb)
 233{
 234        if (scb->proc_info) {
 235                struct proc_scb_info * scb_info = scb->proc_info->private_data;
 236
 237                snd_printdd("cs46xx_dsp_proc_free_scb_desc: freeing %s\n",scb->scb_name);
 238
 239                snd_info_free_entry(scb->proc_info);
 240                scb->proc_info = NULL;
 241
 242                kfree (scb_info);
 243        }
 244}
 245
 246void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
 247                                        struct dsp_scb_descriptor * scb)
 248{
 249        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 250        struct snd_info_entry * entry;
 251        struct proc_scb_info * scb_info;
 252
 253        /* register to proc */
 254        if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL &&
 255            scb->proc_info == NULL) {
 256  
 257                if ((entry = snd_info_create_card_entry(ins->snd_card, scb->scb_name, 
 258                                                        ins->proc_dsp_dir)) != NULL) {
 259                        scb_info = kmalloc(sizeof(struct proc_scb_info), GFP_KERNEL);
 260                        if (!scb_info) {
 261                                snd_info_free_entry(entry);
 262                                entry = NULL;
 263                                goto out;
 264                        }
 265
 266                        scb_info->chip = chip;
 267                        scb_info->scb_desc = scb;
 268      
 269                        entry->content = SNDRV_INFO_CONTENT_TEXT;
 270                        entry->private_data = scb_info;
 271                        entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
 272      
 273                        entry->c.text.read = cs46xx_dsp_proc_scb_info_read;
 274      
 275                        if (snd_info_register(entry) < 0) {
 276                                snd_info_free_entry(entry);
 277                                kfree (scb_info);
 278                                entry = NULL;
 279                        }
 280                }
 281out:
 282                scb->proc_info = entry;
 283        }
 284}
 285#endif /* CONFIG_PROC_FS */
 286
 287static struct dsp_scb_descriptor * 
 288_dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest,
 289                         struct dsp_symbol_entry * task_entry,
 290                         struct dsp_scb_descriptor * parent_scb,
 291                         int scb_child_type)
 292{
 293        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 294        struct dsp_scb_descriptor * scb;
 295  
 296        unsigned long flags;
 297
 298        if (snd_BUG_ON(!ins->the_null_scb))
 299                return NULL;
 300
 301        /* fill the data that will be wroten to DSP */
 302        scb_data[SCBsubListPtr] = 
 303                (ins->the_null_scb->address << 0x10) | ins->the_null_scb->address;
 304
 305        scb_data[SCBfuncEntryPtr] &= 0xFFFF0000;
 306        scb_data[SCBfuncEntryPtr] |= task_entry->address;
 307
 308        snd_printdd("dsp_spos: creating SCB <%s>\n",name);
 309
 310        scb = cs46xx_dsp_create_scb(chip,name,scb_data,dest);
 311
 312
 313        scb->sub_list_ptr = ins->the_null_scb;
 314        scb->next_scb_ptr = ins->the_null_scb;
 315
 316        scb->parent_scb_ptr = parent_scb;
 317        scb->task_entry = task_entry;
 318
 319  
 320        /* update parent SCB */
 321        if (scb->parent_scb_ptr) {
 322#if 0
 323                printk ("scb->parent_scb_ptr = %s\n",scb->parent_scb_ptr->scb_name);
 324                printk ("scb->parent_scb_ptr->next_scb_ptr = %s\n",scb->parent_scb_ptr->next_scb_ptr->scb_name);
 325                printk ("scb->parent_scb_ptr->sub_list_ptr = %s\n",scb->parent_scb_ptr->sub_list_ptr->scb_name);
 326#endif
 327                /* link to  parent SCB */
 328                if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) {
 329                        if (snd_BUG_ON(scb->parent_scb_ptr->next_scb_ptr !=
 330                                       ins->the_null_scb))
 331                                return NULL;
 332
 333                        scb->parent_scb_ptr->next_scb_ptr = scb;
 334
 335                } else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) {
 336                        if (snd_BUG_ON(scb->parent_scb_ptr->sub_list_ptr !=
 337                                       ins->the_null_scb))
 338                                return NULL;
 339
 340                        scb->parent_scb_ptr->sub_list_ptr = scb;
 341                } else {
 342                        snd_BUG();
 343                }
 344
 345                spin_lock_irqsave(&chip->reg_lock, flags);
 346
 347                /* update entry in DSP RAM */
 348                cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
 349
 350                spin_unlock_irqrestore(&chip->reg_lock, flags);
 351        }
 352
 353
 354        cs46xx_dsp_proc_register_scb_desc (chip,scb);
 355
 356        return scb;
 357}
 358
 359static struct dsp_scb_descriptor * 
 360cs46xx_dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data,
 361                               u32 dest, char * task_entry_name,
 362                               struct dsp_scb_descriptor * parent_scb,
 363                               int scb_child_type)
 364{
 365        struct dsp_symbol_entry * task_entry;
 366
 367        task_entry = cs46xx_dsp_lookup_symbol (chip,task_entry_name,
 368                                               SYMBOL_CODE);
 369  
 370        if (task_entry == NULL) {
 371                snd_printk (KERN_ERR "dsp_spos: symbol %s not found\n",task_entry_name);
 372                return NULL;
 373        }
 374  
 375        return _dsp_create_generic_scb (chip,name,scb_data,dest,task_entry,
 376                                        parent_scb,scb_child_type);
 377}
 378
 379struct dsp_scb_descriptor * 
 380cs46xx_dsp_create_timing_master_scb (struct snd_cs46xx *chip)
 381{
 382        struct dsp_scb_descriptor * scb;
 383  
 384        struct dsp_timing_master_scb timing_master_scb = {
 385                { 0,
 386                  0,
 387                  0,
 388                  0
 389                },
 390                { 0,
 391                  0,
 392                  0,
 393                  0,
 394                  0
 395                },
 396                0,0,
 397                0,NULL_SCB_ADDR,
 398                0,0,             /* extraSampleAccum:TMreserved */
 399                0,0,             /* codecFIFOptr:codecFIFOsyncd */
 400                0x0001,0x8000,   /* fracSampAccumQm1:TMfrmsLeftInGroup */
 401                0x0001,0x0000,   /* fracSampCorrectionQm1:TMfrmGroupLength */
 402                0x00060000       /* nSampPerFrmQ15 */
 403        };    
 404  
 405        scb = cs46xx_dsp_create_generic_scb(chip,"TimingMasterSCBInst",(u32 *)&timing_master_scb,
 406                                            TIMINGMASTER_SCB_ADDR,
 407                                            "TIMINGMASTER",NULL,SCB_NO_PARENT);
 408
 409        return scb;
 410}
 411
 412
 413struct dsp_scb_descriptor * 
 414cs46xx_dsp_create_codec_out_scb(struct snd_cs46xx * chip, char * codec_name,
 415                                u16 channel_disp, u16 fifo_addr, u16 child_scb_addr,
 416                                u32 dest, struct dsp_scb_descriptor * parent_scb,
 417                                int scb_child_type)
 418{
 419        struct dsp_scb_descriptor * scb;
 420  
 421        struct dsp_codec_output_scb codec_out_scb = {
 422                { 0,
 423                  0,
 424                  0,
 425                  0
 426                },
 427                {
 428                        0,
 429                        0,
 430                        0,
 431                        0,
 432                        0
 433                },
 434                0,0,
 435                0,NULL_SCB_ADDR,
 436                0,                      /* COstrmRsConfig */
 437                0,                      /* COstrmBufPtr */
 438                channel_disp,fifo_addr, /* leftChanBaseIOaddr:rightChanIOdisp */
 439                0x0000,0x0080,          /* (!AC97!) COexpVolChangeRate:COscaleShiftCount */
 440                0,child_scb_addr        /* COreserved - need child scb to work with rom code */
 441        };
 442  
 443  
 444        scb = cs46xx_dsp_create_generic_scb(chip,codec_name,(u32 *)&codec_out_scb,
 445                                            dest,"S16_CODECOUTPUTTASK",parent_scb,
 446                                            scb_child_type);
 447  
 448        return scb;
 449}
 450
 451struct dsp_scb_descriptor * 
 452cs46xx_dsp_create_codec_in_scb(struct snd_cs46xx * chip, char * codec_name,
 453                               u16 channel_disp, u16 fifo_addr, u16 sample_buffer_addr,
 454                               u32 dest, struct dsp_scb_descriptor * parent_scb,
 455                               int scb_child_type)
 456{
 457
 458        struct dsp_scb_descriptor * scb;
 459        struct dsp_codec_input_scb codec_input_scb = {
 460                { 0,
 461                  0,
 462                  0,
 463                  0
 464                },
 465                {
 466                        0,
 467                        0,
 468                        0,
 469                        0,
 470                        0
 471                },
 472    
 473#if 0  /* cs4620 */
 474                SyncIOSCB,NULL_SCB_ADDR
 475#else
 476                0 , 0,
 477#endif
 478                0,0,
 479
 480                RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,  /* strmRsConfig */
 481                sample_buffer_addr << 0x10,       /* strmBufPtr; defined as a dword ptr, used as a byte ptr */
 482                channel_disp,fifo_addr,           /* (!AC97!) leftChanBaseINaddr=AC97primary 
 483                                                     link input slot 3 :rightChanINdisp=""slot 4 */
 484                0x0000,0x0000,                    /* (!AC97!) ????:scaleShiftCount; no shift needed 
 485                                                     because AC97 is already 20 bits */
 486                0x80008000                        /* ??clw cwcgame.scb has 0 */
 487        };
 488  
 489        scb = cs46xx_dsp_create_generic_scb(chip,codec_name,(u32 *)&codec_input_scb,
 490                                            dest,"S16_CODECINPUTTASK",parent_scb,
 491                                            scb_child_type);
 492        return scb;
 493}
 494
 495
 496static struct dsp_scb_descriptor * 
 497cs46xx_dsp_create_pcm_reader_scb(struct snd_cs46xx * chip, char * scb_name,
 498                                 u16 sample_buffer_addr, u32 dest,
 499                                 int virtual_channel, u32 playback_hw_addr,
 500                                 struct dsp_scb_descriptor * parent_scb,
 501                                 int scb_child_type)
 502{
 503        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 504        struct dsp_scb_descriptor * scb;
 505  
 506        struct dsp_generic_scb pcm_reader_scb = {
 507    
 508                /*
 509                  Play DMA Task xfers data from host buffer to SP buffer
 510                  init/runtime variables:
 511                  PlayAC: Play Audio Data Conversion - SCB loc: 2nd dword, mask: 0x0000F000L
 512                  DATA_FMT_16BIT_ST_LTLEND(0x00000000L)   from 16-bit stereo, little-endian
 513                  DATA_FMT_8_BIT_ST_SIGNED(0x00001000L)   from 8-bit stereo, signed
 514                  DATA_FMT_16BIT_MN_LTLEND(0x00002000L)   from 16-bit mono, little-endian
 515                  DATA_FMT_8_BIT_MN_SIGNED(0x00003000L)   from 8-bit mono, signed
 516                  DATA_FMT_16BIT_ST_BIGEND(0x00004000L)   from 16-bit stereo, big-endian
 517                  DATA_FMT_16BIT_MN_BIGEND(0x00006000L)   from 16-bit mono, big-endian
 518                  DATA_FMT_8_BIT_ST_UNSIGNED(0x00009000L) from 8-bit stereo, unsigned
 519                  DATA_FMT_8_BIT_MN_UNSIGNED(0x0000b000L) from 8-bit mono, unsigned
 520                  ? Other combinations possible from:
 521                  DMA_RQ_C2_AUDIO_CONVERT_MASK    0x0000F000L
 522                  DMA_RQ_C2_AC_NONE               0x00000000L
 523                  DMA_RQ_C2_AC_8_TO_16_BIT        0x00001000L
 524                  DMA_RQ_C2_AC_MONO_TO_STEREO     0x00002000L
 525                  DMA_RQ_C2_AC_ENDIAN_CONVERT     0x00004000L
 526                  DMA_RQ_C2_AC_SIGNED_CONVERT     0x00008000L
 527        
 528                  HostBuffAddr: Host Buffer Physical Byte Address - SCB loc:3rd dword, Mask: 0xFFFFFFFFL
 529                  aligned to dword boundary
 530                */
 531                /* Basic (non scatter/gather) DMA requestor (4 ints) */
 532                { DMA_RQ_C1_SOURCE_ON_HOST +        /* source buffer is on the host */
 533                  DMA_RQ_C1_SOURCE_MOD1024 +        /* source buffer is 1024 dwords (4096 bytes) */
 534                  DMA_RQ_C1_DEST_MOD32 +            /* dest buffer(PCMreaderBuf) is 32 dwords*/
 535                  DMA_RQ_C1_WRITEBACK_SRC_FLAG +    /* ?? */
 536                  DMA_RQ_C1_WRITEBACK_DEST_FLAG +   /* ?? */
 537                  15,                             /* DwordCount-1: picked 16 for DwordCount because Jim */
 538                  /*        Barnette said that is what we should use since */
 539                  /*        we are not running in optimized mode? */
 540                  DMA_RQ_C2_AC_NONE +
 541                  DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG + /* set play interrupt (bit0) in HISR when source */
 542                  /*   buffer (on host) crosses half-way point */
 543                  virtual_channel,                   /* Play DMA channel arbitrarily set to 0 */
 544                  playback_hw_addr,                  /* HostBuffAddr (source) */
 545                  DMA_RQ_SD_SP_SAMPLE_ADDR +         /* destination buffer is in SP Sample Memory */
 546                  sample_buffer_addr                 /* SP Buffer Address (destination) */
 547                },
 548                /* Scatter/gather DMA requestor extension   (5 ints) */
 549                {
 550                        0,
 551                        0,
 552                        0,
 553                        0,
 554                        0 
 555                },
 556                /* Sublist pointer & next stream control block (SCB) link. */
 557                NULL_SCB_ADDR,NULL_SCB_ADDR,
 558                /* Pointer to this tasks parameter block & stream function pointer */
 559                0,NULL_SCB_ADDR,
 560                /* rsConfig register for stream buffer (rsDMA reg. is loaded from basicReq.daw */
 561                /*   for incoming streams, or basicReq.saw, for outgoing streams) */
 562                RSCONFIG_DMA_ENABLE +                 /* enable DMA */
 563                (19 << RSCONFIG_MAX_DMA_SIZE_SHIFT) + /* MAX_DMA_SIZE picked to be 19 since SPUD  */
 564                /*  uses it for some reason */
 565                ((dest >> 4) << RSCONFIG_STREAM_NUM_SHIFT) + /* stream number = SCBaddr/16 */
 566                RSCONFIG_SAMPLE_16STEREO +
 567                RSCONFIG_MODULO_32,             /* dest buffer(PCMreaderBuf) is 32 dwords (256 bytes) */
 568                /* Stream sample pointer & MAC-unit mode for this stream */
 569                (sample_buffer_addr << 0x10),
 570                /* Fractional increment per output sample in the input sample buffer */
 571                0, 
 572                {
 573                        /* Standard stereo volume control
 574                           default muted */
 575                        0xffff,0xffff,
 576                        0xffff,0xffff
 577                }
 578        };
 579
 580        if (ins->null_algorithm == NULL) {
 581                ins->null_algorithm =  cs46xx_dsp_lookup_symbol (chip,"NULLALGORITHM",
 582                                                                 SYMBOL_CODE);
 583    
 584                if (ins->null_algorithm == NULL) {
 585                        snd_printk (KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n");
 586                        return NULL;
 587                }    
 588        }
 589
 590        scb = _dsp_create_generic_scb(chip,scb_name,(u32 *)&pcm_reader_scb,
 591                                      dest,ins->null_algorithm,parent_scb,
 592                                      scb_child_type);
 593  
 594        return scb;
 595}
 596
 597#define GOF_PER_SEC 200
 598
 599struct dsp_scb_descriptor * 
 600cs46xx_dsp_create_src_task_scb(struct snd_cs46xx * chip, char * scb_name,
 601                               int rate,
 602                               u16 src_buffer_addr,
 603                               u16 src_delay_buffer_addr, u32 dest,
 604                               struct dsp_scb_descriptor * parent_scb,
 605                               int scb_child_type,
 606                               int pass_through)
 607{
 608
 609        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 610        struct dsp_scb_descriptor * scb;
 611        unsigned int tmp1, tmp2;
 612        unsigned int phiIncr;
 613        unsigned int correctionPerGOF, correctionPerSec;
 614
 615        snd_printdd( "dsp_spos: setting %s rate to %u\n",scb_name,rate);
 616
 617        /*
 618         *  Compute the values used to drive the actual sample rate conversion.
 619         *  The following formulas are being computed, using inline assembly
 620         *  since we need to use 64 bit arithmetic to compute the values:
 621         *
 622         *  phiIncr = floor((Fs,in * 2^26) / Fs,out)
 623         *  correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
 624         *                                   GOF_PER_SEC)
 625         *  ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M
 626         *                       GOF_PER_SEC * correctionPerGOF
 627         *
 628         *  i.e.
 629         *
 630         *  phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out)
 631         *  correctionPerGOF:correctionPerSec =
 632         *      dividend:remainder(ulOther / GOF_PER_SEC)
 633         */
 634        tmp1 = rate << 16;
 635        phiIncr = tmp1 / 48000;
 636        tmp1 -= phiIncr * 48000;
 637        tmp1 <<= 10;
 638        phiIncr <<= 10;
 639        tmp2 = tmp1 / 48000;
 640        phiIncr += tmp2;
 641        tmp1 -= tmp2 * 48000;
 642        correctionPerGOF = tmp1 / GOF_PER_SEC;
 643        tmp1 -= correctionPerGOF * GOF_PER_SEC;
 644        correctionPerSec = tmp1;
 645
 646        {
 647                struct dsp_src_task_scb src_task_scb = {
 648                        0x0028,0x00c8,
 649                        0x5555,0x0000,
 650                        0x0000,0x0000,
 651                        src_buffer_addr,1,
 652                        correctionPerGOF,correctionPerSec,
 653                        RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_32,  
 654                        0x0000,src_delay_buffer_addr,                  
 655                        0x0,                                            
 656                        0x080,(src_delay_buffer_addr + (24 * 4)),
 657                        0,0, /* next_scb, sub_list_ptr */
 658                        0,0, /* entry, this_spb */
 659                        RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_8,
 660                        src_buffer_addr << 0x10,
 661                        phiIncr,
 662                        { 
 663                                0xffff - ins->dac_volume_right,0xffff - ins->dac_volume_left,
 664                                0xffff - ins->dac_volume_right,0xffff - ins->dac_volume_left
 665                        }
 666                };
 667                
 668                if (ins->s16_up == NULL) {
 669                        ins->s16_up =  cs46xx_dsp_lookup_symbol (chip,"S16_UPSRC",
 670                                                                 SYMBOL_CODE);
 671                        
 672                        if (ins->s16_up == NULL) {
 673                                snd_printk (KERN_ERR "dsp_spos: symbol S16_UPSRC not found\n");
 674                                return NULL;
 675                        }    
 676                }
 677                
 678                /* clear buffers */
 679                _dsp_clear_sample_buffer (chip,src_buffer_addr,8);
 680                _dsp_clear_sample_buffer (chip,src_delay_buffer_addr,32);
 681                                
 682                if (pass_through) {
 683                        /* wont work with any other rate than
 684                           the native DSP rate */
 685                        snd_BUG_ON(rate != 48000);
 686
 687                        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb,
 688                                                            dest,"DMAREADER",parent_scb,
 689                                                            scb_child_type);
 690                } else {
 691                        scb = _dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb,
 692                                                      dest,ins->s16_up,parent_scb,
 693                                                      scb_child_type);
 694                }
 695
 696
 697        }
 698
 699        return scb;
 700}
 701
 702#if 0 /* not used */
 703struct dsp_scb_descriptor * 
 704cs46xx_dsp_create_filter_scb(struct snd_cs46xx * chip, char * scb_name,
 705                             u16 buffer_addr, u32 dest,
 706                             struct dsp_scb_descriptor * parent_scb,
 707                             int scb_child_type) {
 708        struct dsp_scb_descriptor * scb;
 709        
 710        struct dsp_filter_scb filter_scb = {
 711                .a0_right            = 0x41a9,
 712                .a0_left             = 0x41a9,
 713                .a1_right            = 0xb8e4,
 714                .a1_left             = 0xb8e4,
 715                .a2_right            = 0x3e55,
 716                .a2_left             = 0x3e55,
 717                
 718                .filter_unused3      = 0x0000,
 719                .filter_unused2      = 0x0000,
 720
 721                .output_buf_ptr      = buffer_addr,
 722                .init                = 0x000,
 723
 724                .prev_sample_output1 = 0x00000000,
 725                .prev_sample_output2 = 0x00000000,
 726
 727                .prev_sample_input1  = 0x00000000,
 728                .prev_sample_input2  = 0x00000000,
 729
 730                .next_scb_ptr        = 0x0000,
 731                .sub_list_ptr        = 0x0000,
 732
 733                .entry_point         = 0x0000,
 734                .spb_ptr             = 0x0000,
 735
 736                .b0_right            = 0x0e38,
 737                .b0_left             = 0x0e38,
 738                .b1_right            = 0x1c71,
 739                .b1_left             = 0x1c71,
 740                .b2_right            = 0x0e38,
 741                .b2_left             = 0x0e38,
 742        };
 743
 744
 745        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&filter_scb,
 746                                            dest,"FILTERTASK",parent_scb,
 747                                            scb_child_type);
 748
 749        return scb;
 750}
 751#endif /* not used */
 752
 753struct dsp_scb_descriptor * 
 754cs46xx_dsp_create_mix_only_scb(struct snd_cs46xx * chip, char * scb_name,
 755                               u16 mix_buffer_addr, u32 dest,
 756                               struct dsp_scb_descriptor * parent_scb,
 757                               int scb_child_type)
 758{
 759        struct dsp_scb_descriptor * scb;
 760  
 761        struct dsp_mix_only_scb master_mix_scb = {
 762                /* 0 */ { 0,
 763                          /* 1 */   0,
 764                          /* 2 */  mix_buffer_addr,
 765                          /* 3 */  0
 766                          /*   */ },
 767                {
 768                        /* 4 */  0,
 769                        /* 5 */  0,
 770                        /* 6 */  0,
 771                        /* 7 */  0,
 772                        /* 8 */  0x00000080
 773                },
 774                /* 9 */ 0,0,
 775                /* A */ 0,0,
 776                /* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_32,
 777                /* C */ (mix_buffer_addr  + (16 * 4)) << 0x10, 
 778                /* D */ 0,
 779                {
 780                        /* E */ 0x8000,0x8000,
 781                        /* F */ 0x8000,0x8000
 782                }
 783        };
 784
 785
 786        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&master_mix_scb,
 787                                            dest,"S16_MIX",parent_scb,
 788                                            scb_child_type);
 789        return scb;
 790}
 791
 792
 793struct dsp_scb_descriptor * 
 794cs46xx_dsp_create_mix_to_ostream_scb(struct snd_cs46xx * chip, char * scb_name,
 795                                     u16 mix_buffer_addr, u16 writeback_spb, u32 dest,
 796                                     struct dsp_scb_descriptor * parent_scb,
 797                                     int scb_child_type)
 798{
 799        struct dsp_scb_descriptor * scb;
 800
 801        struct dsp_mix2_ostream_scb mix2_ostream_scb = {
 802                /* Basic (non scatter/gather) DMA requestor (4 ints) */
 803                { 
 804                        DMA_RQ_C1_SOURCE_MOD64 +
 805                        DMA_RQ_C1_DEST_ON_HOST +
 806                        DMA_RQ_C1_DEST_MOD1024 +
 807                        DMA_RQ_C1_WRITEBACK_SRC_FLAG + 
 808                        DMA_RQ_C1_WRITEBACK_DEST_FLAG +
 809                        15,                            
 810      
 811                        DMA_RQ_C2_AC_NONE +
 812                        DMA_RQ_C2_SIGNAL_DEST_PINGPONG + 
 813      
 814                        CS46XX_DSP_CAPTURE_CHANNEL,                                 
 815                        DMA_RQ_SD_SP_SAMPLE_ADDR + 
 816                        mix_buffer_addr, 
 817                        0x0                   
 818                },
 819    
 820                { 0, 0, 0, 0, 0, },
 821                0,0,
 822                0,writeback_spb,
 823    
 824                RSCONFIG_DMA_ENABLE + 
 825                (19 << RSCONFIG_MAX_DMA_SIZE_SHIFT) + 
 826    
 827                ((dest >> 4) << RSCONFIG_STREAM_NUM_SHIFT) +
 828                RSCONFIG_DMA_TO_HOST + 
 829                RSCONFIG_SAMPLE_16STEREO +
 830                RSCONFIG_MODULO_64,    
 831                (mix_buffer_addr + (32 * 4)) << 0x10,
 832                1,0,            
 833                0x0001,0x0080,
 834                0xFFFF,0
 835        };
 836
 837
 838        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&mix2_ostream_scb,
 839                                
 840            dest,"S16_MIX_TO_OSTREAM",parent_scb,
 841                                            scb_child_type);
 842  
 843        return scb;
 844}
 845
 846
 847struct dsp_scb_descriptor * 
 848cs46xx_dsp_create_vari_decimate_scb(struct snd_cs46xx * chip,char * scb_name,
 849                                    u16 vari_buffer_addr0,
 850                                    u16 vari_buffer_addr1,
 851                                    u32 dest,
 852                                    struct dsp_scb_descriptor * parent_scb,
 853                                    int scb_child_type)
 854{
 855
 856        struct dsp_scb_descriptor * scb;
 857  
 858        struct dsp_vari_decimate_scb vari_decimate_scb = {
 859                0x0028,0x00c8,
 860                0x5555,0x0000,
 861                0x0000,0x0000,
 862                vari_buffer_addr0,vari_buffer_addr1,
 863    
 864                0x0028,0x00c8,
 865                RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_256, 
 866    
 867                0xFF800000,   
 868                0,
 869                0x0080,vari_buffer_addr1 + (25 * 4), 
 870    
 871                0,0, 
 872                0,0,
 873
 874                RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_8,
 875                vari_buffer_addr0 << 0x10,   
 876                0x04000000,                   
 877                {
 878                        0x8000,0x8000, 
 879                        0xFFFF,0xFFFF
 880                }
 881        };
 882
 883        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&vari_decimate_scb,
 884                                            dest,"VARIDECIMATE",parent_scb,
 885                                            scb_child_type);
 886  
 887        return scb;
 888}
 889
 890
 891static struct dsp_scb_descriptor * 
 892cs46xx_dsp_create_pcm_serial_input_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
 893                                       struct dsp_scb_descriptor * input_scb,
 894                                       struct dsp_scb_descriptor * parent_scb,
 895                                       int scb_child_type)
 896{
 897
 898        struct dsp_scb_descriptor * scb;
 899
 900
 901        struct dsp_pcm_serial_input_scb pcm_serial_input_scb = {
 902                { 0,
 903                  0,
 904                  0,
 905                  0
 906                },
 907                {
 908                        0,
 909                        0,
 910                        0,
 911                        0,
 912                        0
 913                },
 914
 915                0,0,
 916                0,0,
 917
 918                RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_16,
 919                0,
 920      /* 0xD */ 0,input_scb->address,
 921                {
 922      /* 0xE */   0x8000,0x8000,
 923      /* 0xF */   0x8000,0x8000
 924                }
 925        };
 926
 927        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&pcm_serial_input_scb,
 928                                            dest,"PCMSERIALINPUTTASK",parent_scb,
 929                                            scb_child_type);
 930        return scb;
 931}
 932
 933
 934static struct dsp_scb_descriptor * 
 935cs46xx_dsp_create_asynch_fg_tx_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
 936                                   u16 hfg_scb_address,
 937                                   u16 asynch_buffer_address,
 938                                   struct dsp_scb_descriptor * parent_scb,
 939                                   int scb_child_type)
 940{
 941
 942        struct dsp_scb_descriptor * scb;
 943
 944        struct dsp_asynch_fg_tx_scb asynch_fg_tx_scb = {
 945                0xfc00,0x03ff,      /*  Prototype sample buffer size of 256 dwords */
 946                0x0058,0x0028,      /* Min Delta 7 dwords == 28 bytes */
 947                /* : Max delta 25 dwords == 100 bytes */
 948                0,hfg_scb_address,  /* Point to HFG task SCB */
 949                0,0,                /* Initialize current Delta and Consumer ptr adjustment count */
 950                0,                  /* Initialize accumulated Phi to 0 */
 951                0,0x2aab,           /* Const 1/3 */
 952    
 953                {
 954                        0,         /* Define the unused elements */
 955                        0,
 956                        0
 957                },
 958    
 959                0,0,
 960                0,dest + AFGTxAccumPhi,
 961    
 962                RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_256, /* Stereo, 256 dword */
 963                (asynch_buffer_address) << 0x10,  /* This should be automagically synchronized
 964                                                     to the producer pointer */
 965    
 966                /* There is no correct initial value, it will depend upon the detected
 967                   rate etc  */
 968                0x18000000,                     /* Phi increment for approx 32k operation */
 969                0x8000,0x8000,                  /* Volume controls are unused at this time */
 970                0x8000,0x8000
 971        };
 972  
 973        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&asynch_fg_tx_scb,
 974                                            dest,"ASYNCHFGTXCODE",parent_scb,
 975                                            scb_child_type);
 976
 977        return scb;
 978}
 979
 980
 981struct dsp_scb_descriptor * 
 982cs46xx_dsp_create_asynch_fg_rx_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
 983                                   u16 hfg_scb_address,
 984                                   u16 asynch_buffer_address,
 985                                   struct dsp_scb_descriptor * parent_scb,
 986                                   int scb_child_type)
 987{
 988        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 989        struct dsp_scb_descriptor * scb;
 990
 991        struct dsp_asynch_fg_rx_scb asynch_fg_rx_scb = {
 992                0xfe00,0x01ff,      /*  Prototype sample buffer size of 128 dwords */
 993                0x0064,0x001c,      /* Min Delta 7 dwords == 28 bytes */
 994                                    /* : Max delta 25 dwords == 100 bytes */
 995                0,hfg_scb_address,  /* Point to HFG task SCB */
 996                0,0,                            /* Initialize current Delta and Consumer ptr adjustment count */
 997                {
 998                        0,                /* Define the unused elements */
 999                        0,
1000                        0,
1001                        0,
1002                        0
1003                },
1004      
1005                0,0,
1006                0,dest,
1007    
1008                RSCONFIG_MODULO_128 |
1009        RSCONFIG_SAMPLE_16STEREO,                         /* Stereo, 128 dword */
1010                ( (asynch_buffer_address + (16 * 4))  << 0x10),   /* This should be automagically 
1011                                                                                          synchrinized to the producer pointer */
1012    
1013                /* There is no correct initial value, it will depend upon the detected
1014                   rate etc  */
1015                0x18000000,         
1016
1017                /* Set IEC958 input volume */
1018                0xffff - ins->spdif_input_volume_right,0xffff - ins->spdif_input_volume_left,
1019                0xffff - ins->spdif_input_volume_right,0xffff - ins->spdif_input_volume_left,
1020        };
1021
1022        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&asynch_fg_rx_scb,
1023                                            dest,"ASYNCHFGRXCODE",parent_scb,
1024                                            scb_child_type);
1025
1026        return scb;
1027}
1028
1029
1030#if 0 /* not used */
1031struct dsp_scb_descriptor * 
1032cs46xx_dsp_create_output_snoop_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
1033                                   u16 snoop_buffer_address,
1034                                   struct dsp_scb_descriptor * snoop_scb,
1035                                   struct dsp_scb_descriptor * parent_scb,
1036                                   int scb_child_type)
1037{
1038
1039        struct dsp_scb_descriptor * scb;
1040  
1041        struct dsp_output_snoop_scb output_snoop_scb = {
1042                { 0,    /*  not used.  Zero */
1043                  0,
1044                  0,
1045                  0,
1046                },
1047                {
1048                        0, /* not used.  Zero */
1049                        0,
1050                        0,
1051                        0,
1052                        0
1053                },
1054    
1055                0,0,
1056                0,0,
1057    
1058                RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
1059                snoop_buffer_address << 0x10,  
1060                0,0,
1061                0,
1062                0,snoop_scb->address
1063        };
1064  
1065        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&output_snoop_scb,
1066                                            dest,"OUTPUTSNOOP",parent_scb,
1067                                            scb_child_type);
1068        return scb;
1069}
1070#endif /* not used */
1071
1072
1073struct dsp_scb_descriptor * 
1074cs46xx_dsp_create_spio_write_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
1075                                 struct dsp_scb_descriptor * parent_scb,
1076                                 int scb_child_type)
1077{
1078        struct dsp_scb_descriptor * scb;
1079  
1080        struct dsp_spio_write_scb spio_write_scb = {
1081                0,0,         /*   SPIOWAddress2:SPIOWAddress1; */
1082                0,           /*   SPIOWData1; */
1083                0,           /*   SPIOWData2; */
1084                0,0,         /*   SPIOWAddress4:SPIOWAddress3; */
1085                0,           /*   SPIOWData3; */
1086                0,           /*   SPIOWData4; */
1087                0,0,         /*   SPIOWDataPtr:Unused1; */
1088                { 0,0 },     /*   Unused2[2]; */
1089    
1090                0,0,         /*   SPIOWChildPtr:SPIOWSiblingPtr; */
1091                0,0,         /*   SPIOWThisPtr:SPIOWEntryPoint; */
1092    
1093                { 
1094                        0,
1095                        0,
1096                        0,
1097                        0,
1098                        0          /*   Unused3[5];  */
1099                }
1100        };
1101
1102        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&spio_write_scb,
1103                                            dest,"SPIOWRITE",parent_scb,
1104                                            scb_child_type);
1105
1106        return scb;
1107}
1108
1109struct dsp_scb_descriptor *
1110cs46xx_dsp_create_magic_snoop_scb(struct snd_cs46xx * chip, char * scb_name, u32 dest,
1111                                  u16 snoop_buffer_address,
1112                                  struct dsp_scb_descriptor * snoop_scb,
1113                                  struct dsp_scb_descriptor * parent_scb,
1114                                  int scb_child_type)
1115{
1116        struct dsp_scb_descriptor * scb;
1117  
1118        struct dsp_magic_snoop_task magic_snoop_scb = {
1119                /* 0 */ 0, /* i0 */
1120                /* 1 */ 0, /* i1 */
1121                /* 2 */ snoop_buffer_address << 0x10,
1122                /* 3 */ 0,snoop_scb->address,
1123                /* 4 */ 0, /* i3 */
1124                /* 5 */ 0, /* i4 */
1125                /* 6 */ 0, /* i5 */
1126                /* 7 */ 0, /* i6 */
1127                /* 8 */ 0, /* i7 */
1128                /* 9 */ 0,0, /* next_scb, sub_list_ptr */
1129                /* A */ 0,0, /* entry_point, this_ptr */
1130                /* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
1131                /* C */ snoop_buffer_address  << 0x10,
1132                /* D */ 0,
1133                /* E */ { 0x8000,0x8000,
1134                /* F */   0xffff,0xffff
1135                }
1136        };
1137
1138        scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&magic_snoop_scb,
1139                                            dest,"MAGICSNOOPTASK",parent_scb,
1140                                            scb_child_type);
1141
1142        return scb;
1143}
1144
1145static struct dsp_scb_descriptor *
1146find_next_free_scb (struct snd_cs46xx * chip, struct dsp_scb_descriptor * from)
1147{
1148        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
1149        struct dsp_scb_descriptor * scb = from;
1150
1151        while (scb->next_scb_ptr != ins->the_null_scb) {
1152                if (snd_BUG_ON(!scb->next_scb_ptr))
1153                        return NULL;
1154
1155                scb = scb->next_scb_ptr;
1156        }
1157
1158        return scb;
1159}
1160
1161static u32 pcm_reader_buffer_addr[DSP_MAX_PCM_CHANNELS] = {
1162        0x0600, /* 1 */
1163        0x1500, /* 2 */
1164        0x1580, /* 3 */
1165        0x1600, /* 4 */
1166        0x1680, /* 5 */
1167        0x1700, /* 6 */
1168        0x1780, /* 7 */
1169        0x1800, /* 8 */
1170        0x1880, /* 9 */
1171        0x1900, /* 10 */
1172        0x1980, /* 11 */
1173        0x1A00, /* 12 */
1174        0x1A80, /* 13 */
1175        0x1B00, /* 14 */
1176        0x1B80, /* 15 */
1177        0x1C00, /* 16 */
1178        0x1C80, /* 17 */
1179        0x1D00, /* 18 */
1180        0x1D80, /* 19 */
1181        0x1E00, /* 20 */
1182        0x1E80, /* 21 */
1183        0x1F00, /* 22 */
1184        0x1F80, /* 23 */
1185        0x2000, /* 24 */
1186        0x2080, /* 25 */
1187        0x2100, /* 26 */
1188        0x2180, /* 27 */
1189        0x2200, /* 28 */
1190        0x2280, /* 29 */
1191        0x2300, /* 30 */
1192        0x2380, /* 31 */
1193        0x2400, /* 32 */
1194};
1195
1196static u32 src_output_buffer_addr[DSP_MAX_SRC_NR] = {
1197        0x2B80,
1198        0x2BA0,
1199        0x2BC0,
1200        0x2BE0,
1201        0x2D00,  
1202        0x2D20,  
1203        0x2D40,  
1204        0x2D60,
1205        0x2D80,
1206        0x2DA0,
1207        0x2DC0,
1208        0x2DE0,
1209        0x2E00,
1210        0x2E20
1211};
1212
1213static u32 src_delay_buffer_addr[DSP_MAX_SRC_NR] = {
1214        0x2480,
1215        0x2500,
1216        0x2580,
1217        0x2600,
1218        0x2680,
1219        0x2700,
1220        0x2780,
1221        0x2800,
1222        0x2880,
1223        0x2900,
1224        0x2980,
1225        0x2A00,
1226        0x2A80,
1227        0x2B00
1228};
1229
1230struct dsp_pcm_channel_descriptor *
1231cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
1232                               u32 sample_rate, void * private_data, 
1233                               u32 hw_dma_addr,
1234                               int pcm_channel_id)
1235{
1236        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
1237        struct dsp_scb_descriptor * src_scb = NULL, * pcm_scb, * mixer_scb = NULL;
1238        struct dsp_scb_descriptor * src_parent_scb = NULL;
1239
1240        /* struct dsp_scb_descriptor * pcm_parent_scb; */
1241        char scb_name[DSP_MAX_SCB_NAME];
1242        int i, pcm_index = -1, insert_point, src_index = -1, pass_through = 0;
1243        unsigned long flags;
1244
1245        switch (pcm_channel_id) {
1246        case DSP_PCM_MAIN_CHANNEL:
1247                mixer_scb = ins->master_mix_scb;
1248                break;
1249        case DSP_PCM_REAR_CHANNEL:
1250                mixer_scb = ins->rear_mix_scb;
1251                break;
1252        case DSP_PCM_CENTER_LFE_CHANNEL:
1253                mixer_scb = ins->center_lfe_mix_scb;
1254                break;
1255        case DSP_PCM_S71_CHANNEL:
1256                /* TODO */
1257                snd_BUG();
1258                break;
1259        case DSP_IEC958_CHANNEL:
1260                if (snd_BUG_ON(!ins->asynch_tx_scb))
1261                        return NULL;
1262                mixer_scb = ins->asynch_tx_scb;
1263
1264                /* if sample rate is set to 48khz we pass
1265                   the Sample Rate Converted (which could
1266                   alter the raw data stream ...) */
1267                if (sample_rate == 48000) {
1268                        snd_printdd ("IEC958 pass through\n");
1269                        /* Hack to bypass creating a new SRC */
1270                        pass_through = 1;
1271                }
1272                break;
1273        default:
1274                snd_BUG();
1275                return NULL;
1276        }
1277        /* default sample rate is 44100 */
1278        if (!sample_rate) sample_rate = 44100;
1279
1280        /* search for a already created SRC SCB with the same sample rate */
1281        for (i = 0; i < DSP_MAX_PCM_CHANNELS && 
1282                     (pcm_index == -1 || src_scb == NULL); ++i) {
1283
1284                /* virtual channel reserved 
1285                   for capture */
1286                if (i == CS46XX_DSP_CAPTURE_CHANNEL) continue;
1287
1288                if (ins->pcm_channels[i].active) {
1289                        if (!src_scb && 
1290                            ins->pcm_channels[i].sample_rate == sample_rate &&
1291                            ins->pcm_channels[i].mixer_scb == mixer_scb) {
1292                                src_scb = ins->pcm_channels[i].src_scb;
1293                                ins->pcm_channels[i].src_scb->ref_count ++;
1294                                src_index = ins->pcm_channels[i].src_slot;
1295                        }
1296                } else if (pcm_index == -1) {
1297                        pcm_index = i;
1298                }
1299        }
1300
1301        if (pcm_index == -1) {
1302                snd_printk (KERN_ERR "dsp_spos: no free PCM channel\n");
1303                return NULL;
1304        }
1305
1306        if (src_scb == NULL) {
1307                if (ins->nsrc_scb >= DSP_MAX_SRC_NR) {
1308                        snd_printk(KERN_ERR "dsp_spos: to many SRC instances\n!");
1309                        return NULL;
1310                }
1311
1312                /* find a free slot */
1313                for (i = 0; i < DSP_MAX_SRC_NR; ++i) {
1314                        if (ins->src_scb_slots[i] == 0) {
1315                                src_index = i;
1316                                ins->src_scb_slots[i] = 1;
1317                                break;
1318                        }
1319                }
1320                if (snd_BUG_ON(src_index == -1))
1321                        return NULL;
1322
1323                /* we need to create a new SRC SCB */
1324                if (mixer_scb->sub_list_ptr == ins->the_null_scb) {
1325                        src_parent_scb = mixer_scb;
1326                        insert_point = SCB_ON_PARENT_SUBLIST_SCB;
1327                } else {
1328                        src_parent_scb = find_next_free_scb(chip,mixer_scb->sub_list_ptr);
1329                        insert_point = SCB_ON_PARENT_NEXT_SCB;
1330                }
1331
1332                snprintf (scb_name,DSP_MAX_SCB_NAME,"SrcTask_SCB%d",src_index);
1333                
1334                snd_printdd( "dsp_spos: creating SRC \"%s\"\n",scb_name);
1335                src_scb = cs46xx_dsp_create_src_task_scb(chip,scb_name,
1336                                                         sample_rate,
1337                                                         src_output_buffer_addr[src_index],
1338                                                         src_delay_buffer_addr[src_index],
1339                                                         /* 0x400 - 0x600 source SCBs */
1340                                                         0x400 + (src_index * 0x10) ,
1341                                                         src_parent_scb,
1342                                                         insert_point,
1343                                                         pass_through);
1344
1345                if (!src_scb) {
1346                        snd_printk (KERN_ERR "dsp_spos: failed to create SRCtaskSCB\n");
1347                        return NULL;
1348                }
1349
1350                /* cs46xx_dsp_set_src_sample_rate(chip,src_scb,sample_rate); */
1351
1352                ins->nsrc_scb ++;
1353        } 
1354  
1355  
1356        snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index);
1357
1358        snd_printdd( "dsp_spos: creating PCM \"%s\" (%d)\n",scb_name,
1359                 pcm_channel_id);
1360
1361        pcm_scb = cs46xx_dsp_create_pcm_reader_scb(chip,scb_name,
1362                                                   pcm_reader_buffer_addr[pcm_index],
1363                                                   /* 0x200 - 400 PCMreader SCBs */
1364                                                   (pcm_index * 0x10) + 0x200,
1365                                                   pcm_index,    /* virtual channel 0-31 */
1366                                                   hw_dma_addr,  /* pcm hw addr */
1367                           NULL,         /* parent SCB ptr */
1368                           0             /* insert point */ 
1369                           );
1370
1371        if (!pcm_scb) {
1372                snd_printk (KERN_ERR "dsp_spos: failed to create PCMreaderSCB\n");
1373                return NULL;
1374        }
1375        
1376        spin_lock_irqsave(&chip->reg_lock, flags);
1377        ins->pcm_channels[pcm_index].sample_rate = sample_rate;
1378        ins->pcm_channels[pcm_index].pcm_reader_scb = pcm_scb;
1379        ins->pcm_channels[pcm_index].src_scb = src_scb;
1380        ins->pcm_channels[pcm_index].unlinked = 1;
1381        ins->pcm_channels[pcm_index].private_data = private_data;
1382        ins->pcm_channels[pcm_index].src_slot = src_index;
1383        ins->pcm_channels[pcm_index].active = 1;
1384        ins->pcm_channels[pcm_index].pcm_slot = pcm_index;
1385        ins->pcm_channels[pcm_index].mixer_scb = mixer_scb;
1386        ins->npcm_channels ++;
1387        spin_unlock_irqrestore(&chip->reg_lock, flags);
1388
1389        return (ins->pcm_channels + pcm_index);
1390}
1391
1392int cs46xx_dsp_pcm_channel_set_period (struct snd_cs46xx * chip,
1393                                       struct dsp_pcm_channel_descriptor * pcm_channel,
1394                                       int period_size)
1395{
1396        u32 temp = snd_cs46xx_peek (chip,pcm_channel->pcm_reader_scb->address << 2);
1397        temp &= ~DMA_RQ_C1_SOURCE_SIZE_MASK;
1398
1399        switch (period_size) {
1400        case 2048:
1401                temp |= DMA_RQ_C1_SOURCE_MOD1024;
1402                break;
1403        case 1024:
1404                temp |= DMA_RQ_C1_SOURCE_MOD512;
1405                break;
1406        case 512:
1407                temp |= DMA_RQ_C1_SOURCE_MOD256;
1408                break;
1409        case 256:
1410                temp |= DMA_RQ_C1_SOURCE_MOD128;
1411                break;
1412        case 128:
1413                temp |= DMA_RQ_C1_SOURCE_MOD64;
1414                break;
1415        case 64:
1416                temp |= DMA_RQ_C1_SOURCE_MOD32;
1417                break;                
1418        case 32:
1419                temp |= DMA_RQ_C1_SOURCE_MOD16;
1420                break; 
1421        default:
1422                snd_printdd ("period size (%d) not supported by HW\n", period_size);
1423                return -EINVAL;
1424        }
1425
1426        snd_cs46xx_poke (chip,pcm_channel->pcm_reader_scb->address << 2,temp);
1427
1428        return 0;
1429}
1430
1431int cs46xx_dsp_pcm_ostream_set_period (struct snd_cs46xx * chip,
1432                                       int period_size)
1433{
1434        u32 temp = snd_cs46xx_peek (chip,WRITEBACK_SCB_ADDR << 2);
1435        temp &= ~DMA_RQ_C1_DEST_SIZE_MASK;
1436
1437        switch (period_size) {
1438        case 2048:
1439                temp |= DMA_RQ_C1_DEST_MOD1024;
1440                break;
1441        case 1024:
1442                temp |= DMA_RQ_C1_DEST_MOD512;
1443                break;
1444        case 512:
1445                temp |= DMA_RQ_C1_DEST_MOD256;
1446                break;
1447        case 256:
1448                temp |= DMA_RQ_C1_DEST_MOD128;
1449                break;
1450        case 128:
1451                temp |= DMA_RQ_C1_DEST_MOD64;
1452                break;
1453        case 64:
1454                temp |= DMA_RQ_C1_DEST_MOD32;
1455                break;                
1456        case 32:
1457                temp |= DMA_RQ_C1_DEST_MOD16;
1458                break; 
1459        default:
1460                snd_printdd ("period size (%d) not supported by HW\n", period_size);
1461                return -EINVAL;
1462        }
1463
1464        snd_cs46xx_poke (chip,WRITEBACK_SCB_ADDR << 2,temp);
1465
1466        return 0;
1467}
1468
1469void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,
1470                                     struct dsp_pcm_channel_descriptor * pcm_channel)
1471{
1472        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
1473        unsigned long flags;
1474
1475        if (snd_BUG_ON(!pcm_channel->active ||
1476                       ins->npcm_channels <= 0 ||
1477                       pcm_channel->src_scb->ref_count <= 0))
1478                return;
1479
1480        spin_lock_irqsave(&chip->reg_lock, flags);
1481        pcm_channel->unlinked = 1;
1482        pcm_channel->active = 0;
1483        pcm_channel->private_data = NULL;
1484        pcm_channel->src_scb->ref_count --;
1485        ins->npcm_channels --;
1486        spin_unlock_irqrestore(&chip->reg_lock, flags);
1487
1488        cs46xx_dsp_remove_scb(chip,pcm_channel->pcm_reader_scb);
1489
1490        if (!pcm_channel->src_scb->ref_count) {
1491                cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb);
1492
1493                if (snd_BUG_ON(pcm_channel->src_slot < 0 ||
1494                               pcm_channel->src_slot >= DSP_MAX_SRC_NR))
1495                        return;
1496
1497                ins->src_scb_slots[pcm_channel->src_slot] = 0;
1498                ins->nsrc_scb --;
1499        }
1500}
1501
1502int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip,
1503                           struct dsp_pcm_channel_descriptor * pcm_channel)
1504{
1505        unsigned long flags;
1506
1507        if (snd_BUG_ON(!pcm_channel->active ||
1508                       chip->dsp_spos_instance->npcm_channels <= 0))
1509                return -EIO;
1510
1511        spin_lock(&pcm_channel->src_scb->lock);
1512
1513        if (pcm_channel->unlinked) {
1514                spin_unlock(&pcm_channel->src_scb->lock);
1515                return -EIO;
1516        }
1517
1518        spin_lock_irqsave(&chip->reg_lock, flags);
1519        pcm_channel->unlinked = 1;
1520        spin_unlock_irqrestore(&chip->reg_lock, flags);
1521
1522        _dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
1523
1524        spin_unlock(&pcm_channel->src_scb->lock);
1525        return 0;
1526}
1527
1528int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
1529                         struct dsp_pcm_channel_descriptor * pcm_channel)
1530{
1531        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
1532        struct dsp_scb_descriptor * parent_scb;
1533        struct dsp_scb_descriptor * src_scb = pcm_channel->src_scb;
1534        unsigned long flags;
1535
1536        spin_lock(&pcm_channel->src_scb->lock);
1537
1538        if (pcm_channel->unlinked == 0) {
1539                spin_unlock(&pcm_channel->src_scb->lock);
1540                return -EIO;
1541        }
1542
1543        parent_scb = src_scb;
1544
1545        if (src_scb->sub_list_ptr != ins->the_null_scb) {
1546                src_scb->sub_list_ptr->parent_scb_ptr = pcm_channel->pcm_reader_scb;
1547                pcm_channel->pcm_reader_scb->next_scb_ptr = src_scb->sub_list_ptr;
1548        }
1549
1550        src_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
1551
1552        snd_BUG_ON(pcm_channel->pcm_reader_scb->parent_scb_ptr);
1553        pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
1554
1555        spin_lock_irqsave(&chip->reg_lock, flags);
1556
1557        /* update SCB entry in DSP RAM */
1558        cs46xx_dsp_spos_update_scb(chip,pcm_channel->pcm_reader_scb);
1559
1560        /* update parent SCB entry */
1561        cs46xx_dsp_spos_update_scb(chip,parent_scb);
1562
1563        pcm_channel->unlinked = 0;
1564        spin_unlock_irqrestore(&chip->reg_lock, flags);
1565
1566        spin_unlock(&pcm_channel->src_scb->lock);
1567        return 0;
1568}
1569
1570struct dsp_scb_descriptor *
1571cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * source,
1572                          u16 addr, char * scb_name)
1573{
1574        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
1575        struct dsp_scb_descriptor * parent;
1576        struct dsp_scb_descriptor * pcm_input;
1577        int insert_point;
1578
1579        if (snd_BUG_ON(!ins->record_mixer_scb))
1580                return NULL;
1581
1582        if (ins->record_mixer_scb->sub_list_ptr != ins->the_null_scb) {
1583                parent = find_next_free_scb (chip,ins->record_mixer_scb->sub_list_ptr);
1584                insert_point = SCB_ON_PARENT_NEXT_SCB;
1585        } else {
1586                parent = ins->record_mixer_scb;
1587                insert_point = SCB_ON_PARENT_SUBLIST_SCB;
1588        }
1589
1590        pcm_input = cs46xx_dsp_create_pcm_serial_input_scb(chip,scb_name,addr,
1591                                                           source, parent,
1592                                                           insert_point);
1593
1594        return pcm_input;
1595}
1596
1597int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
1598{
1599        if (snd_BUG_ON(!src->parent_scb_ptr))
1600                return -EINVAL;
1601
1602        /* mute SCB */
1603        cs46xx_dsp_scb_set_volume (chip,src,0,0);
1604
1605        _dsp_unlink_scb (chip,src);
1606
1607        return 0;
1608}
1609
1610int cs46xx_src_link(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
1611{
1612        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
1613        struct dsp_scb_descriptor * parent_scb;
1614
1615        if (snd_BUG_ON(src->parent_scb_ptr))
1616                return -EINVAL;
1617        if (snd_BUG_ON(!ins->master_mix_scb))
1618                return -EINVAL;
1619
1620        if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) {
1621                parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr);
1622                parent_scb->next_scb_ptr = src;
1623        } else {
1624                parent_scb = ins->master_mix_scb;
1625                parent_scb->sub_list_ptr = src;
1626        }
1627
1628        src->parent_scb_ptr = parent_scb;
1629
1630        /* update entry in DSP RAM */
1631        cs46xx_dsp_spos_update_scb(chip,parent_scb);
1632  
1633        return 0;
1634}
1635
1636int cs46xx_dsp_enable_spdif_out (struct snd_cs46xx *chip)
1637{
1638        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
1639
1640        if ( ! (ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) ) {
1641                cs46xx_dsp_enable_spdif_hw (chip);
1642        }
1643
1644        /* dont touch anything if SPDIF is open */
1645        if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) {
1646                /* when cs46xx_iec958_post_close(...) is called it
1647                   will call this function if necessary depending on
1648                   this bit */
1649                ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
1650
1651                return -EBUSY;
1652        }
1653
1654        if (snd_BUG_ON(ins->asynch_tx_scb))
1655                return -EINVAL;
1656        if (snd_BUG_ON(ins->master_mix_scb->next_scb_ptr !=
1657                       ins->the_null_scb))
1658                return -EINVAL;
1659
1660        /* reset output snooper sample buffer pointer */
1661        snd_cs46xx_poke (chip, (ins->ref_snoop_scb->address + 2) << 2,
1662                         (OUTPUT_SNOOP_BUFFER + 0x10) << 0x10 );
1663        
1664        /* The asynch. transfer task */
1665        ins->asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
1666                                                                SPDIFO_SCB_INST,
1667                                                                SPDIFO_IP_OUTPUT_BUFFER1,
1668                                                                ins->master_mix_scb,
1669                                                                SCB_ON_PARENT_NEXT_SCB);
1670        if (!ins->asynch_tx_scb) return -ENOMEM;
1671
1672        ins->spdif_pcm_input_scb = cs46xx_dsp_create_pcm_serial_input_scb(chip,"PCMSerialInput_II",
1673                                                                          PCMSERIALINII_SCB_ADDR,
1674                                                                          ins->ref_snoop_scb,
1675                                                                          ins->asynch_tx_scb,
1676                                                                          SCB_ON_PARENT_SUBLIST_SCB);
1677  
1678        
1679        if (!ins->spdif_pcm_input_scb) return -ENOMEM;
1680
1681        /* monitor state */
1682        ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
1683
1684        return 0;
1685}
1686
1687int  cs46xx_dsp_disable_spdif_out (struct snd_cs46xx *chip)
1688{
1689        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
1690
1691        /* dont touch anything if SPDIF is open */
1692        if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) {
1693                ins->spdif_status_out &= ~DSP_SPDIF_STATUS_OUTPUT_ENABLED;
1694                return -EBUSY;
1695        }
1696
1697        /* check integrety */
1698        if (snd_BUG_ON(!ins->asynch_tx_scb))
1699                return -EINVAL;
1700        if (snd_BUG_ON(!ins->spdif_pcm_input_scb))
1701                return -EINVAL;
1702        if (snd_BUG_ON(ins->master_mix_scb->next_scb_ptr != ins->asynch_tx_scb))
1703                return -EINVAL;
1704        if (snd_BUG_ON(ins->asynch_tx_scb->parent_scb_ptr !=
1705                       ins->master_mix_scb))
1706                return -EINVAL;
1707
1708        cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb);
1709        cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb);
1710
1711        ins->spdif_pcm_input_scb = NULL;
1712        ins->asynch_tx_scb = NULL;
1713
1714        /* clear buffer to prevent any undesired noise */
1715        _dsp_clear_sample_buffer(chip,SPDIFO_IP_OUTPUT_BUFFER1,256);
1716
1717        /* monitor state */
1718        ins->spdif_status_out  &= ~DSP_SPDIF_STATUS_OUTPUT_ENABLED;
1719
1720
1721        return 0;
1722}
1723
1724int cs46xx_iec958_pre_open (struct snd_cs46xx *chip)
1725{
1726        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
1727
1728        if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) {
1729                /* remove AsynchFGTxSCB and and PCMSerialInput_II */
1730                cs46xx_dsp_disable_spdif_out (chip);
1731
1732                /* save state */
1733                ins->spdif_status_out |= DSP_SPDIF_STATUS_OUTPUT_ENABLED;
1734        }
1735        
1736        /* if not enabled already */
1737        if ( !(ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) ) {
1738                cs46xx_dsp_enable_spdif_hw (chip);
1739        }
1740
1741        /* Create the asynch. transfer task  for playback */
1742        ins->asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
1743                                                                SPDIFO_SCB_INST,
1744                                                                SPDIFO_IP_OUTPUT_BUFFER1,
1745                                                                ins->master_mix_scb,
1746                                                                SCB_ON_PARENT_NEXT_SCB);
1747
1748
1749        /* set spdif channel status value for streaming */
1750        cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_stream);
1751
1752        ins->spdif_status_out  |= DSP_SPDIF_STATUS_PLAYBACK_OPEN;
1753
1754        return 0;
1755}
1756
1757int cs46xx_iec958_post_close (struct snd_cs46xx *chip)
1758{
1759        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
1760
1761        if (snd_BUG_ON(!ins->asynch_tx_scb))
1762                return -EINVAL;
1763
1764        ins->spdif_status_out  &= ~DSP_SPDIF_STATUS_PLAYBACK_OPEN;
1765
1766        /* restore settings */
1767        cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV, ins->spdif_csuv_default);
1768        
1769        /* deallocate stuff */
1770        if (ins->spdif_pcm_input_scb != NULL) {
1771                cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb);
1772                ins->spdif_pcm_input_scb = NULL;
1773        }
1774
1775        cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb);
1776        ins->asynch_tx_scb = NULL;
1777
1778        /* clear buffer to prevent any undesired noise */
1779        _dsp_clear_sample_buffer(chip,SPDIFO_IP_OUTPUT_BUFFER1,256);
1780
1781        /* restore state */
1782        if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) {
1783                cs46xx_dsp_enable_spdif_out (chip);
1784        }
1785        
1786        return 0;
1787}
1788