⛏️ index : haiku.git

/*
 * 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 <KernelExport.h>
#include <Drivers.h>
#include <PCI.h>
#include <debug.h>

#include <string.h>
#include <stdlib.h>

#define DEVFS_PATH_FORMAT	"audio/hmulti/hda/%" B_PRIu32
#include <hmulti_audio.h>

#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_ */