945 lines
23 KiB
Rust
Executable File
945 lines
23 KiB
Rust
Executable File
#![allow(dead_code)]
|
|
|
|
use std::cmp;
|
|
use std::collections::HashMap;
|
|
use std::str;
|
|
use std::collections::BTreeMap;
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
use syscall::PHYSMAP_WRITE;
|
|
use syscall::error::{Error, EACCES, EBADF, Result, EINVAL};
|
|
use syscall::flag::{SEEK_SET, SEEK_CUR, SEEK_END};
|
|
use syscall::io::{Mmio, Io};
|
|
use syscall::scheme::SchemeBlockMut;
|
|
|
|
use spin::Mutex;
|
|
|
|
use super::BufferDescriptorListEntry;
|
|
use super::common::*;
|
|
use super::StreamDescriptorRegs;
|
|
use super::StreamBuffer;
|
|
use super::BitsPerSample;
|
|
use super::CommandBuffer;
|
|
use super::HDANode;
|
|
use super::OutputStream;
|
|
|
|
// GCTL - Global Control
|
|
const CRST: u32 = 1 << 0; // 1 bit
|
|
const FNCTRL: u32 = 1 << 1; // 1 bit
|
|
const UNSOL: u32 = 1 << 8; // 1 bit
|
|
|
|
// CORBCTL
|
|
const CMEIE: u8 = 1 << 0; // 1 bit
|
|
const CORBRUN: u8 = 1 << 1; // 1 bit
|
|
|
|
// CORBSIZE
|
|
const CORBSZCAP: (u8,u8) = (4, 4);
|
|
const CORBSIZE: (u8,u8) = (0, 2);
|
|
|
|
// CORBRP
|
|
const CORBRPRST: u16 = 1 << 15;
|
|
|
|
// RIRBWP
|
|
const RIRBWPRST: u16 = 1 << 15;
|
|
|
|
// RIRBCTL
|
|
const RINTCTL: u8 = 1 << 0; // 1 bit
|
|
const RIRBDMAEN: u8 = 1 << 1; // 1 bit
|
|
|
|
// ICS
|
|
const ICB: u16 = 1 << 0;
|
|
const IRV: u16 = 1 << 1;
|
|
|
|
// CORB and RIRB offset
|
|
|
|
const COMMAND_BUFFER_OFFSET: usize = 0x40;
|
|
|
|
const NUM_SUB_BUFFS: usize = 2;
|
|
const SUB_BUFF_SIZE: usize = 2048;
|
|
|
|
enum Handle {
|
|
Todo,
|
|
Pcmout(usize, usize, usize), // Card, index, block_ptr
|
|
Pcmin(usize, usize, usize), // Card, index, block_ptr
|
|
StrBuf(Vec<u8>,usize),
|
|
}
|
|
|
|
#[repr(packed)]
|
|
#[allow(dead_code)]
|
|
struct Regs {
|
|
gcap: Mmio<u16>,
|
|
vmin: Mmio<u8>,
|
|
vmaj: Mmio<u8>,
|
|
outpay: Mmio<u16>,
|
|
inpay: Mmio<u16>,
|
|
gctl: Mmio<u32>,
|
|
wakeen: Mmio<u16>,
|
|
statests: Mmio<u16>,
|
|
gsts: Mmio<u16>,
|
|
rsvd0: [Mmio<u8>; 6],
|
|
outstrmpay: Mmio<u16>,
|
|
instrmpay: Mmio<u16>,
|
|
rsvd1: [Mmio<u8>; 4],
|
|
intctl: Mmio<u32>,
|
|
intsts: Mmio<u32>,
|
|
rsvd2: [Mmio<u8>; 8],
|
|
walclk: Mmio<u32>,
|
|
rsvd3: Mmio<u32>,
|
|
ssync: Mmio<u32>,
|
|
rsvd4: Mmio<u32>,
|
|
|
|
corblbase: Mmio<u32>,
|
|
corbubase: Mmio<u32>,
|
|
corbwp: Mmio<u16>,
|
|
corbrp: Mmio<u16>,
|
|
corbctl: Mmio<u8>,
|
|
corbsts: Mmio<u8>,
|
|
corbsize: Mmio<u8>,
|
|
rsvd5: Mmio<u8>,
|
|
|
|
rirblbase: Mmio<u32>,
|
|
rirbubase: Mmio<u32>,
|
|
rirbwp: Mmio<u16>,
|
|
rintcnt: Mmio<u16>,
|
|
rirbctl: Mmio<u8>,
|
|
rirbsts: Mmio<u8>,
|
|
rirbsize: Mmio<u8>,
|
|
rsvd6: Mmio<u8>,
|
|
|
|
icoi: Mmio<u32>,
|
|
irii: Mmio<u32>,
|
|
ics: Mmio<u16>,
|
|
rsvd7: [Mmio<u8>; 6],
|
|
|
|
dplbase: Mmio<u32>, // 0x70
|
|
dpubase: Mmio<u32>, // 0x74
|
|
}
|
|
|
|
pub struct IntelHDA {
|
|
vend_prod: u32,
|
|
|
|
base: usize,
|
|
regs: &'static mut Regs,
|
|
|
|
//corb_rirb_base_phys: usize,
|
|
|
|
cmd: CommandBuffer,
|
|
|
|
codecs: Vec<CodecAddr>,
|
|
|
|
outputs: Vec<WidgetAddr>,
|
|
inputs: Vec<WidgetAddr>,
|
|
|
|
widget_map: HashMap<WidgetAddr, HDANode>,
|
|
|
|
output_pins: Vec<WidgetAddr>,
|
|
input_pins: Vec<WidgetAddr>,
|
|
|
|
beep_addr: WidgetAddr,
|
|
|
|
buff_desc: &'static mut [BufferDescriptorListEntry; 256],
|
|
buff_desc_phys: usize,
|
|
|
|
output_streams: Vec<OutputStream>,
|
|
|
|
buffs: Vec<Vec<StreamBuffer>>,
|
|
|
|
int_counter: usize,
|
|
handles: Mutex<BTreeMap<usize, Handle>>,
|
|
next_id: AtomicUsize,
|
|
}
|
|
|
|
impl IntelHDA {
|
|
pub unsafe fn new(base: usize, vend_prod:u32) -> Result<Self> {
|
|
let regs = &mut *(base as *mut Regs);
|
|
|
|
let buff_desc_phys = unsafe {
|
|
syscall::physalloc(0x1000)
|
|
.expect("Could not allocate physical memory for buffer descriptor list.")
|
|
};
|
|
|
|
let buff_desc_virt = unsafe {
|
|
syscall::physmap(buff_desc_phys, 0x1000, PHYSMAP_WRITE)
|
|
.expect("ihdad: failed to map address for buffer descriptor list.")
|
|
};
|
|
|
|
print!("Virt: {:016X}, Phys: {:016X}\n", buff_desc_virt, buff_desc_phys);
|
|
|
|
let buff_desc = &mut *(buff_desc_virt as *mut [BufferDescriptorListEntry;256]);
|
|
|
|
let cmd_buff_address = unsafe {
|
|
syscall::physalloc(0x1000)
|
|
.expect("Could not allocate physical memory for CORB and RIRB.")
|
|
};
|
|
|
|
let cmd_buff_virt = unsafe { syscall::physmap(cmd_buff_address, 0x1000, PHYSMAP_WRITE).expect("ihdad: failed to map address for CORB/RIRB buff") };
|
|
|
|
print!("Virt: {:016X}, Phys: {:016X}\n", cmd_buff_virt, cmd_buff_address);
|
|
let mut module = IntelHDA {
|
|
vend_prod: vend_prod,
|
|
base: base,
|
|
regs: regs,
|
|
|
|
cmd: CommandBuffer::new(base + COMMAND_BUFFER_OFFSET, cmd_buff_address, cmd_buff_virt),
|
|
|
|
beep_addr: (0,0),
|
|
|
|
widget_map: HashMap::<WidgetAddr, HDANode>::new(),
|
|
|
|
codecs: Vec::<CodecAddr>::new(),
|
|
|
|
outputs: Vec::<WidgetAddr>::new(),
|
|
inputs: Vec::<WidgetAddr>::new(),
|
|
|
|
output_pins: Vec::<WidgetAddr>::new(),
|
|
input_pins: Vec::<WidgetAddr>::new(),
|
|
|
|
buff_desc: buff_desc,
|
|
buff_desc_phys: buff_desc_phys,
|
|
|
|
output_streams: Vec::<OutputStream>::new(),
|
|
|
|
buffs: Vec::<Vec<StreamBuffer>>::new(),
|
|
|
|
int_counter: 0,
|
|
handles: Mutex::new(BTreeMap::new()),
|
|
next_id: AtomicUsize::new(0),
|
|
};
|
|
|
|
module.init();
|
|
|
|
// module.info();
|
|
module.enumerate();
|
|
|
|
module.configure();
|
|
print!("IHDA: Initialization finished.\n");
|
|
Ok(module)
|
|
|
|
}
|
|
|
|
pub fn init(&mut self) -> bool {
|
|
self.reset_controller();
|
|
|
|
let use_immediate_command_interface = match self.vend_prod {
|
|
|
|
0x8086_2668 => false,
|
|
_ => true,
|
|
};
|
|
|
|
self.cmd.init(use_immediate_command_interface);
|
|
self.init_interrupts();
|
|
|
|
true
|
|
}
|
|
|
|
pub fn init_interrupts(&mut self) {
|
|
// TODO: provide a function to enable certain interrupts
|
|
// This just enables the first output stream interupt and the global interrupt
|
|
|
|
// TODO: No magic numbers! Bad Schemm.
|
|
self.regs.intctl.write((1 << 31) | /* (1 << 30) |*/ (1 << 4));
|
|
}
|
|
|
|
pub fn irq(&mut self) -> bool {
|
|
self.int_counter += 1;
|
|
|
|
self.handle_interrupts()
|
|
}
|
|
|
|
pub fn int_count(&self) -> usize {
|
|
self.int_counter
|
|
}
|
|
|
|
pub fn read_node(&mut self, addr: WidgetAddr) -> HDANode {
|
|
let mut node = HDANode::new();
|
|
let mut temp:u64;
|
|
|
|
node.addr = addr;
|
|
|
|
temp = self.cmd.cmd12( addr, 0xF00, 0x04);
|
|
|
|
node.subnode_count = (temp & 0xff) as u16;
|
|
node.subnode_start = ((temp >> 16) & 0xff) as u16;
|
|
|
|
if addr == (0,0) {
|
|
return node;
|
|
}
|
|
temp = self.cmd.cmd12(addr, 0xF00, 0x04);
|
|
|
|
node.function_group_type = (temp & 0xff) as u8;
|
|
|
|
temp = self.cmd.cmd12(addr, 0xF00, 0x09);
|
|
node.capabilities = temp as u32;
|
|
|
|
|
|
temp = self.cmd.cmd12(addr, 0xF00, 0x0E);
|
|
|
|
node.conn_list_len = (temp & 0xFF) as u8;
|
|
|
|
node.connections = self.node_get_connection_list(&node);
|
|
|
|
|
|
node.config_default = self.cmd.cmd12(addr, 0xF1C, 0x00) as u32;
|
|
|
|
node
|
|
}
|
|
|
|
pub fn node_get_connection_list(&mut self, node: &HDANode) -> Vec<WidgetAddr> {
|
|
let len_field: u8 = (self.cmd.cmd12(node.addr, 0xF00, 0x0E) & 0xFF) as u8;
|
|
|
|
// Highest bit is if addresses are represented in longer notation
|
|
// lower 7 is actual count
|
|
|
|
let count:u8 = len_field & 0x7F;
|
|
let use_long_addr: bool = (len_field >> 7) & 0x1 == 1;
|
|
|
|
let mut current: u8 = 0;
|
|
|
|
let mut list = Vec::<WidgetAddr>::new();
|
|
|
|
while current < count {
|
|
|
|
let response: u32 = (self.cmd.cmd12(node.addr, 0xF02, current) & 0xFFFFFFFF) as u32;
|
|
|
|
if use_long_addr {
|
|
for i in 0..2 {
|
|
let addr_field = ((response >> (16 * i)) & 0xFFFF) as u16;
|
|
let addr = addr_field & 0x7FFF;
|
|
|
|
if addr == 0 { break; }
|
|
|
|
if (addr_field >> 15) & 0x1 == 0x1 {
|
|
for i in list.pop().unwrap().1 .. (addr + 1) {
|
|
list.push((node.addr.0, i));
|
|
}
|
|
} else {
|
|
list.push((node.addr.0, addr));
|
|
}
|
|
}
|
|
|
|
} else {
|
|
for i in 0..4 {
|
|
let addr_field = ((response >> (8 * i)) & 0xff) as u16;
|
|
let addr = addr_field & 0x7F;
|
|
|
|
if addr == 0 { break; }
|
|
|
|
if (addr_field >> 7) & 0x1 == 0x1 {
|
|
for i in list.pop().unwrap().1 .. (addr + 1) {
|
|
list.push((node.addr.0, i));
|
|
}
|
|
} else {
|
|
list.push((node.addr.0, addr));
|
|
}
|
|
}
|
|
}
|
|
|
|
current = list.len() as u8;
|
|
}
|
|
|
|
list
|
|
}
|
|
|
|
pub fn enumerate(&mut self) {
|
|
self.output_pins.clear();
|
|
self.input_pins.clear();
|
|
|
|
let codec:u8 = 0;
|
|
|
|
let root = self.read_node((codec,0));
|
|
|
|
// print!("{}\n", root);
|
|
|
|
let root_count = root.subnode_count;
|
|
let root_start = root.subnode_start;
|
|
|
|
//FIXME: So basically the way this is set up is to only support one codec and hopes the first one is an audio
|
|
for i in 0..root_count {
|
|
let afg = self.read_node((codec, root_start + i));
|
|
// print!("{}\n", afg);
|
|
let afg_count = afg.subnode_count;
|
|
let afg_start = afg.subnode_start;
|
|
|
|
for j in 0..afg_count {
|
|
|
|
let mut widget = self.read_node((codec, afg_start + j));
|
|
widget.is_widget = true;
|
|
match widget.widget_type() {
|
|
HDAWidgetType::AudioOutput => {self.outputs.push(widget.addr)},
|
|
HDAWidgetType::AudioInput => {self.inputs.push(widget.addr)},
|
|
HDAWidgetType::BeepGenerator => {self.beep_addr = widget.addr },
|
|
HDAWidgetType::PinComplex => {
|
|
let config = widget.configuration_default();
|
|
if config.is_output() {
|
|
self.output_pins.push(widget.addr);
|
|
} else if config.is_input() {
|
|
self.input_pins.push(widget.addr);
|
|
}
|
|
|
|
print!("{:02X}{:02X} {}\n", widget.addr().0, widget.addr().1, config);
|
|
|
|
},
|
|
_ => {},
|
|
}
|
|
|
|
print!("{}\n", widget);
|
|
self.widget_map.insert(widget.addr(), widget);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn find_best_output_pin(&self) -> Option<WidgetAddr>{
|
|
let outs = &self.output_pins;
|
|
if outs.len() == 0 {
|
|
None
|
|
} else if outs.len() == 1 {
|
|
Some(outs[0])
|
|
} else {
|
|
// TODO: Somehow find the best.
|
|
// Slightly okay is find the speaker with the lowest sequence number.
|
|
|
|
for &out in outs {
|
|
let widget = self.widget_map.get(&out).unwrap();
|
|
|
|
let cd = widget.configuration_default();
|
|
if cd.sequence() == 0 && cd.default_device() == DefaultDevice::Speaker {
|
|
return Some(out);
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn find_path_to_dac(&self, addr: WidgetAddr) -> Option<Vec<WidgetAddr>>{
|
|
let widget = self.widget_map.get(&addr).unwrap();
|
|
if widget.widget_type() == HDAWidgetType::AudioOutput {
|
|
return Some(vec![addr]);
|
|
}else{
|
|
if widget.connections.len() == 0 {
|
|
return None;
|
|
}else{
|
|
// TODO: do more than just first widget
|
|
|
|
let res = self.find_path_to_dac(widget.connections[0]);
|
|
match res {
|
|
Some(p) => {
|
|
let mut ret = p.clone();
|
|
ret.insert(0, addr);
|
|
Some(ret)
|
|
},
|
|
None => {None},
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
Here we update the buffers and split them into 128 byte sub chunks
|
|
because each BufferDescriptorList needs to be 128 byte aligned,
|
|
this makes it so each of the streams can have up to 128/16 (8) buffer descriptors
|
|
*/
|
|
/*
|
|
Vec of a Vec was doing something weird and causing the driver to hang.
|
|
So now we have a set of variables instead.
|
|
|
|
|
|
Fixed?
|
|
*/
|
|
|
|
pub fn update_sound_buffers(&mut self) {
|
|
/*
|
|
for i in 0..self.buffs.len(){
|
|
for j in 0.. min(self.buffs[i].len(), 128/16 ) {
|
|
self.buff_desc[i * 128/16 + j].set_address(self.buffs[i][j].phys());
|
|
self.buff_desc[i * 128/16 + j].set_length(self.buffs[i][j].length() as u32);
|
|
self.buff_desc[i * 128/16 + j].set_interrupt_on_complete(true);
|
|
}
|
|
}*/
|
|
|
|
let r = self.get_output_stream_descriptor(0).unwrap();
|
|
|
|
self.output_streams.push(OutputStream::new(NUM_SUB_BUFFS, SUB_BUFF_SIZE, r));
|
|
|
|
let o = self.output_streams.get_mut(0).unwrap();
|
|
|
|
self.buff_desc[0].set_address(o.phys());
|
|
self.buff_desc[0].set_length(o.block_size() as u32);
|
|
self.buff_desc[0].set_interrupt_on_complete(true);
|
|
|
|
self.buff_desc[1].set_address(o.phys() + o.block_size());
|
|
self.buff_desc[1].set_length(o.block_size() as u32);
|
|
self.buff_desc[1].set_interrupt_on_complete(true);
|
|
}
|
|
|
|
pub fn configure(&mut self) {
|
|
let outpin = self.find_best_output_pin().expect("IHDA: No output pins?!");
|
|
|
|
//print!("Best pin: {:01X}:{:02X}\n", outpin.0, outpin.1);
|
|
|
|
let path = self.find_path_to_dac(outpin).unwrap();
|
|
|
|
let dac = *path.last().unwrap();
|
|
let pin = *path.first().unwrap();
|
|
|
|
//print!("Path to DAC: {:?}\n", path);
|
|
|
|
// Pin enable
|
|
self.cmd.cmd12(pin, 0x707, 0x40);
|
|
|
|
// EAPD enable
|
|
self.cmd.cmd12(pin, 0x70C, 2);
|
|
|
|
self.set_stream_channel(dac, 1, 0);
|
|
|
|
self.update_sound_buffers();
|
|
|
|
//print!("Supported Formats: {:08X}\n", self.get_supported_formats((0,0x1)));
|
|
//print!("Capabilities: {:08X}\n", self.get_capabilities(path[0]));
|
|
|
|
let output = self.get_output_stream_descriptor(0).unwrap();
|
|
|
|
output.set_address(self.buff_desc_phys);
|
|
|
|
output.set_pcm_format(&super::SR_44_1, BitsPerSample::Bits16, 2);
|
|
output.set_cyclic_buffer_length(0x8000); // number of bytes
|
|
output.set_stream_number(1);
|
|
output.set_last_valid_index(1);
|
|
output.set_interrupt_on_completion(true);
|
|
|
|
|
|
self.set_power_state(dac, 0); // Power state 0 is fully on
|
|
self.set_converter_format(dac, &super::SR_44_1, BitsPerSample::Bits16, 2);
|
|
|
|
|
|
self.cmd.cmd12(dac, 0xA00, 0);
|
|
|
|
// Unmute and set gain for pin complex and DAC
|
|
self.set_amplifier_gain_mute(dac, true, true, true, true, 0, false, 0x7f);
|
|
self.set_amplifier_gain_mute(pin, true, true, true, true, 0, false, 0x7f);
|
|
|
|
output.run();
|
|
|
|
}
|
|
/*
|
|
|
|
pub fn configure_vbox(&mut self) {
|
|
|
|
let outpin = self.find_best_output_pin().expect("IHDA: No output pins?!");
|
|
|
|
print!("Best pin: {:01X}:{:02X}\n", outpin.0, outpin.1);
|
|
|
|
let path = self.find_path_to_dac(outpin).unwrap();
|
|
print!("Path to DAC: {:?}\n", path);
|
|
|
|
// Pin enable
|
|
self.cmd.cmd12((0,0xC), 0x707, 0x40);
|
|
|
|
|
|
// EAPD enable
|
|
self.cmd.cmd12((0,0xC), 0x70C, 2);
|
|
|
|
self.set_stream_channel((0,0x3), 1, 0);
|
|
|
|
self.update_sound_buffers();
|
|
|
|
|
|
print!("Supported Formats: {:08X}\n", self.get_supported_formats((0,0x1)));
|
|
print!("Capabilities: {:08X}\n", self.get_capabilities((0,0x1)));
|
|
|
|
let output = self.get_output_stream_descriptor(0).unwrap();
|
|
|
|
output.set_address(self.buff_desc_phys);
|
|
|
|
output.set_pcm_format(&super::SR_44_1, BitsPerSample::Bits16, 2);
|
|
output.set_cyclic_buffer_length(0x8000);
|
|
output.set_stream_number(1);
|
|
output.set_last_valid_index(1);
|
|
output.set_interrupt_on_completion(true);
|
|
|
|
|
|
self.set_power_state((0,0x3), 0); // Power state 0 is fully on
|
|
self.set_converter_format((0,0x3), &super::SR_44_1, BitsPerSample::Bits16, 2);
|
|
|
|
|
|
self.cmd.cmd12((0,0x3), 0xA00, 0);
|
|
|
|
// Unmute and set gain for pin complex and DAC
|
|
self.set_amplifier_gain_mute((0,0x3), true, true, true, true, 0, false, 0x7f);
|
|
self.set_amplifier_gain_mute((0,0xC), true, true, true, true, 0, false, 0x7f);
|
|
|
|
output.run();
|
|
|
|
self.beep(1);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
// BEEP!!
|
|
pub fn beep(&mut self, div:u8) {
|
|
let addr = self.beep_addr;
|
|
if addr != (0,0) {
|
|
let _ = self.cmd.cmd12(addr, 0xF0A, div);
|
|
}
|
|
}
|
|
|
|
pub fn read_beep(&mut self) -> u8 {
|
|
let addr = self.beep_addr;
|
|
if addr != (0,0) {
|
|
self.cmd.cmd12(addr, 0x70A, 0) as u8
|
|
}else{
|
|
0
|
|
}
|
|
}
|
|
|
|
pub fn reset_controller(&mut self) -> bool {
|
|
self.regs.statests.write(0xFFFF);
|
|
|
|
// 3.3.7
|
|
self.regs.gctl.writef(CRST, false);
|
|
loop {
|
|
if ! self.regs.gctl.readf(CRST) {
|
|
break;
|
|
}
|
|
}
|
|
self.regs.gctl.writef(CRST, true);
|
|
loop {
|
|
if self.regs.gctl.readf(CRST) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
let mut ticks:u32 = 0;
|
|
while self.regs.statests.read() == 0 {
|
|
ticks += 1;
|
|
if ticks > 10000 { break;}
|
|
|
|
}
|
|
|
|
let statests = self.regs.statests.read();
|
|
print!("Statests: {:04X}\n", statests);
|
|
|
|
for i in 0..15 {
|
|
if (statests >> i) & 0x1 == 1 {
|
|
self.codecs.push(i as CodecAddr);
|
|
}
|
|
}
|
|
true
|
|
|
|
}
|
|
|
|
pub fn num_output_streams(&self) -> usize{
|
|
let gcap = self.regs.gcap.read();
|
|
((gcap >> 12) & 0xF) as usize
|
|
}
|
|
|
|
pub fn num_input_streams(&self) -> usize{
|
|
let gcap = self.regs.gcap.read();
|
|
((gcap >> 8) & 0xF) as usize
|
|
}
|
|
|
|
pub fn num_bidirectional_streams(&self) -> usize{
|
|
let gcap = self.regs.gcap.read();
|
|
((gcap >> 3) & 0xF) as usize
|
|
}
|
|
|
|
pub fn num_serial_data_out(&self) -> usize{
|
|
let gcap = self.regs.gcap.read();
|
|
((gcap >> 1) & 0x3) as usize
|
|
}
|
|
|
|
pub fn info(&self) {
|
|
print!("Intel HD Audio Version {}.{}\n", self.regs.vmaj.read(), self.regs.vmin.read());
|
|
print!("IHDA: Input Streams: {}\n", self.num_input_streams());
|
|
print!("IHDA: Output Streams: {}\n", self.num_output_streams());
|
|
print!("IHDA: Bidirectional Streams: {}\n", self.num_bidirectional_streams());
|
|
print!("IHDA: Serial Data Outputs: {}\n", self.num_serial_data_out());
|
|
print!("IHDA: 64-Bit: {}\n", self.regs.gcap.read() & 1 == 1);
|
|
}
|
|
|
|
fn get_input_stream_descriptor(&self, index: usize) -> Option<&'static mut StreamDescriptorRegs> {
|
|
unsafe {
|
|
if index < self.num_input_streams() {
|
|
Some(&mut *((self.base + 0x80 + index * 0x20) as *mut StreamDescriptorRegs))
|
|
}else{
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_output_stream_descriptor(&self, index: usize) -> Option<&'static mut StreamDescriptorRegs> {
|
|
unsafe {
|
|
if index < self.num_output_streams() {
|
|
Some(&mut *((self.base + 0x80 +
|
|
self.num_input_streams() * 0x20 +
|
|
index * 0x20) as *mut StreamDescriptorRegs))
|
|
}else{
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
fn get_bidirectional_stream_descriptor(&self, index: usize) -> Option<&'static mut StreamDescriptorRegs> {
|
|
unsafe {
|
|
if index < self.num_bidirectional_streams() {
|
|
Some(&mut *((self.base + 0x80 +
|
|
self.num_input_streams() * 0x20 +
|
|
self.num_output_streams() * 0x20 +
|
|
index * 0x20) as *mut StreamDescriptorRegs))
|
|
}else{
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
fn set_dma_position_buff_addr(&mut self, addr: usize) {
|
|
let addr_val = addr & !0x7F;
|
|
self.regs.dplbase.write((addr_val & 0xFFFFFFFF) as u32);
|
|
self.regs.dpubase.write((addr_val >> 32) as u32);
|
|
}
|
|
|
|
|
|
fn set_stream_channel(&mut self, addr: WidgetAddr, stream: u8, channel:u8) {
|
|
let val = ((stream & 0xF) << 4) | (channel & 0xF);
|
|
self.cmd.cmd12(addr, 0x706, val);
|
|
}
|
|
|
|
fn set_power_state(&mut self, addr:WidgetAddr, state:u8) {
|
|
self.cmd.cmd12(addr, 0x705, state & 0xF) as u32;
|
|
}
|
|
|
|
fn get_supported_formats(&mut self, addr: WidgetAddr) -> u32 {
|
|
self.cmd.cmd12(addr, 0xF00, 0x0A) as u32
|
|
}
|
|
|
|
fn get_capabilities(&mut self, addr: WidgetAddr) -> u32 {
|
|
self.cmd.cmd12(addr, 0xF00, 0x09) as u32
|
|
}
|
|
|
|
fn set_converter_format(&mut self, addr:WidgetAddr, sr: &super::SampleRate, bps: BitsPerSample, channels:u8) {
|
|
let fmt = super::format_to_u16(sr, bps, channels);
|
|
self.cmd.cmd4(addr, 0x2, fmt);
|
|
}
|
|
|
|
fn set_amplifier_gain_mute(&mut self, addr: WidgetAddr, output:bool, input:bool, left:bool, right:bool, index:u8, mute:bool, gain: u8) {
|
|
let mut payload: u16 = 0;
|
|
|
|
if output { payload |= 1 << 15; }
|
|
if input { payload |= 1 << 14; }
|
|
if left { payload |= 1 << 13; }
|
|
if right { payload |= 1 << 12; }
|
|
if mute { payload |= 1 << 7; }
|
|
payload |= ((index as u16) & 0x0F) << 8;
|
|
payload |= (gain as u16) & 0x7F;
|
|
|
|
self.cmd.cmd4(addr, 0x3, payload);
|
|
|
|
|
|
}
|
|
|
|
pub fn write_to_output(&mut self, index:u8, buf: &[u8]) -> Result<Option<usize>> {
|
|
let output = self.get_output_stream_descriptor(index as usize).unwrap();
|
|
let os = self.output_streams.get_mut(index as usize).unwrap();
|
|
|
|
//let sample_size:usize = output.sample_size();
|
|
let mut open_block = (output.link_position() as usize) / os.block_size();
|
|
|
|
if open_block == 0 {
|
|
open_block = 1;
|
|
} else {
|
|
open_block = open_block - 1;
|
|
}
|
|
|
|
//print!("Status: {:02X} Pos: {:08X} Output CTL: {:06X}\n", output.status(), output.link_position(), output.control());
|
|
|
|
if open_block == os.current_block() {
|
|
Ok(None)
|
|
} else {
|
|
os.write_block(buf).map(|count| Some(count))
|
|
}
|
|
}
|
|
|
|
pub fn handle_interrupts(&mut self) -> bool {
|
|
let intsts = self.regs.intsts.read();
|
|
let sis = intsts & 0x3FFFFFFF;
|
|
//print!("IHDA INTSTS: {:08X}\n", intsts);
|
|
if ((intsts >> 31) & 1) == 1 { // Global Interrupt Status
|
|
if ((intsts >> 30) & 1) == 1 { // Controller Interrupt Status
|
|
self.handle_controller_interrupt();
|
|
}
|
|
|
|
if sis != 0 {
|
|
self.handle_stream_interrupts(sis);
|
|
}
|
|
}
|
|
|
|
intsts != 0
|
|
}
|
|
|
|
pub fn handle_controller_interrupt(&mut self) {
|
|
|
|
}
|
|
|
|
pub fn handle_stream_interrupts(&mut self, sis: u32) {
|
|
let oss = self.num_output_streams();
|
|
let iss = self.num_input_streams();
|
|
let bss = self.num_bidirectional_streams();
|
|
|
|
for i in 0..iss {
|
|
if ((sis >> i) & 1 ) == 1 {
|
|
let mut input = self.get_input_stream_descriptor(i).unwrap();
|
|
input.clear_interrupts();
|
|
}
|
|
}
|
|
|
|
for i in 0..oss {
|
|
if ((sis >> (i + iss)) & 1 ) == 1 {
|
|
let mut output = self.get_output_stream_descriptor(i).unwrap();
|
|
output.clear_interrupts();
|
|
}
|
|
}
|
|
|
|
for i in 0..bss {
|
|
if ((sis >> (i + iss + oss)) & 1 ) == 1 {
|
|
let mut bid = self.get_bidirectional_stream_descriptor(i).unwrap();
|
|
bid.clear_interrupts();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn validate_path(&mut self, path: &Vec<&str>) -> bool {
|
|
print!("Path: {:?}\n", path);
|
|
let mut it = path.iter();
|
|
match it.next() {
|
|
Some(card_str) if (*card_str).starts_with("card") => {
|
|
match usize::from_str_radix(&(*card_str)[4..], 10) {
|
|
Ok(card_num) => {
|
|
print!("Card# {}\n", card_num);
|
|
match it.next() {
|
|
Some(codec_str) if (*codec_str).starts_with("codec#") => {
|
|
match usize::from_str_radix(&(*codec_str)[6..], 10) {
|
|
Ok(_codec_num) => {
|
|
//let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
|
//self.handles.lock().insert(id, Handle::Disk(disk.clone(), 0));
|
|
true
|
|
|
|
},
|
|
_ => false,
|
|
}
|
|
},
|
|
Some(pcmout_str) if (*pcmout_str).starts_with("pcmout") => {
|
|
match usize::from_str_radix(&(*pcmout_str)[6..], 10) {
|
|
Ok(pcmout_num) => {
|
|
print!("pcmout {}\n", pcmout_num);
|
|
true
|
|
},
|
|
_ => false,
|
|
}
|
|
},
|
|
Some(pcmin_str) if (*pcmin_str).starts_with("pcmin") => {
|
|
match usize::from_str_radix(&(*pcmin_str)[6..], 10) {
|
|
Ok(pcmin_num) => {
|
|
print!("pcmin {}\n", pcmin_num);
|
|
true
|
|
},
|
|
_ => false,
|
|
}
|
|
},
|
|
_ => false,
|
|
}
|
|
},
|
|
_ => false,
|
|
}
|
|
},
|
|
Some(cards_str) if *cards_str == "cards" => {
|
|
true
|
|
},
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
impl Drop for IntelHDA {
|
|
fn drop(&mut self) {
|
|
print!("IHDA: Deallocating IHDA driver.\n");
|
|
|
|
}
|
|
}
|
|
|
|
impl SchemeBlockMut for IntelHDA {
|
|
fn open(&mut self, _path: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result<Option<usize>> {
|
|
//let path: Vec<&str>;
|
|
/*
|
|
match str::from_utf8(_path) {
|
|
Ok(p) => {
|
|
path = p.split("/").collect();
|
|
if !self.validate_path(&path) {
|
|
return Err(Error::new(EINVAL));
|
|
|
|
},
|
|
Err(_) => {return Err(Error::new(EINVAL));},
|
|
}*/
|
|
|
|
// TODO:
|
|
if uid == 0 {
|
|
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
|
self.handles.lock().insert(id, Handle::Todo);
|
|
Ok(Some(id))
|
|
} else {
|
|
Err(Error::new(EACCES))
|
|
}
|
|
}
|
|
|
|
fn write(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
|
|
let index = {
|
|
let mut handles = self.handles.lock();
|
|
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
|
0
|
|
};
|
|
|
|
//print!("Int count: {}\n", self.int_counter);
|
|
|
|
self.write_to_output(index, buf)
|
|
}
|
|
|
|
fn seek(&mut self, id: usize, pos: usize, whence: usize) -> Result<Option<usize>> {
|
|
let mut handles = self.handles.lock();
|
|
match *handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
|
Handle::StrBuf(ref mut strbuf, ref mut size) => {
|
|
let len = strbuf.len() as usize;
|
|
*size = match whence {
|
|
SEEK_SET => cmp::min(len, pos),
|
|
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
|
|
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
|
|
_ => return Err(Error::new(EINVAL))
|
|
};
|
|
Ok(Some(*size))
|
|
},
|
|
|
|
_ => Err(Error::new(EINVAL)),
|
|
}
|
|
}
|
|
|
|
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
|
let mut handles = self.handles.lock();
|
|
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
|
|
|
let mut i = 0;
|
|
let scheme_path = b"hda:";
|
|
while i < buf.len() && i < scheme_path.len() {
|
|
buf[i] = scheme_path[i];
|
|
i += 1;
|
|
}
|
|
Ok(Some(i))
|
|
}
|
|
|
|
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
|
let mut handles = self.handles.lock();
|
|
handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(Some(0)))
|
|
}
|
|
}
|