FramebufferDevice.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Checked.h>
  7. #include <Kernel/Debug.h>
  8. #include <Kernel/Graphics/FramebufferDevice.h>
  9. #include <Kernel/Graphics/GraphicsManagement.h>
  10. #include <Kernel/Memory/AnonymousVMObject.h>
  11. #include <Kernel/Memory/MemoryManager.h>
  12. #include <Kernel/Process.h>
  13. #include <Kernel/Sections.h>
  14. #include <LibC/errno_numbers.h>
  15. #include <LibC/sys/ioctl_numbers.h>
  16. #define MAX_RESOLUTION_WIDTH 4096
  17. #define MAX_RESOLUTION_HEIGHT 2160
  18. namespace Kernel {
  19. NonnullRefPtr<FramebufferDevice> FramebufferDevice::create(const GraphicsDevice& adapter, size_t output_port_index, PhysicalAddress paddr, size_t width, size_t height, size_t pitch)
  20. {
  21. auto framebuffer_device_or_error = try_create_device<FramebufferDevice>(adapter, output_port_index, paddr, width, height, pitch);
  22. // FIXME: Find a way to propagate errors
  23. VERIFY(!framebuffer_device_or_error.is_error());
  24. return framebuffer_device_or_error.release_value();
  25. }
  26. KResultOr<Memory::Region*> FramebufferDevice::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared)
  27. {
  28. SpinlockLocker lock(m_activation_lock);
  29. REQUIRE_PROMISE(video);
  30. if (!shared)
  31. return ENODEV;
  32. if (offset != 0)
  33. return ENXIO;
  34. if (range.size() != Memory::page_round_up(framebuffer_size_in_bytes()))
  35. return EOVERFLOW;
  36. m_userspace_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, Memory::page_round_up(framebuffer_size_in_bytes())));
  37. m_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, Memory::page_round_up(framebuffer_size_in_bytes())));
  38. m_swapped_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(Memory::page_round_up(framebuffer_size_in_bytes()), AllocationStrategy::AllocateNow));
  39. m_real_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, Memory::page_round_up(framebuffer_size_in_bytes()), "Framebuffer", Memory::Region::Access::ReadWrite));
  40. m_swapped_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, Memory::page_round_up(framebuffer_size_in_bytes()), "Framebuffer Swap (Blank)", Memory::Region::Access::ReadWrite));
  41. RefPtr<Memory::VMObject> chosen_vmobject;
  42. if (m_graphical_writes_enabled) {
  43. chosen_vmobject = m_real_framebuffer_vmobject;
  44. } else {
  45. chosen_vmobject = m_swapped_framebuffer_vmobject;
  46. }
  47. m_userspace_framebuffer_region = TRY(process.address_space().allocate_region_with_vmobject(
  48. range,
  49. chosen_vmobject.release_nonnull(),
  50. 0,
  51. "Framebuffer",
  52. prot,
  53. shared));
  54. return m_userspace_framebuffer_region;
  55. }
  56. void FramebufferDevice::deactivate_writes()
  57. {
  58. SpinlockLocker lock(m_activation_lock);
  59. if (!m_userspace_framebuffer_region)
  60. return;
  61. memcpy(m_swapped_framebuffer_region->vaddr().as_ptr(), m_real_framebuffer_region->vaddr().as_ptr(), Memory::page_round_up(framebuffer_size_in_bytes()));
  62. auto vmobject = m_swapped_framebuffer_vmobject;
  63. m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
  64. m_userspace_framebuffer_region->remap();
  65. m_graphical_writes_enabled = false;
  66. }
  67. void FramebufferDevice::activate_writes()
  68. {
  69. SpinlockLocker lock(m_activation_lock);
  70. if (!m_userspace_framebuffer_region || !m_real_framebuffer_vmobject)
  71. return;
  72. // restore the image we had in the void area
  73. // FIXME: if we happen to have multiple Framebuffers that are writing to that location
  74. // we will experience glitches...
  75. memcpy(m_real_framebuffer_region->vaddr().as_ptr(), m_swapped_framebuffer_region->vaddr().as_ptr(), Memory::page_round_up(framebuffer_size_in_bytes()));
  76. auto vmobject = m_userspace_real_framebuffer_vmobject;
  77. m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
  78. m_userspace_framebuffer_region->remap();
  79. m_graphical_writes_enabled = true;
  80. }
  81. UNMAP_AFTER_INIT KResult FramebufferDevice::initialize()
  82. {
  83. // FIXME: Would be nice to be able to unify this with mmap above, but this
  84. // function is UNMAP_AFTER_INIT for the time being.
  85. m_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, Memory::page_round_up(framebuffer_size_in_bytes())));
  86. m_swapped_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(Memory::page_round_up(framebuffer_size_in_bytes()), AllocationStrategy::AllocateNow));
  87. m_real_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, Memory::page_round_up(framebuffer_size_in_bytes()), "Framebuffer", Memory::Region::Access::ReadWrite));
  88. m_swapped_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, Memory::page_round_up(framebuffer_size_in_bytes()), "Framebuffer Swap (Blank)", Memory::Region::Access::ReadWrite));
  89. return KSuccess;
  90. }
  91. UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(const GraphicsDevice& adapter, size_t output_port_index, PhysicalAddress addr, size_t width, size_t height, size_t pitch)
  92. : BlockDevice(29, GraphicsManagement::the().allocate_minor_device_number())
  93. , m_framebuffer_address(addr)
  94. , m_framebuffer_pitch(pitch)
  95. , m_framebuffer_width(width)
  96. , m_framebuffer_height(height)
  97. , m_output_port_index(output_port_index)
  98. , m_graphics_adapter(adapter)
  99. {
  100. VERIFY(!m_framebuffer_address.is_null());
  101. VERIFY(m_framebuffer_pitch);
  102. VERIFY(m_framebuffer_width);
  103. VERIFY(m_framebuffer_height);
  104. dbgln("Framebuffer {}: address={}, pitch={}, width={}, height={}", minor(), addr, pitch, width, height);
  105. }
  106. size_t FramebufferDevice::framebuffer_size_in_bytes() const
  107. {
  108. if (m_graphics_adapter->double_framebuffering_capable())
  109. return m_framebuffer_pitch * m_framebuffer_height * 2;
  110. return m_framebuffer_pitch * m_framebuffer_height;
  111. }
  112. KResult FramebufferDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
  113. {
  114. REQUIRE_PROMISE(video);
  115. switch (request) {
  116. case FB_IOCTL_GET_SIZE_IN_BYTES: {
  117. auto user_size = static_ptr_cast<size_t*>(arg);
  118. size_t value = framebuffer_size_in_bytes();
  119. return copy_to_user(user_size, &value);
  120. }
  121. case FB_IOCTL_GET_BUFFER: {
  122. auto user_index = static_ptr_cast<int*>(arg);
  123. int value = m_y_offset == 0 ? 0 : 1;
  124. TRY(copy_to_user(user_index, &value));
  125. if (!m_graphics_adapter->double_framebuffering_capable())
  126. return ENOTIMPL;
  127. return KSuccess;
  128. }
  129. case FB_IOCTL_SET_BUFFER: {
  130. auto buffer = static_cast<int>(arg.ptr());
  131. if (buffer != 0 && buffer != 1)
  132. return EINVAL;
  133. if (!m_graphics_adapter->double_framebuffering_capable())
  134. return ENOTIMPL;
  135. m_graphics_adapter->set_y_offset(m_output_port_index, buffer == 0 ? 0 : m_framebuffer_height);
  136. return KSuccess;
  137. }
  138. case FB_IOCTL_GET_RESOLUTION: {
  139. auto user_resolution = static_ptr_cast<FBResolution*>(arg);
  140. FBResolution resolution {};
  141. resolution.pitch = m_framebuffer_pitch;
  142. resolution.width = m_framebuffer_width;
  143. resolution.height = m_framebuffer_height;
  144. return copy_to_user(user_resolution, &resolution);
  145. }
  146. case FB_IOCTL_SET_RESOLUTION: {
  147. auto user_resolution = static_ptr_cast<FBResolution*>(arg);
  148. FBResolution resolution;
  149. TRY(copy_from_user(&resolution, user_resolution));
  150. if (resolution.width > MAX_RESOLUTION_WIDTH || resolution.height > MAX_RESOLUTION_HEIGHT)
  151. return EINVAL;
  152. if (!m_graphics_adapter->modesetting_capable()) {
  153. resolution.pitch = m_framebuffer_pitch;
  154. resolution.width = m_framebuffer_width;
  155. resolution.height = m_framebuffer_height;
  156. TRY(copy_to_user(user_resolution, &resolution));
  157. return ENOTIMPL;
  158. }
  159. if (!m_graphics_adapter->try_to_set_resolution(m_output_port_index, resolution.width, resolution.height)) {
  160. m_framebuffer_pitch = m_framebuffer_width * sizeof(u32);
  161. dbgln_if(FRAMEBUFFER_DEVICE_DEBUG, "Reverting resolution: [{}x{}]", m_framebuffer_width, m_framebuffer_height);
  162. // Note: We try to revert everything back, and if it doesn't work, just assert.
  163. if (!m_graphics_adapter->try_to_set_resolution(m_output_port_index, m_framebuffer_width, m_framebuffer_height)) {
  164. VERIFY_NOT_REACHED();
  165. }
  166. resolution.pitch = m_framebuffer_pitch;
  167. resolution.width = m_framebuffer_width;
  168. resolution.height = m_framebuffer_height;
  169. TRY(copy_to_user(user_resolution, &resolution));
  170. return EINVAL;
  171. }
  172. m_framebuffer_width = resolution.width;
  173. m_framebuffer_height = resolution.height;
  174. m_framebuffer_pitch = m_framebuffer_width * sizeof(u32);
  175. dbgln_if(FRAMEBUFFER_DEVICE_DEBUG, "New resolution: [{}x{}]", m_framebuffer_width, m_framebuffer_height);
  176. resolution.pitch = m_framebuffer_pitch;
  177. resolution.width = m_framebuffer_width;
  178. resolution.height = m_framebuffer_height;
  179. return copy_to_user(user_resolution, &resolution);
  180. }
  181. case FB_IOCTL_GET_BUFFER_OFFSET: {
  182. auto user_buffer_offset = static_ptr_cast<FBBufferOffset*>(arg);
  183. FBBufferOffset buffer_offset;
  184. TRY(copy_from_user(&buffer_offset, user_buffer_offset));
  185. if (buffer_offset.buffer_index != 0 && buffer_offset.buffer_index != 1)
  186. return EINVAL;
  187. buffer_offset.offset = (size_t)buffer_offset.buffer_index * m_framebuffer_pitch * m_framebuffer_height;
  188. return copy_to_user(user_buffer_offset, &buffer_offset);
  189. }
  190. case FB_IOCTL_FLUSH_BUFFERS:
  191. return ENOTSUP;
  192. default:
  193. return EINVAL;
  194. };
  195. }
  196. }