1
2 #include <cstring>
3 #include <cerrno>
4
5 #include <stdexcept>
6 #include <sys/mman.h>
7 #include <sys/ioctl.h>
8 #include <xf86drm.h>
9 #include <xf86drmMode.h>
10 #include <linux/dma-buf.h>
11
12 #include <kms++/kms++.h>
13
14 using namespace std;
15
16 namespace kms
17 {
DmabufFramebuffer(Card & card,uint32_t width,uint32_t height,const string & format,vector<int> fds,vector<uint32_t> pitches,vector<uint32_t> offsets,vector<uint64_t> modifiers)18 DmabufFramebuffer::DmabufFramebuffer(Card& card, uint32_t width, uint32_t height, const string& format,
19 vector<int> fds, vector<uint32_t> pitches, vector<uint32_t> offsets, vector<uint64_t> modifiers)
20 : DmabufFramebuffer(card, width, height, FourCCToPixelFormat(format), fds, pitches, offsets, modifiers)
21 {
22 }
23
DmabufFramebuffer(Card & card,uint32_t width,uint32_t height,PixelFormat format,vector<int> fds,vector<uint32_t> pitches,vector<uint32_t> offsets,vector<uint64_t> modifiers)24 DmabufFramebuffer::DmabufFramebuffer(Card& card, uint32_t width, uint32_t height, PixelFormat format,
25 vector<int> fds, vector<uint32_t> pitches, vector<uint32_t> offsets, vector<uint64_t> modifiers)
26 : Framebuffer(card, width, height)
27 {
28 int r;
29
30 m_format = format;
31
32 const PixelFormatInfo& format_info = get_pixel_format_info(format);
33
34 m_num_planes = format_info.num_planes;
35
36 if (fds.size() != m_num_planes || pitches.size() != m_num_planes || offsets.size() != m_num_planes)
37 throw std::invalid_argument("the size of fds, pitches and offsets has to match number of planes");
38
39 for (int i = 0; i < format_info.num_planes; ++i) {
40 FramebufferPlane& plane = m_planes.at(i);
41
42 plane.prime_fd = fds[i];
43
44 r = drmPrimeFDToHandle(card.fd(), fds[i], &plane.handle);
45 if (r)
46 throw invalid_argument(string("drmPrimeFDToHandle: ") + strerror(errno));
47
48 plane.stride = pitches[i];
49 plane.offset = offsets[i];
50 plane.modifier = modifiers.empty() ? 0 : modifiers[i];
51 plane.size = plane.stride * height;
52 plane.map = 0;
53 }
54
55 uint32_t id;
56 uint32_t bo_handles[4] = { m_planes[0].handle, m_planes[1].handle, m_planes[2].handle, m_planes[3].handle };
57 pitches.resize(4);
58 offsets.resize(4);
59
60 if (modifiers.empty()) {
61 r = drmModeAddFB2(card.fd(), width, height, (uint32_t)format,
62 bo_handles, pitches.data(), offsets.data(), &id, 0);
63 if (r)
64 throw invalid_argument(string("drmModeAddFB2 failed: ") + strerror(errno));
65 } else {
66 modifiers.resize(4);
67 r = drmModeAddFB2WithModifiers(card.fd(), width, height, (uint32_t)format,
68 bo_handles, pitches.data(), offsets.data(), modifiers.data(), &id, DRM_MODE_FB_MODIFIERS);
69 if (r)
70 throw invalid_argument(string("drmModeAddFB2WithModifiers failed: ") + strerror(errno));
71 }
72
73 set_id(id);
74 }
75
~DmabufFramebuffer()76 DmabufFramebuffer::~DmabufFramebuffer()
77 {
78 drmModeRmFB(card().fd(), id());
79 }
80
map(unsigned plane)81 uint8_t* DmabufFramebuffer::map(unsigned plane)
82 {
83 FramebufferPlane& p = m_planes.at(plane);
84
85 if (p.map)
86 return p.map;
87
88 p.map = (uint8_t*)mmap(0, p.size, PROT_READ | PROT_WRITE, MAP_SHARED,
89 p.prime_fd, 0);
90 if (p.map == MAP_FAILED)
91 throw invalid_argument(string("mmap failed: ") + strerror(errno));
92
93 return p.map;
94 }
95
prime_fd(unsigned plane)96 int DmabufFramebuffer::prime_fd(unsigned plane)
97 {
98 FramebufferPlane& p = m_planes.at(plane);
99
100 return p.prime_fd;
101 }
102
begin_cpu_access(CpuAccess access)103 void DmabufFramebuffer::begin_cpu_access(CpuAccess access)
104 {
105 if (m_sync_flags != 0)
106 throw runtime_error("begin_cpu sync already started");
107
108 switch (access) {
109 case CpuAccess::Read:
110 m_sync_flags = DMA_BUF_SYNC_READ;
111 break;
112 case CpuAccess::Write:
113 m_sync_flags = DMA_BUF_SYNC_WRITE;
114 break;
115 case CpuAccess::ReadWrite:
116 m_sync_flags = DMA_BUF_SYNC_RW;
117 break;
118 }
119
120 dma_buf_sync dbs{
121 .flags = DMA_BUF_SYNC_START | m_sync_flags
122 };
123
124 for (uint32_t p = 0; p < m_num_planes; ++p) {
125 int r = ioctl(prime_fd(p), DMA_BUF_IOCTL_SYNC, &dbs);
126 if (r)
127 throw runtime_error("DMA_BUF_IOCTL_SYNC failed");
128 }
129 }
130
end_cpu_access()131 void DmabufFramebuffer::end_cpu_access()
132 {
133 if (m_sync_flags == 0)
134 throw runtime_error("begin_cpu sync not started");
135
136 dma_buf_sync dbs{
137 .flags = DMA_BUF_SYNC_END | m_sync_flags
138 };
139
140 for (uint32_t p = 0; p < m_num_planes; ++p) {
141 int r = ioctl(prime_fd(p), DMA_BUF_IOCTL_SYNC, &dbs);
142 if (r)
143 throw runtime_error("DMA_BUF_IOCTL_SYNC failed");
144 }
145
146 m_sync_flags = 0;
147 }
148
149 } // namespace kms
150