Serenity Operating System
at hosted 275 lines 6.7 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/Memory.h> 28#include <AK/StringView.h> 29#include <Kernel/Devices/SB16.h> 30#include <Kernel/Thread.h> 31#include <Kernel/VM/AnonymousVMObject.h> 32#include <Kernel/VM/MemoryManager.h> 33#include <LibBareMetal/IO.h> 34 35//#define SB16_DEBUG 36 37namespace Kernel { 38#define SB16_DEFAULT_IRQ 5 39 40enum class SampleFormat : u8 { 41 Signed = 0x10, 42 Stereo = 0x20, 43}; 44 45const u16 DSP_READ = 0x22A; 46const u16 DSP_WRITE = 0x22C; 47const u16 DSP_STATUS = 0x22E; 48const u16 DSP_R_ACK = 0x22F; 49 50/* Write a value to the DSP write register */ 51void SB16::dsp_write(u8 value) 52{ 53 while (IO::in8(DSP_WRITE) & 0x80) { 54 ; 55 } 56 IO::out8(DSP_WRITE, value); 57} 58 59/* Reads the value of the DSP read register */ 60u8 SB16::dsp_read() 61{ 62 while (!(IO::in8(DSP_STATUS) & 0x80)) { 63 ; 64 } 65 return IO::in8(DSP_READ); 66} 67 68/* Changes the sample rate of sound output */ 69void SB16::set_sample_rate(uint16_t hz) 70{ 71 dsp_write(0x41); // output 72 dsp_write((u8)(hz >> 8)); 73 dsp_write((u8)hz); 74 dsp_write(0x42); // input 75 dsp_write((u8)(hz >> 8)); 76 dsp_write((u8)hz); 77} 78 79static SB16* s_the; 80 81SB16::SB16() 82 : IRQHandler(SB16_DEFAULT_IRQ) 83 , CharacterDevice(42, 42) // ### ? 84{ 85 s_the = this; 86 initialize(); 87} 88 89SB16::~SB16() 90{ 91} 92 93SB16& SB16::the() 94{ 95 return *s_the; 96} 97 98void SB16::initialize() 99{ 100 disable_irq(); 101 102 IO::out8(0x226, 1); 103 IO::delay(); 104 IO::out8(0x226, 0); 105 106 auto data = dsp_read(); 107 if (data != 0xaa) { 108 klog() << "SB16: sb not ready"; 109 return; 110 } 111 112 // Get the version info 113 dsp_write(0xe1); 114 m_major_version = dsp_read(); 115 auto vmin = dsp_read(); 116 117 klog() << "SB16: found version " << m_major_version << "." << vmin; 118 set_irq_register(SB16_DEFAULT_IRQ); 119 klog() << "SB16: IRQ " << get_irq_line(); 120} 121 122void SB16::set_irq_register(u8 irq_number) 123{ 124 u8 bitmask; 125 switch (irq_number) { 126 case 2: 127 bitmask = 0; 128 break; 129 case 5: 130 bitmask = 0b10; 131 break; 132 case 7: 133 bitmask = 0b100; 134 break; 135 case 10: 136 bitmask = 0b1000; 137 break; 138 default: 139 ASSERT_NOT_REACHED(); 140 } 141 IO::out8(0x224, 0x80); 142 IO::out8(0x225, bitmask); 143} 144 145u8 SB16::get_irq_line() 146{ 147 IO::out8(0x224, 0x80); 148 u8 bitmask = IO::in8(0x225); 149 switch (bitmask) { 150 case 0: 151 return 2; 152 case 0b10: 153 return 5; 154 case 0b100: 155 return 7; 156 case 0b1000: 157 return 10; 158 } 159 return bitmask; 160} 161void SB16::set_irq_line(u8 irq_number) 162{ 163 InterruptDisabler disabler; 164 if (irq_number == get_irq_line()) 165 return; 166 set_irq_register(irq_number); 167 change_irq_number(irq_number); 168} 169 170bool SB16::can_read(const FileDescription&) const 171{ 172 return false; 173} 174 175ssize_t SB16::read(FileDescription&, u8*, ssize_t) 176{ 177 return 0; 178} 179 180void SB16::dma_start(uint32_t length) 181{ 182 const auto addr = m_dma_region->vmobject().physical_pages()[0]->paddr().get(); 183 const u8 channel = 5; // 16-bit samples use DMA channel 5 (on the master DMA controller) 184 const u8 mode = 0; 185 186 // Disable the DMA channel 187 IO::out8(0xd4, 4 + (channel % 4)); 188 189 // Clear the byte pointer flip-flop 190 IO::out8(0xd8, 0); 191 192 // Write the DMA mode for the transfer 193 IO::out8(0xd6, (channel % 4) | mode); 194 195 // Write the offset of the buffer 196 u16 offset = (addr / 2) % 65536; 197 IO::out8(0xc4, (u8)offset); 198 IO::out8(0xc4, (u8)(offset >> 8)); 199 200 // Write the transfer length 201 IO::out8(0xc6, (u8)(length - 1)); 202 IO::out8(0xc6, (u8)((length - 1) >> 8)); 203 204 // Write the buffer 205 IO::out8(0x8b, addr >> 16); 206 207 // Enable the DMA channel 208 IO::out8(0xd4, (channel % 4)); 209} 210 211void SB16::handle_irq(const RegisterState&) 212{ 213 // Stop sound output ready for the next block. 214 dsp_write(0xd5); 215 216 IO::in8(DSP_STATUS); // 8 bit interrupt 217 if (m_major_version >= 4) 218 IO::in8(DSP_R_ACK); // 16 bit interrupt 219 220 m_irq_queue.wake_all(); 221} 222 223void SB16::wait_for_irq() 224{ 225 Thread::current->wait_on(m_irq_queue); 226 disable_irq(); 227} 228 229ssize_t SB16::write(FileDescription&, const u8* data, ssize_t length) 230{ 231 if (!m_dma_region) { 232 auto page = MM.allocate_supervisor_physical_page(); 233 auto vmobject = AnonymousVMObject::create_with_physical_page(*page); 234 m_dma_region = MM.allocate_kernel_region_with_vmobject(*vmobject, PAGE_SIZE, "SB16 DMA buffer", Region::Access::Write); 235 } 236 237#ifdef SB16_DEBUG 238 klog() << "SB16: Writing buffer of " << length << " bytes"; 239#endif 240 ASSERT(length <= PAGE_SIZE); 241 const int BLOCK_SIZE = 32 * 1024; 242 if (length > BLOCK_SIZE) { 243 return -ENOSPC; 244 } 245 246 u8 mode = (u8)SampleFormat::Signed | (u8)SampleFormat::Stereo; 247 248 const int sample_rate = 44100; 249 set_sample_rate(sample_rate); 250 memcpy(m_dma_region->vaddr().as_ptr(), data, length); 251 dma_start(length); 252 253 // 16-bit single-cycle output. 254 // FIXME: Implement auto-initialized output. 255 u8 command = 0xb0; 256 257 u16 sample_count = length / sizeof(i16); 258 if (mode & (u8)SampleFormat::Stereo) 259 sample_count /= 2; 260 261 sample_count -= 1; 262 263 cli(); 264 enable_irq(); 265 266 dsp_write(command); 267 dsp_write(mode); 268 dsp_write((u8)sample_count); 269 dsp_write((u8)(sample_count >> 8)); 270 271 wait_for_irq(); 272 return length; 273} 274 275}