Kernel: Add ioctls to select audio device's output pin
This commit is contained in:
parent
da6794c8ce
commit
8f1b314802
|
|
@ -19,6 +19,10 @@ namespace Kernel
|
|||
uint32_t get_channels() const override { return 2; }
|
||||
uint32_t get_sample_rate() const override { return 48000; }
|
||||
|
||||
uint32_t get_total_pins() const override { return 1; }
|
||||
uint32_t get_current_pin() const override { return 0; }
|
||||
BAN::ErrorOr<void> set_current_pin(uint32_t pin) override { if (pin != 0) return BAN::Error::from_errno(EINVAL); return {}; }
|
||||
|
||||
private:
|
||||
AC97AudioController(PCI::Device& pci_device)
|
||||
: m_pci_device(pci_device)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ namespace Kernel
|
|||
virtual uint32_t get_channels() const = 0;
|
||||
virtual uint32_t get_sample_rate() const = 0;
|
||||
|
||||
virtual uint32_t get_total_pins() const = 0;
|
||||
virtual uint32_t get_current_pin() const = 0;
|
||||
virtual BAN::ErrorOr<void> set_current_pin(uint32_t) = 0;
|
||||
|
||||
bool can_read_impl() const override { return false; }
|
||||
bool can_write_impl() const override { SpinLockGuard _(m_spinlock); return m_sample_data_size < m_sample_data_capacity; }
|
||||
bool has_error_impl() const override { return false; }
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ namespace Kernel
|
|||
uint32_t get_channels() const override { return 2; }
|
||||
uint32_t get_sample_rate() const override { return 48000; }
|
||||
|
||||
uint32_t get_total_pins() const override;
|
||||
uint32_t get_current_pin() const override;
|
||||
BAN::ErrorOr<void> set_current_pin(uint32_t) override;
|
||||
|
||||
void handle_new_data() override;
|
||||
|
||||
private:
|
||||
|
|
@ -33,7 +37,9 @@ namespace Kernel
|
|||
BAN::ErrorOr<void> initialize();
|
||||
BAN::ErrorOr<void> initialize_stream();
|
||||
BAN::ErrorOr<void> initialize_output();
|
||||
|
||||
BAN::ErrorOr<void> enable_output_path(uint8_t index);
|
||||
BAN::ErrorOr<void> disable_output_path(uint8_t index);
|
||||
|
||||
void reset_stream();
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,15 @@ namespace Kernel
|
|||
m_sample_data_size = 0;
|
||||
return 0;
|
||||
}
|
||||
case SND_GET_TOTAL_PINS:
|
||||
*static_cast<uint32_t*>(arg) = get_total_pins();
|
||||
return 0;
|
||||
case SND_GET_PIN:
|
||||
*static_cast<uint32_t*>(arg) = get_current_pin();
|
||||
return 0;
|
||||
case SND_SET_PIN:
|
||||
TRY(set_current_pin(*static_cast<uint32_t*>(arg)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return CharacterDevice::ioctl_impl(cmd, arg);
|
||||
|
|
|
|||
|
|
@ -84,6 +84,71 @@ namespace Kernel
|
|||
return {};
|
||||
}
|
||||
|
||||
uint32_t HDAudioFunctionGroup::get_total_pins() const
|
||||
{
|
||||
uint32_t count = 0;
|
||||
for (const auto& widget : m_afg_node.widgets)
|
||||
if (widget.type == HDAudio::AFGWidget::Type::PinComplex && widget.pin_complex.output)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t HDAudioFunctionGroup::get_current_pin() const
|
||||
{
|
||||
const auto current_id = m_output_paths[m_output_path_index].front()->id;
|
||||
|
||||
uint32_t pin = 0;
|
||||
for (const auto& widget : m_afg_node.widgets)
|
||||
{
|
||||
if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output)
|
||||
continue;
|
||||
if (widget.id == current_id)
|
||||
return pin;
|
||||
pin++;
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> HDAudioFunctionGroup::set_current_pin(uint32_t pin)
|
||||
{
|
||||
uint32_t pin_id = 0;
|
||||
for (const auto& widget : m_afg_node.widgets)
|
||||
{
|
||||
if (widget.type != HDAudio::AFGWidget::Type::PinComplex || !widget.pin_complex.output)
|
||||
continue;
|
||||
if (pin-- > 0)
|
||||
continue;
|
||||
pin_id = widget.id;
|
||||
break;
|
||||
}
|
||||
|
||||
if (auto ret = disable_output_path(m_output_path_index); ret.is_error())
|
||||
dwarnln("failed to disable old output path {}", ret.error());
|
||||
|
||||
for (size_t i = 0; i < m_output_paths.size(); i++)
|
||||
{
|
||||
if (m_output_paths[i].front()->id != pin_id)
|
||||
continue;
|
||||
|
||||
if (auto ret = enable_output_path(i); !ret.is_error())
|
||||
{
|
||||
if (ret.error().get_error_code() == ENOTSUP)
|
||||
continue;
|
||||
dwarnln("path {} not supported", i);
|
||||
return ret.release_error();
|
||||
}
|
||||
|
||||
dprintln("set output widget to {}", pin_id);
|
||||
m_output_path_index = i;
|
||||
return {};
|
||||
}
|
||||
|
||||
dwarnln("failed to set output widget to {}", pin_id);
|
||||
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
size_t HDAudioFunctionGroup::bdl_offset() const
|
||||
{
|
||||
const size_t bdl_entry_bytes = m_bdl_entry_sample_frames * get_channels() * sizeof(uint16_t);
|
||||
|
|
@ -328,6 +393,53 @@ namespace Kernel
|
|||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> HDAudioFunctionGroup::disable_output_path(uint8_t index)
|
||||
{
|
||||
ASSERT(index < m_output_paths.size());
|
||||
const auto& path = m_output_paths[index];
|
||||
|
||||
for (size_t i = 0; i < path.size(); i++)
|
||||
{
|
||||
// set power state D3
|
||||
TRY(m_controller->send_command({
|
||||
.data = 0x03,
|
||||
.command = 0x705,
|
||||
.node_index = path[i]->id,
|
||||
.codec_address = m_cid,
|
||||
}));
|
||||
|
||||
switch (path[i]->type)
|
||||
{
|
||||
using HDAudio::AFGWidget;
|
||||
|
||||
case AFGWidget::Type::OutputConverter:
|
||||
break;
|
||||
|
||||
case AFGWidget::Type::PinComplex:
|
||||
// disable output and H-Phn
|
||||
TRY(m_controller->send_command({
|
||||
.data = 0x00,
|
||||
.command = 0x707,
|
||||
.node_index = path[i]->id,
|
||||
.codec_address = m_cid,
|
||||
}));
|
||||
// disable EAPD
|
||||
TRY(m_controller->send_command({
|
||||
.data = 0x00,
|
||||
.command = 0x70C,
|
||||
.node_index = path[i]->id,
|
||||
.codec_address = m_cid,
|
||||
}));
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> HDAudioFunctionGroup::recurse_output_paths(const HDAudio::AFGWidget& widget, BAN::Vector<const HDAudio::AFGWidget*>& path)
|
||||
{
|
||||
// cycle "detection"
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ struct winsize
|
|||
#define SND_GET_SAMPLE_RATE 61 /* stores sample rate to uint32_t argument */
|
||||
#define SND_RESET_BUFFER 62 /* stores the size of internal buffer to uint32_t argument and clears the buffer */
|
||||
#define SND_GET_BUFFERSZ 63 /* stores the size of internal buffer to uint32_t argument */
|
||||
#define SND_GET_TOTAL_PINS 64 /* gets the number of pins on the current device */
|
||||
#define SND_GET_PIN 65 /* gets the currently active pin */
|
||||
#define SND_SET_PIN 66 /* sets the currently active pin */
|
||||
|
||||
int ioctl(int, int, ...);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue