/* * Copyright 2007-2012, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Ithamar Adema, ithamar AT unet DOT nl * Axel Dörfler, axeld@pinc-software.de */ #ifndef _HDA_H_ #define _HDA_H_ #include #include #include #include #include #define DEVFS_PATH_FORMAT "audio/hmulti/hda/%" B_PRIu32 #include #include "hda_controller_defs.h" #include "hda_codec_defs.h" #define MAX_CARDS 4 #define HDA_MAX_AUDIO_GROUPS 15 #define HDA_MAX_CODECS 15 #define HDA_MAX_STREAMS 16 #define MAX_CODEC_RESPONSES 16 #define MAX_CODEC_UNSOL_RESPONSES 16 #define MAX_INPUTS 32 #define MAX_IO_WIDGETS 8 #define MAX_ASSOCIATIONS 32 #define MAX_ASSOCIATION_PINS 16 #define STREAM_MAX_BUFFERS 10 #define STREAM_MIN_BUFFERS 2 enum { STREAM_PLAYBACK, STREAM_RECORD }; struct hda_codec; struct hda_stream; struct hda_multi; /*! This structure describes a single HDA compliant controller. It contains a list of available streams for use by the codecs contained, and the messaging queue (verb/response) buffers for communication. */ struct hda_controller { struct pci_info pci_info; int32 opened; const char* devfs_path; area_id regs_area; vuint8* regs; uint32 irq; bool msi; bool dma_snooping; bool is_64_bit; uint16 codec_status; uint32 num_input_streams; uint32 num_output_streams; uint32 num_bidir_streams; uint32 corb_length; uint32 rirb_length; uint32 rirb_read_pos; uint32 corb_write_pos; area_id corb_rirb_pos_area; corb_t* corb; rirb_t* rirb; uint32* stream_positions; hda_codec* codecs[HDA_MAX_CODECS + 1]; hda_codec* active_codec; uint32 num_codecs; hda_stream* streams[HDA_MAX_STREAMS]; sem_id buffer_ready_sem; uint8 Read8(uint32 reg) { return *(regs + reg); } uint16 Read16(uint32 reg) { return *(vuint16*)(regs + reg); } uint32 Read32(uint32 reg) { return *(vuint32*)(regs + reg); } void Write8(uint32 reg, uint8 value) { *(regs + reg) = value; } void Write16(uint32 reg, uint16 value) { *(vuint16*)(regs + reg) = value; } void Write32(uint32 reg, uint32 value) { *(vuint32*)(regs + reg) = value; } void ReadModifyWrite8(uint32 reg, uint8 mask, uint8 value) { uint8 temp = Read8(reg); temp &= ~mask; temp |= value; Write8(reg, temp); } void ReadModifyWrite16(uint32 reg, uint16 mask, uint16 value) { uint16 temp = Read16(reg); temp &= ~mask; temp |= value; Write16(reg, temp); } void ReadModifyWrite32(uint32 reg, uint32 mask, uint32 value) { uint32 temp = Read32(reg); temp &= ~mask; temp |= value; Write32(reg, temp); } }; /*! This structure describes a single stream of audio data, which is can have multiple channels (for stereo or better). */ struct hda_stream { uint32 id; /* HDA controller stream # */ uint32 offset; /* HDA I/O/B descriptor offset */ bool running; spinlock lock; /* Write lock */ uint32 type; hda_controller* controller; uint32 pin_widget; /* PIN Widget ID */ uint32 io_widgets[MAX_IO_WIDGETS]; /* Input/Output Converter Widget ID */ uint32 num_io_widgets; uint32 sample_rate; uint32 sample_format; uint32 num_buffers; uint32 num_channels; uint32 buffer_length; /* size of buffer in samples */ uint32 buffer_size; /* actual (aligned) size of buffer in bytes */ uint32 sample_size; uint8* buffers[STREAM_MAX_BUFFERS]; /* Virtual addresses for buffer */ phys_addr_t physical_buffers[STREAM_MAX_BUFFERS]; /* Physical addresses for buffer */ volatile bigtime_t real_time; volatile uint64 frames_count; uint32 last_link_frame_position; volatile int32 buffer_cycle; uint32 rate, bps; /* Samplerate & bits per sample */ area_id buffer_area; area_id buffer_descriptors_area; phys_addr_t physical_buffer_descriptors; /* BDL physical address */ int32 incorrect_position_count; bool use_dma_position; uint8 Read8(uint32 reg) { return controller->Read8(HDAC_STREAM_BASE + offset + reg); } uint16 Read16(uint32 reg) { return controller->Read16(HDAC_STREAM_BASE + offset + reg); } uint32 Read32(uint32 reg) { return controller->Read32(HDAC_STREAM_BASE + offset + reg); } void Write8(uint32 reg, uint8 value) { *(controller->regs + HDAC_STREAM_BASE + offset + reg) = value; } void Write16(uint32 reg, uint16 value) { *(vuint16*)(controller->regs + HDAC_STREAM_BASE + offset + reg) = value; } void Write32(uint32 reg, uint32 value) { *(vuint32*)(controller->regs + HDAC_STREAM_BASE + offset + reg) = value; } }; struct hda_widget { uint32 node_id; uint32 num_inputs; int32 active_input; uint32 inputs[MAX_INPUTS]; uint32 flags; hda_widget_type type; uint32 pm; struct { uint32 audio; uint32 output_amplifier; uint32 input_amplifier; } capabilities; union { struct { uint32 formats; uint32 rates; } io; struct { } mixer; struct { uint32 capabilities; uint32 config; } pin; } d; }; struct hda_association { uint32 index; bool enabled; uint32 pin_count; uint32 pins[MAX_ASSOCIATION_PINS]; }; #define WIDGET_FLAG_OUTPUT_PATH 0x01 #define WIDGET_FLAG_INPUT_PATH 0x02 #define WIDGET_FLAG_WIDGET_PATH 0x04 /*! This structure describes a single Audio Function Group. An AFG is a group of audio widgets which can be used to configure multiple streams of audio either from the HDA Link to an output device (= playback) or from an input device to the HDA link (= recording). */ struct hda_audio_group { hda_codec* codec; hda_widget widget; /* Multi Audio API data */ hda_stream* playback_stream; hda_stream* record_stream; uint32 widget_start; uint32 widget_count; uint32 association_count; uint32 gpio; hda_widget* widgets; hda_association associations[MAX_ASSOCIATIONS]; hda_multi* multi; }; /*! This structure describes a single codec module in the HDA compliant device. This is a discrete component, which can contain both Audio Function Groups, Modem Function Groups, and other customized (vendor specific) Function Groups. NOTE: ATM, only Audio Function Groups are supported. */ struct hda_codec { uint16 vendor_id; uint16 product_id; uint32 subsystem_id; uint8 major; uint8 minor; uint8 revision; uint8 stepping; uint8 addr; uint32 quirks; sem_id response_sem; uint32 responses[MAX_CODEC_RESPONSES]; uint32 response_count; sem_id unsol_response_sem; thread_id unsol_response_thread; uint32 unsol_responses[MAX_CODEC_UNSOL_RESPONSES]; uint32 unsol_response_read, unsol_response_write; hda_audio_group* audio_groups[HDA_MAX_AUDIO_GROUPS]; uint32 num_audio_groups; struct hda_controller* controller; }; #define MULTI_CONTROL_FIRSTID 1024 #define MULTI_CONTROL_MASTERID 0 #define MULTI_MAX_CONTROLS 128 #define MULTI_MAX_CHANNELS 128 struct hda_multi_mixer_control { hda_multi *multi; int32 nid; int32 type; bool input; uint32 mute; uint32 gain; uint32 capabilities; int32 index; multi_mix_control mix_control; }; struct hda_multi { hda_audio_group *group; hda_multi_mixer_control controls[MULTI_MAX_CONTROLS]; uint32 control_count; multi_channel_info chans[MULTI_MAX_CHANNELS]; uint32 output_channel_count; uint32 input_channel_count; uint32 output_bus_channel_count; uint32 input_bus_channel_count; uint32 aux_bus_channel_count; }; /* driver.c */ extern device_hooks gDriverHooks; extern pci_module_info* gPci; extern hda_controller gCards[MAX_CARDS]; extern uint32 gNumCards; /* hda_codec.c */ const char* get_widget_location(uint32 location); hda_widget* hda_audio_group_get_widget(hda_audio_group* audioGroup, uint32 nodeID); status_t hda_audio_group_get_widgets(hda_audio_group* audioGroup, hda_stream* stream); hda_codec* hda_codec_new(hda_controller* controller, uint32 cad); void hda_codec_delete(hda_codec* codec); /* hda_multi_audio.c */ status_t multi_audio_control(void* cookie, uint32 op, void* arg, size_t length); void get_settings_from_file(); /* hda_controller.c: Basic controller support */ status_t hda_hw_init(hda_controller* controller); void hda_hw_stop(hda_controller* controller); void hda_hw_uninit(hda_controller* controller); status_t hda_send_verbs(hda_codec* codec, corb_t* verbs, uint32* responses, uint32 count); status_t hda_verb_write(hda_codec* codec, uint32 nid, uint32 vid, uint16 payload); status_t hda_verb_read(hda_codec* codec, uint32 nid, uint32 vid, uint32 *response); /* hda_controller.c: Stream support */ hda_stream* hda_stream_new(hda_audio_group* audioGroup, int type); void hda_stream_delete(hda_stream* stream); status_t hda_stream_setup_buffers(hda_audio_group* audioGroup, hda_stream* stream, const char* desc); status_t hda_stream_start(hda_controller* controller, hda_stream* stream); status_t hda_stream_stop(hda_controller* controller, hda_stream* stream); #endif /* _HDA_H_ */