Home | History | Annotate | Line # | Download | only in hpc
      1 /* $NetBSD: haudvar.h,v 1.1 2026/06/11 01:03:58 rumble Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2025 Stephen M. Rumble <rumble (at) ephemeral.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/cdefs.h>
     20 
     21 #ifndef _ARCH_SGIMIPS_HPC_HAUDVAR_H_
     22 #define _ARCH_SGIMIPS_HPC_HAUDVAR_H_
     23 
     24 /*
     25  * Hollywood Audio is based around a Motorola 56001 DSP and supporting hardware
     26  * such as DACs, SRAMs, the HPC, and either the AUD1 ASIC or an equivalent FPGA.
     27  *
     28  * This driver leverages IRIX's hdsp.lod 56K code, which takes care of the audio
     29  * hardware behind the DSP and interfaces with the kernel primarily via circular
     30  * buffers in host physical memory.
     31  *
     32  * The DSP has a 32768 24-bit word memory provided by three 32Kbyte SRAMs. This
     33  * is used for the DSP's firmware code and data. The HPC maps the SRAMs into
     34  * the CPU's physical address space so that the kernel can load the DSP's
     35  * firmware and initialise circular command buffers. The SRAM is mapped via
     36  * 32768 contiguous 32-bit words at offset HPC1_DSP_SRAM. The HPC also provides
     37  * DMA for the DSP so that it can read from or write to the circular buffers in
     38  * host memory.
     39  *
     40  * IRIX's hdsp.lod firmware has two command buffers, one for sending command
     41  * requests to the DSP and one for receiving responses back. Command examples
     42  * include setting up new circular buffers for playback or recording ('sample
     43  * buffers'), tearing down unused circular buffers, and changing various audio
     44  * parameters such as output volumes, input attentuation, etc.
     45  *
     46  * The command request and response buffers are bootstrapped by writing to
     47  * special locations low in the SRAM space (after writing the firmware, but
     48  * before taking the DSP chip out of reset). After bootstrap, all subsequent
     49  * interaction happens via host memory (buffers), registers in the HPC, and
     50  * interrupts. The command request and response buffers are contiguous in host
     51  * physical memory, though due to their small size they can easily fit within a
     52  * page.
     53  *
     54  * All playback and recording uses 16-bit big endian PCM samples. Each sample
     55  * occupies a full 32-bit word in the circular buffer and is shifted 8 bits left
     56  * (i.e., it occupies the middle two bytes).
     57  *
     58  * Sample buffers for playback and recording do not need to be physically
     59  * contiguous in memory. Constituent pages are registered with the DSP as part
     60  * of setting up a new buffer (via the command buffer).
     61  *
     62  * Both command and sample buffers are controlled by a 16-byte header. The
     63  * command buffers' headers are followed immediately by their circular buffer.
     64  * Sample buffers' headers and circular buffers can be split apart, but this
     65  * driver maintains the same layout for both types.
     66  *
     67  * The header's format is shared with the DSP and consists of four 32-bit fields
     68  * (see haud_dsp_buffer_header_t below for details). Each circular buffer is
     69  * either used to send audio data to the DSP for playback (TX) or receive audio
     70  * data from the DSP for recording (RX).
     71  *
     72  * Each sample buffer has two unique identifiers: one assigned by the kernel and
     73  * registered with the DSP (kern_id), and one assigned by the DSP in response to
     74  * the registration (dsp_id). The DSP-returned ID is presumably dense and refers
     75  * to a fixed table of buffers under management. The DSP writes dsp_id to the TX
     76  * handshake register to indicate which buffer the current interrupt applies to.
     77  * It's not really clear what use kern_id has on the DSP side.
     78  *
     79  * The special command request and response queues apparently go by IDs 0 and 1,
     80  * respectively, and the first sample buffer registered gets ID 2.
     81  *
     82  * The DSP appears to only support one output stream and one input stream, as
     83  * sample buffers aren't registered against a particular port and sample rates
     84  * are configured globally for all outputs and all inputs. That said, the DSP
     85  * will supposedly mix multiple active output buffers automatically (per IRIX's
     86  * audio(1) manpage).
     87  *
     88  * Audio is played to all output ports simultaneously. On systems with an
     89  * internal speaker (Indigo), plugging into the headphone jack cuts off the
     90  * speaker output.
     91  *
     92  * A single input source can be selected at time. TODO: What happens if multiple
     93  * input sample buffers are allocated? Presumably the DSP would return an error
     94  * for such a request.
     95  */
     96 
     97 /*
     98  * Number of 4KB pages we will allocate for each sample buffer.
     99  */
    100 #define HAUD_SAMPLE_BUFFER_PAGES 64
    101 
    102 /*
    103  * Circular buffer in the format understood by the DSP. Used for both command
    104  * request/response buffers and audio sample buffers.
    105  */
    106 typedef struct haud_dsp_buffer_header {
    107 	u_int32_t head;  	// Start of data (index in words units)
    108 	u_int32_t tail;  	// End of data (index in word units)
    109 	u_int32_t intr;  	// Set to 1 to trigger interrupt by DSP.
    110 	u_int32_t watr;  	// A low watermark for interrupt triggering?
    111 } haud_dsp_buffer_header_t;
    112 CTASSERT(sizeof(haud_dsp_buffer_header_t) == 4 * 4);
    113 
    114 /*
    115  * Represents a circular buffer registered with the DSP along with kernel state.
    116  *
    117  * Buffers are logically contiguous, but cobbled together from individual pages.
    118  * The first 16 bytes of the first page contains the header. We follow that
    119  * immediately with up to 1020 words of data. Pages after the first contain only
    120  * data.
    121  *
    122  * We could split the header and data, but there's not much point.
    123  */
    124 typedef struct haud_buffer {
    125 	u_int32_t kern_id;	// ID assigned by kernel, registered with DSP.
    126 	u_int32_t dsp_id;	// ID assigned by DSP.
    127 	bool is_write_buffer;	// True: CPU->DSP, false: DSP->CPU (ex. record).
    128 	kcondvar_t cv;		// For blocking on DSP I/O (with sc_intr_lock).
    129 
    130 	// One or more pages for the header and circular buffer. Command request
    131 	// and response buffers are one page, sample buffers are multiple.
    132 	struct {
    133 		bus_dmamap_t dma_map;
    134 		bus_dma_segment_t dma_seg;
    135 		uint32_t *kaddr;
    136 	} pages[HAUD_SAMPLE_BUFFER_PAGES];
    137 	int npages;
    138 } haud_buffer_t;
    139 
    140 /*
    141  * DSP Commands: Requests sent to the DSP via the command request buffer to
    142  * configure new sample buffers and change input/output parameters. Each request
    143  * is responded to via the response queue.
    144  *
    145  * The first word of each request and response is the full length of the message
    146  * in 32-bit words. The second word is the command opcode. Responses use the
    147  * same opcode as the request.
    148  */
    149 
    150 #define HAUD_DSP_CMD_REGISTER_BUFFER_OPCODE 4
    151 struct haud_dsp_cmd_register_buffer_req {
    152 	u_int32_t len;		// 8.
    153 	u_int32_t op;		// 4.
    154 	u_int32_t kern_id;	// Kernel-assigned identifier.
    155 	u_int32_t cap;		// Number of 32-bit words in the buffer.
    156 	u_int32_t out;		// 1: Output (CPU->DSP), 0: Input (DSP->CPU).
    157 	u_int32_t hdr_hi;	// haud_dsp_buffer phys. address (high 16 bits).
    158 	u_int32_t hdr_lo;	// haud_dsp_buffer phys. address (low 16 bits).
    159 	u_int32_t buf_off;	// Buffer start offset in first page (bytes).
    160 
    161 	/*
    162 	 * The following contains the physical page number (i.e., phys_addr /
    163 	 * 4096) of each page in the buffer. The first page needs to be included
    164 	 * even though it's already referenced above.
    165 	 *
    166 	 * This is a variable-length message, but we always allocate sample
    167 	 * buffers of the same static size.
    168 	 *
    169 	 * TODO: What is the DSP's upper bound for pages per buffer?
    170 	 */
    171 	u_int32_t page_nums[HAUD_SAMPLE_BUFFER_PAGES];
    172 };
    173 CTASSERT(sizeof(struct haud_dsp_cmd_register_buffer_req) ==
    174     (8 + HAUD_SAMPLE_BUFFER_PAGES) * 4);
    175 
    176 struct haud_dsp_cmd_register_buffer_resp {
    177 	u_int32_t len;		// 5.
    178 	u_int32_t op;		// 4.
    179 	u_int32_t kern_id;	// Echo of the ID given in the request.
    180 	u_int32_t error;	// 0 iff request succeeded.
    181 	u_int32_t dsp_id;	// ID allocated by the DSP.
    182 };
    183 CTASSERT(sizeof(struct haud_dsp_cmd_register_buffer_resp) == 5 * 4);
    184 
    185 #define HAUD_DSP_CMD_DEREGISTER_BUFFER_OPCODE 5
    186 struct haud_dsp_cmd_deregister_buffer_req {
    187 	u_int32_t len;		// 4.
    188 	u_int32_t op;		// 5.
    189 	u_int32_t unknown;	// Always 0?
    190 	u_int32_t dsp_id;	// ID of the buffer to dereg (XXX DSP ID?).
    191 };
    192 CTASSERT(sizeof(struct haud_dsp_cmd_deregister_buffer_req) == 4 * 4);
    193 
    194 struct haud_dsp_cmd_deregister_buffer_resp {
    195 	u_int32_t len;		// 4.
    196 	u_int32_t op;		// 5.
    197 	u_int32_t unknown;	// Always 0?
    198 	u_int32_t error;	// 0 iff request succeeded.
    199 };
    200 CTASSERT(sizeof(struct haud_dsp_cmd_deregister_buffer_resp) == 4 * 4);
    201 
    202 #define HAUD_DSP_CMD_SET_AUDIO_PARAMS 7
    203 struct haud_dsp_cmd_set_audio_params_req {
    204 	u_int32_t len;		// Variable: 3 + 2 * num_params.
    205 	u_int32_t op;		// 7.
    206 	u_int32_t unknown;	// Always 0?
    207 
    208 	/*
    209 	 * This is a variable-length message, but we always send the same full
    210 	 * set of parameters.
    211 	 */
    212 	struct {
    213 		u_int32_t param;
    214 		u_int32_t value;
    215 	} params[7];		// One or more <param, value> pairs.
    216 };
    217 CTASSERT(sizeof(struct haud_dsp_cmd_set_audio_params_req) == (3 + 7 * 2) * 4);
    218 
    219 struct haud_dsp_cmd_set_audio_params_resp {
    220 	u_int32_t len;		// 4.
    221 	u_int32_t op;		// 7.
    222 	u_int32_t unknown;	// Always 0?
    223 	u_int32_t unknown2;	// error?
    224 };
    225 CTASSERT(sizeof(struct haud_dsp_cmd_set_audio_params_resp) == 4 * 4);
    226 
    227 #define HAUD_AUDIO_PARAMS_INPUT_SRC		0	// 0:line 1:mic 2: digtl
    228 #define HAUD_AUDIO_PARAMS_INPUT_ATTN_L		1	// Left attenuation
    229 #define HAUD_AUDIO_PARAMS_INPUT_ATTN_R		2	// Right attenuation
    230 #define HAUD_AUDIO_PARAMS_INPUT_RATE		3	// See values below
    231 #define HAUD_AUDIO_PARAMS_OUTPUT_RATE		4	// See values below
    232 #define HAUD_AUDIO_PARAMS_SPKR_GAIN_L		5	// Left gain
    233 #define HAUD_AUDIO_PARAMS_SPKR_GAIN_R		6	// Right gain
    234 
    235 /*
    236  * Minimum and maximum values for input/output gain/attenuation parameters.
    237  */
    238 #define HAUD_MIN_GAIN	0
    239 #define HAUD_MAX_GAIN	255
    240 #define HAUD_MIN_ATTN	0
    241 #define HAUD_MAX_ATTN	255
    242 
    243 /*
    244  * Values for HAUD_AUDIO_PARAMS_INPUT_RATE and _OUTPUT_RATE parameters.
    245  * Many others are supported, too, but aren't terribly important.
    246  */
    247 #define HAUD_RATE_48000	0x00
    248 #define HAUD_RATE_44100	0x04
    249 #define HAUD_RATE_22050	0x14
    250 #define HAUD_RATE_11025	0x34
    251 
    252 struct haud_softc {
    253 	device_t sc_dev;
    254 	kmutex_t sc_lock;
    255 	kmutex_t sc_intr_lock;
    256 
    257 	bus_space_tag_t sc_st;
    258 	bus_space_handle_t sc_regs_sh;
    259 	bus_space_handle_t sc_sram_sh;
    260 	bus_dma_tag_t sc_dma_tag;
    261 
    262 	haud_buffer_t *sc_cmd_req;
    263 	haud_buffer_t *sc_cmd_resp;
    264 	haud_buffer_t *sc_output;
    265 
    266 	// Protected by sc_intr_lock.
    267 	bool sc_dsp_booted;
    268 
    269 	// Provided by the audio(9) layer in its call to haud_start_output.
    270 	void (*sc_output_intr)(void *);
    271 	void *sc_output_intr_arg;
    272 
    273 	// Used to trigger additional output after haud_start_output.
    274 	void *sc_output_softint_cookie;
    275 
    276 	int sc_speaker_l_gain;
    277 	int sc_speaker_r_gain;
    278 };
    279 
    280 #endif  /* _ARCH_SGIMIPS_HPC_HAUDVAR_H_ */
    281