📄 dsp_spos.c
字号:
/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *//* * 2002-07 Benny Sjostrand benny@hostmobility.com */#include <sound/driver.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/pci.h>#include <linux/pm.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <sound/core.h>#include <sound/control.h>#include <sound/info.h>#include <sound/asoundef.h>#include <sound/cs46xx.h>#include "cs46xx_lib.h"#include "dsp_spos.h"static int cs46xx_dsp_async_init (cs46xx_t *chip, dsp_scb_descriptor_t * fg_entry);static wide_opcode_t wide_opcodes[] = { WIDE_FOR_BEGIN_LOOP, WIDE_FOR_BEGIN_LOOP2, WIDE_COND_GOTO_ADDR, WIDE_COND_GOTO_CALL, WIDE_TBEQ_COND_GOTO_ADDR, WIDE_TBEQ_COND_CALL_ADDR, WIDE_TBEQ_NCOND_GOTO_ADDR, WIDE_TBEQ_NCOND_CALL_ADDR, WIDE_TBEQ_COND_GOTO1_ADDR, WIDE_TBEQ_COND_CALL1_ADDR, WIDE_TBEQ_NCOND_GOTOI_ADDR, WIDE_TBEQ_NCOND_CALL1_ADDR};static int shadow_and_reallocate_code (cs46xx_t * chip,u32 * data,u32 size, u32 overlay_begin_address){ unsigned int i = 0, j, nreallocated = 0; u32 hival,loval,address; u32 mop_operands,mop_type,wide_op; dsp_spos_instance_t * ins = chip->dsp_spos_instance; snd_assert( ((size % 2) == 0), return -EINVAL); while (i < size) { loval = data[i++]; hival = data[i++]; if (ins->code.offset > 0) { mop_operands = (hival >> 6) & 0x03fff; mop_type = mop_operands >> 10; /* check for wide type instruction */ if (mop_type == 0 && (mop_operands & WIDE_LADD_INSTR_MASK) == 0 && (mop_operands & WIDE_INSTR_MASK) != 0) { wide_op = loval & 0x7f; for (j = 0;j < ARRAY_SIZE(wide_opcodes); ++j) { if (wide_opcodes[j] == wide_op) { /* need to reallocate instruction */ address = (hival & 0x00FFF) << 5; address |= loval >> 15; snd_printdd("handle_wideop[1]: %05x:%05x addr %04x\n",hival,loval,address); if ( !(address & 0x8000) ) { address += (ins->code.offset / 2) - overlay_begin_address; } else { snd_printdd("handle_wideop[1]: ROM symbol not reallocated\n"); } hival &= 0xFF000; loval &= 0x07FFF; hival |= ( (address >> 5) & 0x00FFF); loval |= ( (address << 15) & 0xF8000); address = (hival & 0x00FFF) << 5; address |= loval >> 15; snd_printdd("handle_wideop:[2] %05x:%05x addr %04x\n",hival,loval,address); nreallocated ++; } /* wide_opcodes[j] == wide_op */ } /* for */ } /* mod_type == 0 ... */ } /* ins->code.offset > 0 */ ins->code.data[ins->code.size++] = loval; ins->code.data[ins->code.size++] = hival; } snd_printdd("dsp_spos: %d instructions reallocated\n",nreallocated); return nreallocated;}static segment_desc_t * get_segment_desc (dsp_module_desc_t * module, int seg_type){ int i; for (i = 0;i < module->nsegments; ++i) { if (module->segments[i].segment_type == seg_type) { return (module->segments + i); } } return NULL;};static int find_free_symbol_index (dsp_spos_instance_t * ins){ int index = ins->symbol_table.nsymbols,i; for (i = ins->symbol_table.highest_frag_index; i < ins->symbol_table.nsymbols; ++i) { if (ins->symbol_table.symbols[i].deleted) { index = i; break; } } return index;}static int add_symbols (cs46xx_t * chip, dsp_module_desc_t * module){ int i; dsp_spos_instance_t * ins = chip->dsp_spos_instance; if (module->symbol_table.nsymbols > 0) { if (!strcmp(module->symbol_table.symbols[0].symbol_name, "OVERLAYBEGINADDRESS") && module->symbol_table.symbols[0].symbol_type == SYMBOL_CONSTANT ) { module->overlay_begin_address = module->symbol_table.symbols[0].address; } } for (i = 0;i < module->symbol_table.nsymbols; ++i) { if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) { snd_printk(KERN_ERR "dsp_spos: symbol table is full\n"); return -ENOMEM; } if (cs46xx_dsp_lookup_symbol(chip, module->symbol_table.symbols[i].symbol_name, module->symbol_table.symbols[i].symbol_type) == NULL) { ins->symbol_table.symbols[ins->symbol_table.nsymbols] = module->symbol_table.symbols[i]; ins->symbol_table.symbols[ins->symbol_table.nsymbols].address += ((ins->code.offset / 2) - module->overlay_begin_address); ins->symbol_table.symbols[ins->symbol_table.nsymbols].module = module; ins->symbol_table.symbols[ins->symbol_table.nsymbols].deleted = 0; if (ins->symbol_table.nsymbols > ins->symbol_table.highest_frag_index) ins->symbol_table.highest_frag_index = ins->symbol_table.nsymbols; ins->symbol_table.nsymbols++; } else { /* if (0) printk ("dsp_spos: symbol <%s> duplicated, probably nothing wrong with that (Cirrus?)\n", module->symbol_table.symbols[i].symbol_name); */ } } return 0;}static symbol_entry_t * add_symbol (cs46xx_t * chip, char * symbol_name, u32 address, int type){ dsp_spos_instance_t * ins = chip->dsp_spos_instance; symbol_entry_t * symbol = NULL; int index; if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) { snd_printk(KERN_ERR "dsp_spos: symbol table is full\n"); return NULL; } if (cs46xx_dsp_lookup_symbol(chip, symbol_name, type) != NULL) { snd_printk(KERN_ERR "dsp_spos: symbol <%s> duplicated\n", symbol_name); return NULL; } index = find_free_symbol_index (ins); strcpy (ins->symbol_table.symbols[index].symbol_name, symbol_name); ins->symbol_table.symbols[index].address = address; ins->symbol_table.symbols[index].symbol_type = type; ins->symbol_table.symbols[index].module = NULL; ins->symbol_table.symbols[index].deleted = 0; symbol = (ins->symbol_table.symbols + index); if (index > ins->symbol_table.highest_frag_index) ins->symbol_table.highest_frag_index = index; if (index == ins->symbol_table.nsymbols) ins->symbol_table.nsymbols++; /* no frag. in list */ return symbol;}dsp_spos_instance_t * cs46xx_dsp_spos_create (cs46xx_t * chip){ dsp_spos_instance_t * ins = kmalloc(sizeof(dsp_spos_instance_t), GFP_KERNEL); if (ins == NULL) return NULL; memset(ins, 0, sizeof(*ins)); /* better to use vmalloc for this big table */ ins->symbol_table.nsymbols = 0; ins->symbol_table.symbols = vmalloc(sizeof(symbol_entry_t) * DSP_MAX_SYMBOLS); ins->symbol_table.highest_frag_index = 0; if (ins->symbol_table.symbols == NULL) { cs46xx_dsp_spos_destroy(chip); return NULL; } ins->code.offset = 0; ins->code.size = 0; ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL); if (ins->code.data == NULL) { cs46xx_dsp_spos_destroy(chip); return NULL; } ins->nscb = 0; ins->ntask = 0; ins->nmodules = 0; ins->modules = kmalloc(sizeof(dsp_module_desc_t) * DSP_MAX_MODULES, GFP_KERNEL); if (ins->modules == NULL) { cs46xx_dsp_spos_destroy(chip); return NULL; } /* default SPDIF input sample rate to 48000 khz */ ins->spdif_in_sample_rate = 48000; /* maximize volume */ ins->dac_volume_right = 0x8000; ins->dac_volume_left = 0x8000; ins->spdif_input_volume_right = 0x8000; ins->spdif_input_volume_left = 0x8000; /* set left and right validity bits and default channel status */ ins->spdif_csuv_default = ins->spdif_csuv_stream = /* byte 0 */ ((unsigned int)_wrap_all_bits( (SNDRV_PCM_DEFAULT_CON_SPDIF & 0xff)) << 24) | /* byte 1 */ ((unsigned int)_wrap_all_bits( ((SNDRV_PCM_DEFAULT_CON_SPDIF >> 8) & 0xff)) << 16) | /* byte 3 */ (unsigned int)_wrap_all_bits( (SNDRV_PCM_DEFAULT_CON_SPDIF >> 24) & 0xff) | /* left and right validity bits */ (1 << 13) | (1 << 12); return ins;}void cs46xx_dsp_spos_destroy (cs46xx_t * chip){ int i; dsp_spos_instance_t * ins = chip->dsp_spos_instance; snd_assert(ins != NULL, return); down(&chip->spos_mutex); for (i = 0; i < ins->nscb; ++i) { if (ins->scbs[i].deleted) continue; cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) ); } kfree(ins->code.data); vfree(ins->symbol_table.symbols); kfree(ins->modules); kfree(ins); up(&chip->spos_mutex);}int cs46xx_dsp_load_module (cs46xx_t * chip, dsp_module_desc_t * module){ dsp_spos_instance_t * ins = chip->dsp_spos_instance; segment_desc_t * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM); segment_desc_t * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER); segment_desc_t * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE); u32 doffset, dsize; if (ins->nmodules == DSP_MAX_MODULES - 1) { snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n"); return -ENOMEM; } snd_printdd("dsp_spos: loading module %s into DSP\n", module->module_name); if (ins->nmodules == 0) { snd_printdd("dsp_spos: clearing parameter area\n"); snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE); } if (parameter == NULL) { snd_printdd("dsp_spos: module got no parameter segment\n"); } else { if (ins->nmodules > 0) { snd_printk(KERN_WARNING "dsp_spos: WARNING current parameter data may be overwriten!\n"); } doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET); dsize = parameter->size * 4; snd_printdd("dsp_spos: downloading parameter data to chip (%08x-%08x)\n", doffset,doffset + dsize); if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) { snd_printk(KERN_ERR "dsp_spos: failed to download parameter data to DSP\n"); return -EINVAL; } } if (ins->nmodules == 0) { snd_printdd("dsp_spos: clearing sample area\n"); snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE); } if (sample == NULL) { snd_printdd("dsp_spos: module got no sample segment\n"); } else { if (ins->nmodules > 0) { snd_printk(KERN_WARNING "dsp_spos: WARNING current sample data may be overwriten\n"); } doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET); dsize = sample->size * 4; snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n", doffset,doffset + dsize); if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) { snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n"); return -EINVAL; } } if (ins->nmodules == 0) { snd_printdd("dsp_spos: clearing code area\n"); snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE); } if (code == NULL) { snd_printdd("dsp_spos: module got no code segment\n"); } else { if (ins->code.offset + code->size > DSP_CODE_BYTE_SIZE) { snd_printk(KERN_ERR "dsp_spos: no space available in DSP\n"); return -ENOMEM; } module->load_address = ins->code.offset; module->overlay_begin_address = 0x000; /* if module has a code segment it must have symbol table */ snd_assert(module->symbol_table.symbols != NULL ,return -ENOMEM); if (add_symbols(chip,module)) { snd_printk(KERN_ERR "dsp_spos: failed to load symbol table\n"); return -ENOMEM; } doffset = (code->offset * 4 + ins->code.offset * 4 + DSP_CODE_BYTE_OFFSET); dsize = code->size * 4; snd_printdd("dsp_spos: downloading code to chip (%08x-%08x)\n", doffset,doffset + dsize); module->nfixups = shadow_and_reallocate_code(chip,code->data,code->size,module->overlay_begin_address); if (snd_cs46xx_download (chip,(ins->code.data + ins->code.offset),doffset,dsize)) { snd_printk(KERN_ERR "dsp_spos: failed to download code to DSP\n"); return -EINVAL; } ins->code.offset += code->size; } /* NOTE: module segments and symbol table must be statically allocated. Case that module data is not generated by the ospparser */ ins->modules[ins->nmodules] = *module; ins->nmodules++; return 0;}symbol_entry_t * cs46xx_dsp_lookup_symbol (cs46xx_t * chip, char * symbol_name, int symbol_type){ int i; dsp_spos_instance_t * ins = chip->dsp_spos_instance; for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) { if (ins->symbol_table.symbols[i].deleted) continue; if (!strcmp(ins->symbol_table.symbols[i].symbol_name,symbol_name) && ins->symbol_table.symbols[i].symbol_type == symbol_type) { return (ins->symbol_table.symbols + i); } }#if 0 printk ("dsp_spos: symbol <%s> type %02x not found\n", symbol_name,symbol_type);#endif return NULL;}static symbol_entry_t * cs46xx_dsp_lookup_symbol_addr (cs46xx_t * chip, u32 address, int symbol_type){ int i; dsp_spos_instance_t * ins = chip->dsp_spos_instance; for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) { if (ins->symbol_table.symbols[i].deleted) continue; if (ins->symbol_table.symbols[i].address == address && ins->symbol_table.symbols[i].symbol_type == symbol_type) { return (ins->symbol_table.symbols + i); } } return NULL;}static void cs46xx_dsp_proc_symbol_table_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer){ cs46xx_t *chip = entry->private_data; dsp_spos_instance_t * ins = chip->dsp_spos_instance; int i; snd_iprintf(buffer, "SYMBOLS:\n"); for ( i = 0; i < ins->symbol_table.nsymbols; ++i ) { char *module_str = "system"; if (ins->symbol_table.symbols[i].deleted) continue; if (ins->symbol_table.symbols[i].module != NULL) { module_str = ins->symbol_table.symbols[i].module->module_name; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -