📄 echoaudio_dsp.c
字号:
chip->comm_page->monitors[monitor_index(chip, output, input)] = gain; return 0;}#endif /* ECHOCARD_HAS_MONITOR *//* Tell the DSP to read and update output, nominal & monitor levels in comm page. */static int update_output_line_level(struct echoaudio *chip){ if (wait_handshake(chip)) return -EIO; clear_handshake(chip); return send_vector(chip, DSP_VC_UPDATE_OUTVOL);}/* Tell the DSP to read and update input levels in comm page */static int update_input_line_level(struct echoaudio *chip){ if (wait_handshake(chip)) return -EIO; clear_handshake(chip); return send_vector(chip, DSP_VC_UPDATE_INGAIN);}/* set_meters_on turns the meters on or off. If meters are turned on, the DSPwill write the meter and clock detect values to the comm page at about 30Hz */static void set_meters_on(struct echoaudio *chip, char on){ if (on && !chip->meters_enabled) { send_vector(chip, DSP_VC_METERS_ON); chip->meters_enabled = 1; } else if (!on && chip->meters_enabled) { send_vector(chip, DSP_VC_METERS_OFF); chip->meters_enabled = 0; memset((s8 *)chip->comm_page->vu_meter, ECHOGAIN_MUTED, DSP_MAXPIPES); memset((s8 *)chip->comm_page->peak_meter, ECHOGAIN_MUTED, DSP_MAXPIPES); }}/* Fill out an the given array using the current values in the comm page.Meters are written in the comm page by the DSP in this order: Output busses Input busses Output pipes (vmixer cards only)This function assumes there are no more than 16 in/out busses or pipesMeters is an array [3][16][2] of long. */static void get_audio_meters(struct echoaudio *chip, long *meters){ int i, m, n; m = 0; n = 0; for (i = 0; i < num_busses_out(chip); i++, m++) { meters[n++] = chip->comm_page->vu_meter[m]; meters[n++] = chip->comm_page->peak_meter[m]; } for (; n < 32; n++) meters[n] = 0;#ifdef ECHOCARD_ECHO3G m = E3G_MAX_OUTPUTS; /* Skip unused meters */#endif for (i = 0; i < num_busses_in(chip); i++, m++) { meters[n++] = chip->comm_page->vu_meter[m]; meters[n++] = chip->comm_page->peak_meter[m]; } for (; n < 64; n++) meters[n] = 0;#ifdef ECHOCARD_HAS_VMIXER for (i = 0; i < num_pipes_out(chip); i++, m++) { meters[n++] = chip->comm_page->vu_meter[m]; meters[n++] = chip->comm_page->peak_meter[m]; }#endif for (; n < 96; n++) meters[n] = 0;}static int restore_dsp_rettings(struct echoaudio *chip){ int err; DE_INIT(("restore_dsp_settings\n")); if ((err = check_asic_status(chip)) < 0) return err; /* @ Gina20/Darla20 only. Should be harmless for other cards. */ chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF; chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF; chip->comm_page->handshake = 0xffffffff; if ((err = set_sample_rate(chip, chip->sample_rate)) < 0) return err; if (chip->meters_enabled) if (send_vector(chip, DSP_VC_METERS_ON) < 0) return -EIO;#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK if (set_input_clock(chip, chip->input_clock) < 0) return -EIO;#endif#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH if (set_output_clock(chip, chip->output_clock) < 0) return -EIO;#endif if (update_output_line_level(chip) < 0) return -EIO; if (update_input_line_level(chip) < 0) return -EIO;#ifdef ECHOCARD_HAS_VMIXER if (update_vmixer_level(chip) < 0) return -EIO;#endif if (wait_handshake(chip) < 0) return -EIO; clear_handshake(chip); DE_INIT(("restore_dsp_rettings done\n")); return send_vector(chip, DSP_VC_UPDATE_FLAGS);}/**************************************************************************** Transport functions ****************************************************************************//* set_audio_format() sets the format of the audio data in host memory forthis pipe. Note that _MS_ (mono-to-stereo) playback modes are not used by ALSAbut they are here because they are just mono while capturing */static void set_audio_format(struct echoaudio *chip, u16 pipe_index, const struct audioformat *format){ u16 dsp_format; dsp_format = DSP_AUDIOFORM_SS_16LE; /* Look for super-interleave (no big-endian and 8 bits) */ if (format->interleave > 2) { switch (format->bits_per_sample) { case 16: dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE; break; case 24: dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE; break; case 32: dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE; break; } dsp_format |= format->interleave; } else if (format->data_are_bigendian) { /* For big-endian data, only 32 bit samples are supported */ switch (format->interleave) { case 1: dsp_format = DSP_AUDIOFORM_MM_32BE; break;#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 case 2: dsp_format = DSP_AUDIOFORM_SS_32BE; break;#endif } } else if (format->interleave == 1 && format->bits_per_sample == 32 && !format->mono_to_stereo) { /* 32 bit little-endian mono->mono case */ dsp_format = DSP_AUDIOFORM_MM_32LE; } else { /* Handle the other little-endian formats */ switch (format->bits_per_sample) { case 8: if (format->interleave == 2) dsp_format = DSP_AUDIOFORM_SS_8; else dsp_format = DSP_AUDIOFORM_MS_8; break; default: case 16: if (format->interleave == 2) dsp_format = DSP_AUDIOFORM_SS_16LE; else dsp_format = DSP_AUDIOFORM_MS_16LE; break; case 24: if (format->interleave == 2) dsp_format = DSP_AUDIOFORM_SS_24LE; else dsp_format = DSP_AUDIOFORM_MS_24LE; break; case 32: if (format->interleave == 2) dsp_format = DSP_AUDIOFORM_SS_32LE; else dsp_format = DSP_AUDIOFORM_MS_32LE; break; } } DE_ACT(("set_audio_format[%d] = %x\n", pipe_index, dsp_format)); chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format);}/* start_transport starts transport for a set of pipes.The bits 1 in channel_mask specify what pipes to start. Only the bit of thefirst channel must be set, regardless its interleave.Same thing for pause_ and stop_ -trasport below. */static int start_transport(struct echoaudio *chip, u32 channel_mask, u32 cyclic_mask){ DE_ACT(("start_transport %x\n", channel_mask)); if (wait_handshake(chip)) return -EIO; chip->comm_page->cmd_start |= cpu_to_le32(channel_mask); if (chip->comm_page->cmd_start) { clear_handshake(chip); send_vector(chip, DSP_VC_START_TRANSFER); if (wait_handshake(chip)) return -EIO; /* Keep track of which pipes are transporting */ chip->active_mask |= channel_mask; chip->comm_page->cmd_start = 0; return 0; } DE_ACT(("start_transport: No pipes to start!\n")); return -EINVAL;}static int pause_transport(struct echoaudio *chip, u32 channel_mask){ DE_ACT(("pause_transport %x\n", channel_mask)); if (wait_handshake(chip)) return -EIO; chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask); chip->comm_page->cmd_reset = 0; if (chip->comm_page->cmd_stop) { clear_handshake(chip); send_vector(chip, DSP_VC_STOP_TRANSFER); if (wait_handshake(chip)) return -EIO; /* Keep track of which pipes are transporting */ chip->active_mask &= ~channel_mask; chip->comm_page->cmd_stop = 0; chip->comm_page->cmd_reset = 0; return 0; } DE_ACT(("pause_transport: No pipes to stop!\n")); return 0;}static int stop_transport(struct echoaudio *chip, u32 channel_mask){ DE_ACT(("stop_transport %x\n", channel_mask)); if (wait_handshake(chip)) return -EIO; chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask); chip->comm_page->cmd_reset |= cpu_to_le32(channel_mask); if (chip->comm_page->cmd_reset) { clear_handshake(chip); send_vector(chip, DSP_VC_STOP_TRANSFER); if (wait_handshake(chip)) return -EIO; /* Keep track of which pipes are transporting */ chip->active_mask &= ~channel_mask; chip->comm_page->cmd_stop = 0; chip->comm_page->cmd_reset = 0; return 0; } DE_ACT(("stop_transport: No pipes to stop!\n")); return 0;}static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index){ return (chip->pipe_alloc_mask & (1 << pipe_index));}/* Stops everything and turns off the DSP. All pipes should be alreadystopped and unallocated. */static int rest_in_peace(struct echoaudio *chip){ DE_ACT(("rest_in_peace() open=%x\n", chip->pipe_alloc_mask)); /* Stops all active pipes (just to be sure) */ stop_transport(chip, chip->active_mask); set_meters_on(chip, FALSE);#ifdef ECHOCARD_HAS_MIDI enable_midi_input(chip, FALSE);#endif /* Go to sleep */ if (chip->dsp_code) { /* Make load_firmware do a complete reload */ chip->dsp_code = NULL; /* Put the DSP to sleep */ return send_vector(chip, DSP_VC_GO_COMATOSE); } return 0;}/* Fills the comm page with default values */static int init_dsp_comm_page(struct echoaudio *chip){ /* Check if the compiler added extra padding inside the structure */ if (offsetof(struct comm_page, midi_output) != 0xbe0) { DE_INIT(("init_dsp_comm_page() - Invalid struct comm_page structure\n")); return -EPERM; } /* Init all the basic stuff */ chip->card_name = ECHOCARD_NAME; chip->bad_board = TRUE; /* Set TRUE until DSP loaded */ chip->dsp_code = NULL; /* Current DSP code not loaded */ chip->digital_mode = DIGITAL_MODE_NONE; chip->input_clock = ECHO_CLOCK_INTERNAL; chip->output_clock = ECHO_CLOCK_WORD; chip->asic_loaded = FALSE; memset(chip->comm_page, 0, sizeof(struct comm_page)); /* Init the comm page */ chip->comm_page->comm_size = __constant_cpu_to_le32(sizeof(struct comm_page)); chip->comm_page->handshake = 0xffffffff; chip->comm_page->midi_out_free_count = __constant_cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE); chip->comm_page->sample_rate = __constant_cpu_to_le32(44100); chip->sample_rate = 44100; /* Set line levels so we don't blast any inputs on startup */ memset(chip->comm_page->monitors, ECHOGAIN_MUTED, MONITOR_ARRAY_SIZE); memset(chip->comm_page->vmixer, ECHOGAIN_MUTED, VMIXER_ARRAY_SIZE); return 0;}/* This function initializes the several volume controls for busses and pipes.This MUST be called after the DSP is up and running ! */static int init_line_levels(struct echoaudio *chip){ int st, i, o; DE_INIT(("init_line_levels\n")); /* Mute output busses */ for (i = 0; i < num_busses_out(chip); i++) if ((st = set_output_gain(chip, i, ECHOGAIN_MUTED))) return st; if ((st = update_output_line_level(chip))) return st;#ifdef ECHOCARD_HAS_VMIXER /* Mute the Vmixer */ for (i = 0; i < num_pipes_out(chip); i++) for (o = 0; o < num_busses_out(chip); o++) if ((st = set_vmixer_gain(chip, o, i, ECHOGAIN_MUTED))) return st; if ((st = update_vmixer_level(chip))) return st;#endif /* ECHOCARD_HAS_VMIXER */#ifdef ECHOCARD_HAS_MONITOR /* Mute the monitor mixer */ for (o = 0; o < num_busses_out(chip); o++) for (i = 0; i < num_busses_in(chip); i++) if ((st = set_monitor_gain(chip, o, i, ECHOGAIN_MUTED))) return st; if ((st = update_output_line_level(chip))) return st;#endif /* ECHOCARD_HAS_MONITOR */#ifdef ECHOCARD_HAS_INPUT_GAIN for (i = 0; i < num_busses_in(chip); i++) if ((st = set_input_gain(chip, i, ECHOGAIN_MUTED))) return st; if ((st = update_input_line_level(chip))) return st;#endif /* ECHOCARD_HAS_INPUT_GAIN */ return 0;}/* This is low level part of the interrupt handler.It returns -1 if the IRQ is not ours, or N>=0 if it is, where N is the numberof midi data in the input queue. */static int service_irq(struct echoaudio *chip){ int st; /* Read the DSP status register and see if this DSP generated this interrupt */ if (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_IRQ) { st = 0;#ifdef ECHOCARD_HAS_MIDI /* Get and parse midi data if present */ if (chip->comm_page->midi_input[0]) /* The count is at index 0 */ st = midi_service_irq(chip); /* Returns how many midi bytes we received */#endif /* Clear the hardware interrupt */ chip->comm_page->midi_input[0] = 0; send_vector(chip, DSP_VC_ACK_INT); return st; } return -1;}/****************************************************************************** Functions for opening and closing pipes ******************************************************************************//* allocate_pipes is used to reserve audio pipes for your exclusive use.The call will fail if some pipes are already allocated. */static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, int pipe_index, int interleave){ int i; u32 channel_mask; char is_cyclic; DE_ACT(("allocate_pipes: ch=%d int=%d\n", pipe_index, interleave)); if (chip->bad_board) return -EIO; is_cyclic = 1; /* This driver uses cyclic buffers only */ for (channel_mask = i = 0; i < interleave; i++) channel_mask |= 1 << (pipe_index + i); if (chip->pipe_alloc_mask & channel_mask) { DE_ACT(("allocate_pipes: channel already open\n")); return -EAGAIN; } chip->comm_page->position[pipe_index] = 0; chip->pipe_alloc_mask |= channel_mask; if (is_cyclic) chip->pipe_cyclic_mask |= channel_mask; pipe->index = pipe_index; pipe->interleave = interleave; pipe->state = PIPE_STATE_STOPPED; /* The counter register is where the DSP writes the 32 bit DMA position for a pipe. The DSP is constantly updating this value as it moves data. The DMA counter is in units of bytes, not samples. */ pipe->dma_counter = &chip->comm_page->position[pipe_index]; *pipe->dma_counter = 0; DE_ACT(("allocate_pipes: ok\n")); return pipe_index;}static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe){ u32 channel_mask; int i; DE_ACT(("free_pipes: Pipe %d\n", pipe->index)); snd_assert(is_pipe_allocated(chip, pipe->index), return -EINVAL); snd_assert(pipe->state == PIPE_STATE_STOPPED, return -EINVAL); for (channel_mask = i = 0; i < pipe->interleave; i++) channel_mask |= 1 << (pipe->index + i); chip->pipe_alloc_mask &= ~channel_mask; chip->pipe_cyclic_mask &= ~channel_mask; return 0;}/****************************************************************************** Functions for managing the scatter-gather list******************************************************************************/static int sglist_init(struct echoaudio *chip, struct audiopipe *pipe){ pipe->sglist_head = 0; memset(pipe->sgpage.area, 0, PAGE_SIZE); chip->comm_page->sglist_addr[pipe->index].addr = cpu_to_le32(pipe->sgpage.addr); return 0;}static int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe, dma_addr_t address, size_t length){ int head = pipe->sglist_head; struct sg_entry *list = (struct sg_entry *)pipe->sgpage.area; if (head < MAX_SGLIST_ENTRIES - 1) { list[head].addr = cpu_to_le32(address); list[head].size = cpu_to_le32(length); pipe->sglist_head++; } else { DE_ACT(("SGlist: too many fragments\n")); return -ENOMEM; } return 0;}static inline int sglist_add_irq(struct echoaudio *chip, struct audiopipe *pipe){ return sglist_add_mapping(chip, pipe, 0, 0);}static inline int sglist_wrap(struct echoaudio *chip, struct audiopipe *pipe){ return sglist_add_mapping(chip, pipe, pipe->sgpage.addr, 0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -