Serenity Operating System
at portability 272 lines 6.6 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 <Kernel/Devices/SB16.h> 28#include <Kernel/Thread.h> 29#include <Kernel/VM/AnonymousVMObject.h> 30#include <Kernel/VM/MemoryManager.h> 31#include <LibBareMetal/IO.h> 32 33//#define SB16_DEBUG 34 35namespace Kernel { 36#define SB16_DEFAULT_IRQ 5 37 38enum class SampleFormat : u8 { 39 Signed = 0x10, 40 Stereo = 0x20, 41}; 42 43const u16 DSP_READ = 0x22A; 44const u16 DSP_WRITE = 0x22C; 45const u16 DSP_STATUS = 0x22E; 46const u16 DSP_R_ACK = 0x22F; 47 48/* Write a value to the DSP write register */ 49void SB16::dsp_write(u8 value) 50{ 51 while (IO::in8(DSP_WRITE) & 0x80) { 52 ; 53 } 54 IO::out8(DSP_WRITE, value); 55} 56 57/* Reads the value of the DSP read register */ 58u8 SB16::dsp_read() 59{ 60 while (!(IO::in8(DSP_STATUS) & 0x80)) { 61 ; 62 } 63 return IO::in8(DSP_READ); 64} 65 66/* Changes the sample rate of sound output */ 67void SB16::set_sample_rate(uint16_t hz) 68{ 69 dsp_write(0x41); // output 70 dsp_write((u8)(hz >> 8)); 71 dsp_write((u8)hz); 72 dsp_write(0x42); // input 73 dsp_write((u8)(hz >> 8)); 74 dsp_write((u8)hz); 75} 76 77static SB16* s_the; 78 79SB16::SB16() 80 : IRQHandler(SB16_DEFAULT_IRQ) 81 , CharacterDevice(42, 42) // ### ? 82{ 83 s_the = this; 84 initialize(); 85} 86 87SB16::~SB16() 88{ 89} 90 91SB16& SB16::the() 92{ 93 return *s_the; 94} 95 96void SB16::initialize() 97{ 98 disable_irq(); 99 100 IO::out8(0x226, 1); 101 IO::delay(); 102 IO::out8(0x226, 0); 103 104 auto data = dsp_read(); 105 if (data != 0xaa) { 106 kprintf("SB16: sb not ready"); 107 return; 108 } 109 110 // Get the version info 111 dsp_write(0xe1); 112 m_major_version = dsp_read(); 113 auto vmin = dsp_read(); 114 115 kprintf("SB16: found version %d.%d\n", m_major_version, vmin); 116 set_irq_register(SB16_DEFAULT_IRQ); 117 kprintf("SB16: IRQ %d\n", get_irq_line()); 118} 119 120void SB16::set_irq_register(u8 irq_number) 121{ 122 u8 bitmask; 123 switch (irq_number) { 124 case 2: 125 bitmask = 0; 126 break; 127 case 5: 128 bitmask = 0b10; 129 break; 130 case 7: 131 bitmask = 0b100; 132 break; 133 case 10: 134 bitmask = 0b1000; 135 break; 136 default: 137 ASSERT_NOT_REACHED(); 138 } 139 IO::out8(0x224, 0x80); 140 IO::out8(0x225, bitmask); 141} 142 143u8 SB16::get_irq_line() 144{ 145 IO::out8(0x224, 0x80); 146 u8 bitmask = IO::in8(0x225); 147 switch (bitmask) { 148 case 0: 149 return 2; 150 case 0b10: 151 return 5; 152 case 0b100: 153 return 7; 154 case 0b1000: 155 return 10; 156 } 157 return bitmask; 158} 159void SB16::set_irq_line(u8 irq_number) 160{ 161 InterruptDisabler disabler; 162 if (irq_number == get_irq_line()) 163 return; 164 set_irq_register(irq_number); 165 change_irq_number(irq_number); 166} 167 168bool SB16::can_read(const FileDescription&) const 169{ 170 return false; 171} 172 173ssize_t SB16::read(FileDescription&, u8*, ssize_t) 174{ 175 return 0; 176} 177 178void SB16::dma_start(uint32_t length) 179{ 180 const auto addr = m_dma_region->vmobject().physical_pages()[0]->paddr().get(); 181 const u8 channel = 5; // 16-bit samples use DMA channel 5 (on the master DMA controller) 182 const u8 mode = 0; 183 184 // Disable the DMA channel 185 IO::out8(0xd4, 4 + (channel % 4)); 186 187 // Clear the byte pointer flip-flop 188 IO::out8(0xd8, 0); 189 190 // Write the DMA mode for the transfer 191 IO::out8(0xd6, (channel % 4) | mode); 192 193 // Write the offset of the buffer 194 u16 offset = (addr / 2) % 65536; 195 IO::out8(0xc4, (u8)offset); 196 IO::out8(0xc4, (u8)(offset >> 8)); 197 198 // Write the transfer length 199 IO::out8(0xc6, (u8)(length - 1)); 200 IO::out8(0xc6, (u8)((length - 1) >> 8)); 201 202 // Write the buffer 203 IO::out8(0x8b, addr >> 16); 204 205 // Enable the DMA channel 206 IO::out8(0xd4, (channel % 4)); 207} 208 209void SB16::handle_irq(RegisterState&) 210{ 211 // Stop sound output ready for the next block. 212 dsp_write(0xd5); 213 214 IO::in8(DSP_STATUS); // 8 bit interrupt 215 if (m_major_version >= 4) 216 IO::in8(DSP_R_ACK); // 16 bit interrupt 217 218 m_irq_queue.wake_all(); 219} 220 221void SB16::wait_for_irq() 222{ 223 cli(); 224 enable_irq(); 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 kprintf("SB16: Writing buffer of %d bytes\n", length); 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 dsp_write(command); 264 dsp_write(mode); 265 dsp_write((u8)sample_count); 266 dsp_write((u8)(sample_count >> 8)); 267 268 wait_for_irq(); 269 return length; 270} 271 272}