From 009828beac9bfe9c36d336a4de0d297f90aece52 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 28 Sep 2015 01:13:34 +0300 Subject: Initial version --- .gitignore | 6 + CMakeLists.txt | 25 ++++ LICENSE | 8 ++ db/CMakeLists.txt | 6 + db/db.cpp | 309 +++++++++++++++++++++++++++++++++++++++++++++ libkms++/CMakeLists.txt | 7 + libkms++/atomicreq.cpp | 64 ++++++++++ libkms++/atomicreq.h | 31 +++++ libkms++/card.cpp | 198 +++++++++++++++++++++++++++++ libkms++/card.h | 49 +++++++ libkms++/connector.cpp | 111 ++++++++++++++++ libkms++/connector.h | 49 +++++++ libkms++/crtc.cpp | 74 +++++++++++ libkms++/crtc.h | 35 +++++ libkms++/decls.h | 15 +++ libkms++/drmobject.cpp | 69 ++++++++++ libkms++/drmobject.h | 44 +++++++ libkms++/encoder.cpp | 63 +++++++++ libkms++/encoder.h | 25 ++++ libkms++/framebuffer.cpp | 167 ++++++++++++++++++++++++ libkms++/framebuffer.h | 44 +++++++ libkms++/helpers.cpp | 23 ++++ libkms++/helpers.h | 12 ++ libkms++/kms++.h | 10 ++ libkms++/plane.cpp | 64 ++++++++++ libkms++/plane.h | 32 +++++ libkms++/property.cpp | 36 ++++++ libkms++/property.h | 23 ++++ libkms++/utils/color.cpp | 67 ++++++++++ libkms++/utils/color.h | 48 +++++++ libkms++/utils/conv.cpp | 138 ++++++++++++++++++++ libkms++/utils/conv.h | 8 ++ libkms++/utils/testpat.cpp | 162 ++++++++++++++++++++++++ libkms++/utils/testpat.h | 6 + lua/CMakeLists.txt | 20 +++ lua/luakms.i | 20 +++ lua/test.lua | 21 +++ py/CMakeLists.txt | 26 ++++ py/functest.py | 19 +++ py/pykms.i | 20 +++ py/test.py | 19 +++ test.h | 15 +++ testpat/CMakeLists.txt | 6 + testpat/testpat.cpp | 88 +++++++++++++ 44 files changed, 2282 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 db/CMakeLists.txt create mode 100644 db/db.cpp create mode 100644 libkms++/CMakeLists.txt create mode 100644 libkms++/atomicreq.cpp create mode 100644 libkms++/atomicreq.h create mode 100644 libkms++/card.cpp create mode 100644 libkms++/card.h create mode 100644 libkms++/connector.cpp create mode 100644 libkms++/connector.h create mode 100644 libkms++/crtc.cpp create mode 100644 libkms++/crtc.h create mode 100644 libkms++/decls.h create mode 100644 libkms++/drmobject.cpp create mode 100644 libkms++/drmobject.h create mode 100644 libkms++/encoder.cpp create mode 100644 libkms++/encoder.h create mode 100644 libkms++/framebuffer.cpp create mode 100644 libkms++/framebuffer.h create mode 100644 libkms++/helpers.cpp create mode 100644 libkms++/helpers.h create mode 100644 libkms++/kms++.h create mode 100644 libkms++/plane.cpp create mode 100644 libkms++/plane.h create mode 100644 libkms++/property.cpp create mode 100644 libkms++/property.h create mode 100644 libkms++/utils/color.cpp create mode 100644 libkms++/utils/color.h create mode 100644 libkms++/utils/conv.cpp create mode 100644 libkms++/utils/conv.h create mode 100644 libkms++/utils/testpat.cpp create mode 100644 libkms++/utils/testpat.h create mode 100644 lua/CMakeLists.txt create mode 100644 lua/luakms.i create mode 100755 lua/test.lua create mode 100644 py/CMakeLists.txt create mode 100755 py/functest.py create mode 100644 py/pykms.i create mode 100755 py/test.py create mode 100644 test.h create mode 100644 testpat/CMakeLists.txt create mode 100644 testpat/testpat.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3565c82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.swp +*.a +*.so +build +*.txt.user + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..060dd7b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.0) +project(kms++) + +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE Release CACHE STRING + "Choose the type of build, options are: Debug, Release." + FORCE) +ENDIF(NOT CMAKE_BUILD_TYPE) + + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") + +set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(LIBDRM libdrm REQUIRED) + +enable_testing() + +add_subdirectory(libkms++) +add_subdirectory(testpat) +add_subdirectory(db) +add_subdirectory(py) +add_subdirectory(lua) + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4df798d --- /dev/null +++ b/LICENSE @@ -0,0 +1,8 @@ +libkms++ and test tools + +Copyright 2015 Tomi Valkeinen + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + diff --git a/db/CMakeLists.txt b/db/CMakeLists.txt new file mode 100644 index 0000000..91ab888 --- /dev/null +++ b/db/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(${LIBDRM_INCLUDE_DIRS}) +link_directories(${LIBDRM_LIBRARY_DIRS}) + +add_executable (db db.cpp) + +target_link_libraries(db kms++ ${LIBDRM_LIBRARIES}) diff --git a/db/db.cpp b/db/db.cpp new file mode 100644 index 0000000..d119078 --- /dev/null +++ b/db/db.cpp @@ -0,0 +1,309 @@ +#include +#include + +#include +#include +#include + +#include "kms++.h" +#include "utils/color.h" + +#include "../test.h" + +using namespace std; +using namespace kms; + +static void draw_color_bar(Framebuffer& buf, int old_xpos, int xpos, int width); + +static void main_loop(Card& card); + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +struct Output +{ + Connector* connector; + Crtc* crtc; + Framebuffer* fbs[2]; + + int front_buf; + int bar_xpos; +}; + +static void do_flip(Output* out); + +int main() +{ + Card card; + + if (card.master() == false) + printf("Not DRM master, modeset may fail\n"); + + //card.print_short(); + + vector outputs; + + for (auto conn : card.get_connectors()) + { + if (conn->connected() == false) + continue; + + auto mode = conn->get_default_mode(); + + Crtc* crtc = conn->get_current_crtc(); + if (!crtc) { + for (auto c : conn->get_possible_crtcs()) { + if (find_if(outputs.begin(), outputs.end(), [c](Output o) { return o.crtc == c; }) == outputs.end()) { + crtc = c; + break; + } + } + } + + if (!crtc) { + printf("failed to find crtc\n"); + return -1; + } + + auto fb1 = new Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); + auto fb2 = new Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); + + printf("conn %u, crtc %u, fb1 %u, fb2 %u\n", conn->id(), crtc->id(), fb1->id(), fb2->id()); + + Output output = { }; + output.connector = conn; + output.crtc = crtc; + output.fbs[0] = fb1; + output.fbs[1] = fb2; + outputs.push_back(output); + } + + for(auto& out : outputs) { + auto conn = out.connector; + auto crtc = out.crtc; + + auto mode = conn->get_default_mode(); + int r = crtc->set_mode(conn, *out.fbs[0], mode); + ASSERT(r == 0); + } + + for(auto& out : outputs) + do_flip(&out); + + main_loop(card); + + for(auto& out : outputs) { + delete out.fbs[0]; + delete out.fbs[1]; + } +} + +static void do_flip(Output* out) +{ + const int bar_width = 20; + const int bar_speed = 8; + + auto crtc = out->crtc; + auto fb = out->fbs[(out->front_buf + 1) % 2]; + + ASSERT(crtc); + ASSERT(fb); + + int current_xpos = out->bar_xpos; + int old_xpos = (current_xpos + (fb->width() - bar_width - bar_speed)) % (fb->width() - bar_width); + int new_xpos = (current_xpos + bar_speed) % (fb->width() - bar_width); + + draw_color_bar(*fb, old_xpos, new_xpos, bar_width); + + out->bar_xpos = new_xpos; + + auto& card = crtc->card(); + + if (card.has_atomic()) { + int r; + + AtomicReq ctx(card); + + // XXX + //ctx.add(plane, card.get_prop("CRTC_X"), 50); + //ctx.add(plane, card.get_prop("CRTC_Y"), 50); + + r = ctx.test(); + ASSERT(r == 0); + + r = ctx.commit(); + ASSERT(r == 0); + } else { + int r = drmModePageFlip(card.fd(), crtc->id(), fb->id(), DRM_MODE_PAGE_FLIP_EVENT, out); + ASSERT(r == 0); + } +} + +static void page_flip_handler(int fd, unsigned int frame, + unsigned int sec, unsigned int usec, + void *data) +{ + //printf("flip event %d, %d, %u, %u, %p\n", fd, frame, sec, usec, data); + + auto out = (Output*)data; + + out->front_buf = (out->front_buf + 1) % 2; + + do_flip(out); +} + + +static void main_loop(Card& card) +{ + drmEventContext ev = { + .version = DRM_EVENT_CONTEXT_VERSION, + .vblank_handler = 0, + .page_flip_handler = page_flip_handler, + }; + + fd_set fds; + + FD_ZERO(&fds); + + int fd = card.fd(); + + printf("press enter to exit\n"); + + while (true) { + int r; + + FD_SET(0, &fds); + FD_SET(fd, &fds); + + r = select(fd + 1, &fds, NULL, NULL, NULL); + if (r < 0) { + fprintf(stderr, "select() failed with %d: %m\n", errno); + break; + } else if (FD_ISSET(0, &fds)) { + fprintf(stderr, "exit due to user-input\n"); + break; + } else if (FD_ISSET(fd, &fds)) { + drmHandleEvent(fd, &ev); + } + } +} + + +static const RGB colors32[] = { + RGB(255, 255, 255), + RGB(255, 0, 0), + RGB(255, 255, 255), + RGB(0, 255, 0), + RGB(255, 255, 255), + RGB(0, 0, 255), + RGB(255, 255, 255), + RGB(200, 200, 200), + RGB(255, 255, 255), + RGB(100, 100, 100), + RGB(255, 255, 255), + RGB(50, 50, 50), +}; + +static const uint16_t colors16[] = { + colors32[0].rgb565(), + colors32[1].rgb565(), + colors32[2].rgb565(), + colors32[3].rgb565(), + colors32[4].rgb565(), + colors32[5].rgb565(), + colors32[6].rgb565(), + colors32[7].rgb565(), + colors32[8].rgb565(), + colors32[9].rgb565(), + colors32[10].rgb565(), + colors32[11].rgb565(), +}; + +static void drm_draw_color_bar_rgb888(Framebuffer& buf, int old_xpos, int xpos, int width) +{ + for (unsigned y = 0; y < buf.height(); ++y) { + RGB bcol = colors32[y * ARRAY_SIZE(colors32) / buf.height()]; + uint32_t *line = (uint32_t*)(buf.map(0) + buf.stride(0) * y); + + if (old_xpos >= 0) { + for (int x = old_xpos; x < old_xpos + width; ++x) + line[x] = 0; + } + + for (int x = xpos; x < xpos + width; ++x) + line[x] = bcol.raw; + } +} + +static void drm_draw_color_bar_rgb565(Framebuffer& buf, int old_xpos, int xpos, int width) +{ + static_assert(ARRAY_SIZE(colors32) == ARRAY_SIZE(colors16), "bad colors arrays"); + + for (unsigned y = 0; y < buf.height(); ++y) { + uint16_t bcol = colors16[y * ARRAY_SIZE(colors16) / buf.height()]; + uint16_t *line = (uint16_t*)(buf.map(0) + buf.stride(0) * y); + + if (old_xpos >= 0) { + for (int x = old_xpos; x < old_xpos + width; ++x) + line[x] = 0; + } + + for (int x = xpos; x < xpos + width; ++x) + line[x] = bcol; + } +} + +static void drm_draw_color_bar_semiplanar_yuv(Framebuffer& buf, int old_xpos, int xpos, int width) +{ + const uint8_t colors[] = { + 0xff, + 0x00, + 0xff, + 0x20, + 0xff, + 0x40, + 0xff, + 0x80, + 0xff, + }; + + for (unsigned y = 0; y < buf.height(); ++y) { + unsigned int bcol = colors[y * ARRAY_SIZE(colors) / buf.height()]; + uint8_t *line = (uint8_t*)(buf.map(0) + buf.stride(0) * y); + + if (old_xpos >= 0) { + for (int x = old_xpos; x < old_xpos + width; ++x) + line[x] = 0; + } + + for (int x = xpos; x < xpos + width; ++x) + line[x] = bcol; + } +} + +static void draw_color_bar(Framebuffer& buf, int old_xpos, int xpos, int width) +{ + switch (buf.format()) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + // XXX not right but gets something on the screen + drm_draw_color_bar_semiplanar_yuv(buf, old_xpos, xpos, width); + break; + + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + // XXX not right but gets something on the screen + drm_draw_color_bar_rgb565(buf, old_xpos, xpos, width); + break; + + case DRM_FORMAT_RGB565: + drm_draw_color_bar_rgb565(buf, old_xpos, xpos, width); + break; + + case DRM_FORMAT_XRGB8888: + drm_draw_color_bar_rgb888(buf, old_xpos, xpos, width); + break; + + default: + ASSERT(false); + } +} diff --git a/libkms++/CMakeLists.txt b/libkms++/CMakeLists.txt new file mode 100644 index 0000000..e8d14f7 --- /dev/null +++ b/libkms++/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories(${LIBDRM_INCLUDE_DIRS}) +link_directories(${LIBDRM_LIBRARY_DIRS}) + +file(GLOB SRCS "*.cpp" "*.h" "utils/*.cpp" "utils/*.h") +add_library(kms++ ${SRCS}) + +target_include_directories(kms++ PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/libkms++/atomicreq.cpp b/libkms++/atomicreq.cpp new file mode 100644 index 0000000..3346af9 --- /dev/null +++ b/libkms++/atomicreq.cpp @@ -0,0 +1,64 @@ +#include + +#include +#include + +#include "atomicreq.h" +#include "property.h" +#include "card.h" + +#ifndef DRM_CLIENT_CAP_ATOMIC + +#define DRM_MODE_ATOMIC_TEST_ONLY 0 + +struct _drmModeAtomicReq; +typedef struct _drmModeAtomicReq* drmModeAtomicReqPtr; + +static inline drmModeAtomicReqPtr drmModeAtomicAlloc() { return 0; } +static inline void drmModeAtomicFree(drmModeAtomicReqPtr) { } +static inline int drmModeAtomicAddProperty(drmModeAtomicReqPtr, uint32_t, uint32_t, uint64_t) { return 0; } +static inline int drmModeAtomicCommit(int, drmModeAtomicReqPtr, int, void*) { return 0; } + +#endif // DRM_CLIENT_CAP_ATOMIC + +namespace kms +{ +AtomicReq::AtomicReq(Card& card) + : m_card(card) +{ + assert(card.has_atomic()); + m_req = drmModeAtomicAlloc(); +} + +AtomicReq::~AtomicReq() +{ + drmModeAtomicFree(m_req); +} + +void AtomicReq::add(uint32_t ob_id, uint32_t prop_id, uint64_t value) +{ + int r = drmModeAtomicAddProperty(m_req, ob_id, prop_id, value); + if (r <= 0) + throw std::invalid_argument("foo"); +} + +void AtomicReq::add(DrmObject *ob, Property *prop, uint64_t value) +{ + add(ob->id(), prop->id(), value); +} + +int AtomicReq::test() +{ + uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY; + + return drmModeAtomicCommit(m_card.fd(), m_req, flags, 0); +} + +int AtomicReq::commit() +{ + uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT; + void* data = 0; + + return drmModeAtomicCommit(m_card.fd(), m_req, flags, data); +} +} diff --git a/libkms++/atomicreq.h b/libkms++/atomicreq.h new file mode 100644 index 0000000..9a8a748 --- /dev/null +++ b/libkms++/atomicreq.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +struct _drmModeAtomicReq; + +#include "decls.h" + +namespace kms +{ +class AtomicReq +{ +public: + AtomicReq(Card& card); + ~AtomicReq(); + + AtomicReq(const AtomicReq& other) = delete; + AtomicReq& operator=(const AtomicReq& other) = delete; + + void add(uint32_t ob_id, uint32_t prop_id, uint64_t value); + void add(DrmObject *ob, Property *prop, uint64_t value); + + int test(); + int commit(); + +private: + Card& m_card; + _drmModeAtomicReq* m_req; +}; + +} diff --git a/libkms++/card.cpp b/libkms++/card.cpp new file mode 100644 index 0000000..2ba350d --- /dev/null +++ b/libkms++/card.cpp @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "kms++.h" + +#ifndef DRM_CLIENT_CAP_ATOMIC +#define DRM_CLIENT_CAP_ATOMIC 3 +#endif + +namespace kms +{ + +Card::Card() +{ + const char *card = "/dev/dri/card0"; + + int fd = open(card, O_RDWR | O_CLOEXEC); + if (fd < 0) + throw std::invalid_argument("foo"); + m_fd = fd; + + int r; + + r = drmSetMaster(fd); + m_master = r == 0; + + r = drmSetClientCap(m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + if (r) + throw std::invalid_argument("foo"); + + r = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1); + m_has_atomic = r == 0; + + uint64_t has_dumb; + r = drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb); + if (r || !has_dumb) + throw std::invalid_argument("foo"); + + auto res = drmModeGetResources(m_fd); + if (!res) + throw std::invalid_argument("foo"); + + for (int i = 0; i < res->count_connectors; ++i) { + uint32_t id = res->connectors[i]; + m_obmap[id] = new Connector(*this, id, i); + } + + for (int i = 0; i < res->count_crtcs; ++i) { + uint32_t id = res->crtcs[i]; + m_obmap[id] = new Crtc(*this, id, i); + } + + for (int i = 0; i < res->count_encoders; ++i) { + uint32_t id = res->encoders[i]; + m_obmap[id] = new Encoder(*this, id); + } + + drmModeFreeResources(res); + + auto planeRes = drmModeGetPlaneResources(m_fd); + + for (uint i = 0; i < planeRes->count_planes; ++i) { + uint32_t id = planeRes->planes[i]; + m_obmap[id] = new Plane(*this, id); + } + + drmModeFreePlaneResources(planeRes); + + // collect all possible props + for (auto ob : get_objects()) { + auto props = drmModeObjectGetProperties(m_fd, ob->id(), ob->object_type()); + + if (props == nullptr) + continue; + + for (unsigned i = 0; i < props->count_props; ++i) { + uint32_t prop_id = props->props[i]; + + if (m_obmap.find(prop_id) == m_obmap.end()) + m_obmap[prop_id] = new Property(*this, prop_id); + } + + drmModeFreeObjectProperties(props); + } + + for (auto pair : m_obmap) + pair.second->setup(); +} + +Card::~Card() +{ + for (auto pair : m_obmap) + delete pair.second; + + close(m_fd); +} + +template static void print_obs(const std::map& obmap) +{ + for (auto pair : obmap) { + auto ob = pair.second; + if (dynamic_cast(ob)) { + ob->print_short(); + //ob->print_props(); + } + } +} + +void Card::print_short() const +{ + print_obs(m_obmap); + print_obs(m_obmap); + print_obs(m_obmap); + print_obs(m_obmap); +} + +Property* Card::get_prop(const char *name) const +{ + for (auto pair : m_obmap) { + auto prop = dynamic_cast(pair.second); + if (!prop) + continue; + + if (strcmp(name, prop->name()) == 0) + return prop; + } + + throw std::invalid_argument("foo"); +} + +Connector* Card::get_first_connected_connector() const +{ + for(auto pair : m_obmap) { + auto c = dynamic_cast(pair.second); + + if (c && c->connected()) + return c; + } + + throw std::invalid_argument("no connected connectors"); +} + +DrmObject* Card::get_object(uint32_t id) const +{ + return m_obmap.at(id); +} + +std::vector Card::get_connectors() const +{ + std::vector v; + for(auto pair : m_obmap) { + auto p = dynamic_cast(pair.second); + if (p) + v.push_back(p); + } + return v; +} + +std::vector Card::get_planes() const +{ + std::vector v; + for(auto pair : m_obmap) { + auto p = dynamic_cast(pair.second); + if (p) + v.push_back(p); + } + return v; +} + +std::vector Card::get_objects() const +{ + std::vector v; + for(auto pair : m_obmap) + v.push_back(pair.second); + return v; +} + +Crtc* Card::get_crtc_by_index(uint32_t idx) const +{ + for(auto pair : m_obmap) { + auto crtc = dynamic_cast(pair.second); + if (crtc && crtc->idx() == idx) + return crtc; + } + throw std::invalid_argument("fob"); +} + +Crtc* Card::get_crtc(uint32_t id) const { return dynamic_cast(get_object(id)); } +Encoder* Card::get_encoder(uint32_t id) const { return dynamic_cast(get_object(id)); } +Property* Card::get_prop(uint32_t id) const { return dynamic_cast(get_object(id)); } +} diff --git a/libkms++/card.h b/libkms++/card.h new file mode 100644 index 0000000..fb45d04 --- /dev/null +++ b/libkms++/card.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +#include "decls.h" + +namespace kms +{ + +class Card +{ +public: + Card(); + ~Card(); + + Card(const Card& other) = delete; + Card& operator=(const Card& other) = delete; + + int fd() const { return m_fd; } + + Connector* get_first_connected_connector() const; + + DrmObject* get_object(uint32_t id) const; + Crtc* get_crtc(uint32_t id) const; + Crtc* get_crtc_by_index(uint32_t idx) const; + Encoder* get_encoder(uint32_t id) const; + Property* get_prop(uint32_t id) const; + Property* get_prop(const char *name) const; + + bool master() const { return m_master; } + bool has_atomic() const { return m_has_atomic; } + + void print_short() const; + + std::vector get_connectors() const; + std::vector get_objects() const; + std::vector get_planes() const; + +private: + std::map m_obmap; + + int m_fd; + bool m_master; + + bool m_has_atomic; +}; +} diff --git a/libkms++/connector.cpp b/libkms++/connector.cpp new file mode 100644 index 0000000..ee14af9 --- /dev/null +++ b/libkms++/connector.cpp @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include + +#include "kms++.h" +#include "helpers.h" + +using namespace std; + +#define DEF_CONN(c) [DRM_MODE_CONNECTOR_##c] = #c + +namespace kms +{ + +static const char * connector_names[] = { + DEF_CONN(Unknown), + DEF_CONN(VGA), + DEF_CONN(DVII), + DEF_CONN(DVID), + DEF_CONN(DVIA), + DEF_CONN(Composite), + DEF_CONN(SVIDEO), + DEF_CONN(LVDS), + DEF_CONN(Component), + DEF_CONN(9PinDIN), + DEF_CONN(DisplayPort), + DEF_CONN(HDMIA), + DEF_CONN(HDMIB), + DEF_CONN(TV), + DEF_CONN(eDP), + DEF_CONN(VIRTUAL), + DEF_CONN(DSI), +}; + +static const char *connection_str[] = { + [0] = "", + [DRM_MODE_CONNECTED] = "Connected", + [DRM_MODE_DISCONNECTED] = "Disconnected", + [DRM_MODE_UNKNOWNCONNECTION] = "Unknown", +}; + +struct ConnectorPriv +{ + drmModeConnectorPtr drm_connector; +}; + +Connector::Connector(Card &card, uint32_t id, uint32_t idx) + :DrmObject(card, id, DRM_MODE_OBJECT_CONNECTOR, idx) +{ + m_priv = new ConnectorPriv(); + + m_priv->drm_connector = drmModeGetConnector(this->card().fd(), this->id()); + assert(m_priv->drm_connector); + + auto name = connector_names[m_priv->drm_connector->connector_type]; + m_fullname = std::string(string(name) + std::to_string(m_priv->drm_connector->connector_type_id)); +} + + +Connector::~Connector() +{ + drmModeFreeConnector(m_priv->drm_connector); + delete m_priv; +} + +void Connector::setup() +{ + if (m_priv->drm_connector->encoder_id != 0) { + auto enc = card().get_encoder(m_priv->drm_connector->encoder_id); + if (enc) + m_current_crtc = enc->get_crtc(); + } +} + +void Connector::print_short() const +{ + auto c = m_priv->drm_connector; + + printf("Connector %d, %s, %dx%dmm, %s\n", id(), m_fullname.c_str(), + c->mmWidth, c->mmHeight, connection_str[c->connection]); +} + +Videomode Connector::get_default_mode() const +{ + drmModeModeInfo drmmode = m_priv->drm_connector->modes[0]; + + return drm_mode_to_video_mode(drmmode); +} + +bool Connector::connected() +{ + return m_priv->drm_connector->connection == DRM_MODE_CONNECTED; +} + +vector Connector::get_possible_crtcs() const +{ + vector crtcs; + + for (int i = 0; i < m_priv->drm_connector->count_encoders; ++i) { + auto enc = card().get_encoder(m_priv->drm_connector->encoders[i]); + + auto l = enc->get_possible_crtcs(); + + crtcs.insert(crtcs.end(), l.begin(), l.end()); + } + + return crtcs; +} +} diff --git a/libkms++/connector.h b/libkms++/connector.h new file mode 100644 index 0000000..b7d975c --- /dev/null +++ b/libkms++/connector.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include "drmobject.h" + +namespace kms +{ + +struct ConnectorPriv; + +struct Videomode +{ + uint32_t clock; + uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; + uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + + uint32_t vrefresh; + + uint32_t flags; + uint32_t type; + char name[32]; // XXX +}; + +class Connector : public DrmObject +{ +public: + Connector(Card& card, uint32_t id, uint32_t idx); + ~Connector(); + + void setup(); + + void print_short() const; + + Videomode get_default_mode() const; + + Crtc* get_current_crtc() const { return m_current_crtc; } + std::vector get_possible_crtcs() const; + + bool connected(); + +private: + ConnectorPriv* m_priv; + + std::string m_fullname; + + Crtc* m_current_crtc; +}; +} diff --git a/libkms++/crtc.cpp b/libkms++/crtc.cpp new file mode 100644 index 0000000..e583be8 --- /dev/null +++ b/libkms++/crtc.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include + +#include "kms++.h" +#include "helpers.h" + +using namespace std; + +namespace kms +{ + +struct CrtcPriv +{ + drmModeCrtcPtr drm_crtc; +}; + +Crtc::Crtc(Card &card, uint32_t id, uint32_t idx) + :DrmObject(card, id, DRM_MODE_OBJECT_CRTC, idx) +{ + m_priv = new CrtcPriv(); + m_priv->drm_crtc = drmModeGetCrtc(this->card().fd(), this->id()); + assert(m_priv->drm_crtc); +} + +Crtc::~Crtc() +{ + drmModeFreeCrtc(m_priv->drm_crtc); + delete m_priv; +} + +void Crtc::setup() +{ + for (Plane* plane : card().get_planes()) { + if (plane->supports_crtc(this)) + m_possible_planes.push_back(plane); + } +} + +void Crtc::print_short() const +{ + auto c = m_priv->drm_crtc; + + printf("Crtc %d, %d,%d %dx%d\n", id(), + c->x, c->y, c->width, c->height); +} + +int Crtc::set_mode(Connector* conn, Framebuffer& fb, const Videomode& mode) +{ + uint32_t conns[] = { conn->id() }; + drmModeModeInfo drmmode = video_mode_to_drm_mode(mode); + + return drmModeSetCrtc(card().fd(), id(), fb.id(), + 0, 0, + conns, 1, &drmmode); +} + +static inline uint32_t conv(float x) +{ + // XXX fix the conversion for fractional part + return ((uint32_t)x) << 16; +} + +int Crtc::set_plane(Plane* plane, Framebuffer& fb, + int32_t dst_x, int32_t dst_y, uint32_t dst_w, uint32_t dst_h, + float src_x, float src_y, float src_w, float src_h) +{ + return drmModeSetPlane(card().fd(), plane->id(), id(), fb.id(), 0, + dst_x, dst_y, dst_w, dst_h, + conv(src_x), conv(src_y), conv(src_w), conv(src_h)); +} +} diff --git a/libkms++/crtc.h b/libkms++/crtc.h new file mode 100644 index 0000000..ac05da9 --- /dev/null +++ b/libkms++/crtc.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "drmobject.h" + +namespace kms +{ + +struct CrtcPriv; + +class Crtc : public DrmObject +{ +public: + Crtc(Card& card, uint32_t id, uint32_t idx); + ~Crtc(); + + void setup(); + + void print_short() const; + + const std::vector& get_possible_planes() const { return m_possible_planes; } + + int set_mode(Connector* conn, Framebuffer& fb, const Videomode& mode); + + int set_plane(Plane *plane, Framebuffer &fb, + int32_t dst_x, int32_t dst_y, uint32_t dst_w, uint32_t dst_h, + float src_x, float src_y, float src_w, float src_h); + +private: + CrtcPriv* m_priv; + + std::vector m_possible_planes; +}; +} diff --git a/libkms++/decls.h b/libkms++/decls.h new file mode 100644 index 0000000..c2cf09f --- /dev/null +++ b/libkms++/decls.h @@ -0,0 +1,15 @@ +#pragma once + +namespace kms +{ +class AtomicReq; +class Card; +class Connector; +class Crtc; +class Encoder; +class Framebuffer; +class DrmObject; +class Plane; +class Property; +class Videomode; +} diff --git a/libkms++/drmobject.cpp b/libkms++/drmobject.cpp new file mode 100644 index 0000000..ea5d594 --- /dev/null +++ b/libkms++/drmobject.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include + +#include "kms++.h" + +using namespace std; + +namespace kms +{ + +DrmObject::DrmObject(Card& card, uint32_t object_type) + :m_id(-1), m_card(card), m_object_type(object_type) +{ +} + +DrmObject::DrmObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx) + :m_id(id), m_card(card), m_object_type(object_type), m_idx(idx) +{ + refresh_props(); +} + +DrmObject::~DrmObject() +{ + +} + +void DrmObject::refresh_props() +{ + auto props = drmModeObjectGetProperties(card().fd(), this->id(), this->object_type()); + + if (props == nullptr) + return; + + for (unsigned i = 0; i < props->count_props; ++i) { + uint32_t prop_id = props->props[i]; + uint64_t prop_value = props->prop_values[i]; + + m_prop_values[prop_id] = prop_value; + } + + drmModeFreeObjectProperties(props); +} + +void DrmObject::print_props() const +{ + for (auto it = m_prop_values.begin(); it != m_prop_values.end(); ++it) { + cout << "\t" << card().get_prop(it->first)->name() << + " = " << it->second << endl; + } +} + +uint64_t DrmObject::get_prop_value(uint32_t id) const +{ + return m_prop_values.at(id); +} + +uint64_t DrmObject::get_prop_value(const char *name) const +{ + for (auto pair : m_prop_values) { + auto prop = card().get_prop(pair.first); + if (strcmp(name, prop->name()) == 0) + return m_prop_values.at(prop->id()); + } + + throw invalid_argument("foo"); +} +} diff --git a/libkms++/drmobject.h b/libkms++/drmobject.h new file mode 100644 index 0000000..5c945f9 --- /dev/null +++ b/libkms++/drmobject.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include "decls.h" + +namespace kms +{ + +class DrmObject +{ +public: + DrmObject(Card& card, uint32_t object_type); + DrmObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx = 0); + virtual ~DrmObject(); + + DrmObject(const DrmObject& other) = delete; + DrmObject& operator=(const DrmObject& other) = delete; + + virtual void setup() { }; + + virtual void print_short() const = 0; + void print_props() const; + + uint32_t id() const { return m_id; } + Card& card() const { return m_card; } + + uint32_t object_type() const { return m_object_type; } + uint32_t idx() const { return m_idx; } + + void refresh_props(); + uint64_t get_prop_value(uint32_t id) const; + uint64_t get_prop_value(const char *name) const; + +protected: + uint32_t m_id; // protected for Framebuffer... + +private: + Card& m_card; + std::map m_prop_values; + uint32_t m_object_type; + uint32_t m_idx; +}; +} diff --git a/libkms++/encoder.cpp b/libkms++/encoder.cpp new file mode 100644 index 0000000..081177b --- /dev/null +++ b/libkms++/encoder.cpp @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "kms++.h" + +using namespace std; + +namespace kms +{ + +struct EncoderPriv +{ + drmModeEncoderPtr drm_encoder; +}; + +Encoder::Encoder(Card &card, uint32_t id) + :DrmObject(card, id, DRM_MODE_OBJECT_ENCODER) +{ + m_priv = new EncoderPriv(); + m_priv->drm_encoder = drmModeGetEncoder(this->card().fd(), this->id()); + assert(m_priv->drm_encoder); +} + +Encoder::~Encoder() +{ + drmModeFreeEncoder(m_priv->drm_encoder); + delete m_priv; +} + +void Encoder::print_short() const +{ + auto e = m_priv->drm_encoder; + + printf("Encoder %d, %d\n", id(), + e->encoder_type); +} + +Crtc* Encoder::get_crtc() const +{ + return card().get_crtc(m_priv->drm_encoder->crtc_id); +} + +vector Encoder::get_possible_crtcs() const +{ + unsigned bits = m_priv->drm_encoder->possible_crtcs; + vector crtcs; + + for (int idx = 0; bits; idx++, bits >>= 1) { + if ((bits & 1) == 0) + continue; + + auto crtc = card().get_crtc_by_index(idx); + crtcs.push_back(crtc); + } + + return crtcs; +} +} diff --git a/libkms++/encoder.h b/libkms++/encoder.h new file mode 100644 index 0000000..3e9a8e4 --- /dev/null +++ b/libkms++/encoder.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "drmobject.h" + +namespace kms +{ + +struct EncoderPriv; + +class Encoder : public DrmObject +{ +public: + Encoder(Card& card, uint32_t id); + ~Encoder(); + + void print_short() const; + + Crtc* get_crtc() const; + std::vector get_possible_crtcs() const; + +private: + EncoderPriv* m_priv; +}; +} diff --git a/libkms++/framebuffer.cpp b/libkms++/framebuffer.cpp new file mode 100644 index 0000000..5382a6f --- /dev/null +++ b/libkms++/framebuffer.cpp @@ -0,0 +1,167 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kms++.h" +#include "utils/testpat.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +namespace kms +{ + +Framebuffer::Framebuffer(Card &card, uint32_t width, uint32_t height, const char* fourcc) + :DrmObject(card, DRM_MODE_OBJECT_FB) +{ + uint32_t a, b, c, d; + a = fourcc[0]; + b = fourcc[1]; + c = fourcc[2]; + d = fourcc[3]; + + uint32_t code = ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)); + + Create(width, height, code); +} + +Framebuffer::~Framebuffer() +{ + Destroy(); +} + +void Framebuffer::print_short() const +{ + printf("Framebuffer %d\n", id()); +} + +struct FormatPlaneInfo +{ + uint8_t bitspp; /* bits per (macro) pixel */ + uint8_t xsub; + uint8_t ysub; +}; + +struct FormatInfo +{ + uint32_t format; + uint8_t num_planes; + struct FormatPlaneInfo planes[4]; +}; + +static const FormatInfo format_info_array[] = { + /* YUV packed */ + { DRM_FORMAT_UYVY, 1, { { 32, 2, 1 } }, }, + { DRM_FORMAT_YUYV, 1, { { 32, 2, 1 } }, }, + /* YUV semi-planar */ + { DRM_FORMAT_NV12, 2, { { 8, 1, 1, }, { 16, 2, 2 } }, }, + /* RGB16 */ + { DRM_FORMAT_RGB565, 1, { { 16, 1, 1 } }, }, + /* RGB32 */ + { DRM_FORMAT_XRGB8888, 1, { { 32, 1, 1 } }, }, +}; + +static const FormatInfo& find_format(uint32_t format) +{ + for (uint i = 0; i < ARRAY_SIZE(format_info_array); ++i) { + if (format == format_info_array[i].format) + return format_info_array[i]; + } + + throw std::invalid_argument("foo"); +} + +void Framebuffer::Create(uint32_t width, uint32_t height, uint32_t format) +{ + int r; + + m_width = width; + m_height = height; + m_format = format; + + const FormatInfo& format_info = find_format(format); + + m_num_planes = format_info.num_planes; + + for (int i = 0; i < format_info.num_planes; ++i) { + const FormatPlaneInfo& pi = format_info.planes[i]; + FramebufferPlane& plane = m_planes[i]; + + /* create dumb buffer */ + struct drm_mode_create_dumb creq = drm_mode_create_dumb(); + creq.width = m_width / pi.xsub; + creq.height = m_height / pi.ysub; + creq.bpp = pi.bitspp; + r = drmIoctl(card().fd(), DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (r) + throw std::invalid_argument("foo"); + + plane.handle = creq.handle; + plane.stride = creq.pitch; + plane.size = creq.height * creq.pitch; + + /* + printf("buf %d: %dx%d, bitspp %d, stride %d, size %d\n", + i, creq.width, creq.height, pi->bitspp, plane->stride, plane->size); + */ + + /* prepare buffer for memory mapping */ + struct drm_mode_map_dumb mreq = drm_mode_map_dumb(); + mreq.handle = plane.handle; + r = drmIoctl(card().fd(), DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (r) + throw std::invalid_argument("foo"); + + /* perform actual memory mapping */ + m_planes[i].map = (uint8_t *)mmap(0, plane.size, PROT_READ | PROT_WRITE, MAP_SHARED, + card().fd(), mreq.offset); + if (plane.map == MAP_FAILED) + throw std::invalid_argument("foo"); + + /* clear the framebuffer to 0 */ + memset(plane.map, 0, plane.size); + } + + /* create framebuffer object for the dumb-buffer */ + uint32_t bo_handles[4] = { m_planes[0].handle, m_planes[1].handle }; + uint32_t pitches[4] = { m_planes[0].stride, m_planes[1].stride }; + uint32_t offsets[4] = { 0 }; + uint32_t id; + r = drmModeAddFB2(card().fd(), m_width, m_height, format, + bo_handles, pitches, offsets, &id, 0); + if (r) + throw std::invalid_argument("foo"); + + m_id = id; +} + +void Framebuffer::Destroy() +{ + /* delete framebuffer */ + drmModeRmFB(card().fd(), id()); + + for (uint i = 0; i < m_num_planes; ++i) { + FramebufferPlane& plane = m_planes[i]; + + /* unmap buffer */ + munmap(plane.map, plane.size); + + /* delete dumb buffer */ + struct drm_mode_destroy_dumb dreq = drm_mode_destroy_dumb(); + dreq.handle = plane.handle; + drmIoctl(card().fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + + } +} + +void Framebuffer::clear() +{ + for (unsigned i = 0; i < m_num_planes; ++i) + memset(m_planes[i].map, 0, m_planes[i].size); +} +} diff --git a/libkms++/framebuffer.h b/libkms++/framebuffer.h new file mode 100644 index 0000000..2710528 --- /dev/null +++ b/libkms++/framebuffer.h @@ -0,0 +1,44 @@ +#pragma once + +#include "drmobject.h" + +namespace kms +{ + +class Framebuffer : public DrmObject +{ +public: + Framebuffer(Card& card, uint32_t width, uint32_t height, const char* fourcc); + virtual ~Framebuffer(); + + void print_short() const; + + uint32_t width() const { return m_width; } + uint32_t height() const { return m_height; } + uint32_t format() const { return m_format; } + + uint8_t* map(unsigned plane) const { return m_planes[plane].map; } + uint32_t stride(unsigned plane) const { return m_planes[plane].stride; } + uint32_t size(unsigned plane) const { return m_planes[plane].size; } + + void clear(); + +private: + struct FramebufferPlane { + uint32_t handle; + uint32_t size; + uint32_t stride; + uint8_t *map; + }; + + void Create(uint32_t width, uint32_t height, uint32_t format); + void Destroy(); + + unsigned m_num_planes; + struct FramebufferPlane m_planes[4]; + + uint32_t m_width; + uint32_t m_height; + uint32_t m_format; +}; +} diff --git a/libkms++/helpers.cpp b/libkms++/helpers.cpp new file mode 100644 index 0000000..7746bde --- /dev/null +++ b/libkms++/helpers.cpp @@ -0,0 +1,23 @@ + +#include "connector.h" +#include "helpers.h" +#include + +namespace kms +{ +Videomode drm_mode_to_video_mode(const drmModeModeInfo& drmmode) +{ + // XXX these are the same at the moment + Videomode mode; + memcpy(&mode, &drmmode, sizeof(mode)); + return mode; +} + +drmModeModeInfo video_mode_to_drm_mode(const Videomode& mode) +{ + // XXX these are the same at the moment + drmModeModeInfo drmmode; + memcpy(&drmmode, &mode, sizeof(drmmode)); + return drmmode; +} +} diff --git a/libkms++/helpers.h b/libkms++/helpers.h new file mode 100644 index 0000000..ac640e8 --- /dev/null +++ b/libkms++/helpers.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace kms +{ +class Videomode; + +Videomode drm_mode_to_video_mode(const drmModeModeInfo& drmmode); +drmModeModeInfo video_mode_to_drm_mode(const Videomode& mode); +} diff --git a/libkms++/kms++.h b/libkms++/kms++.h new file mode 100644 index 0000000..f9808ed --- /dev/null +++ b/libkms++/kms++.h @@ -0,0 +1,10 @@ +#pragma once + +#include "atomicreq.h" +#include "card.h" +#include "connector.h" +#include "crtc.h" +#include "encoder.h" +#include "framebuffer.h" +#include "plane.h" +#include "property.h" diff --git a/libkms++/plane.cpp b/libkms++/plane.cpp new file mode 100644 index 0000000..afb9c78 --- /dev/null +++ b/libkms++/plane.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "kms++.h" + +using namespace std; + +namespace kms +{ + +struct PlanePriv +{ + drmModePlanePtr drm_plane; +}; + +Plane::Plane(Card &card, uint32_t id) + :DrmObject(card, id, DRM_MODE_OBJECT_PLANE) +{ + m_priv = new PlanePriv(); + m_priv->drm_plane = drmModeGetPlane(this->card().fd(), this->id()); + assert(m_priv->drm_plane); +} + +Plane::~Plane() +{ + drmModeFreePlane(m_priv->drm_plane); + delete m_priv; +} + +void Plane::print_short() const +{ + auto p = m_priv->drm_plane; + + printf("Plane %d, %d modes, %d,%d -> %dx%d\n", id(), + p->count_formats, + p->crtc_x, p->crtc_y, p->x, p->y); + + printf("\t"); + for (unsigned i = 0; i < p->count_formats; ++i) { + uint32_t f = p->formats[i]; + printf("%c%c%c%c ", + (f >> 0) & 0xff, + (f >> 8) & 0xff, + (f >> 16) & 0xff, + (f >> 24) & 0xff); + } + printf("\n"); +} + +bool Plane::supports_crtc(Crtc* crtc) const +{ + return m_priv->drm_plane->possible_crtcs & (1 << crtc->idx()); +} + +PlaneType Plane::plane_type() const +{ + return (PlaneType)get_prop_value("type"); +} +} diff --git a/libkms++/plane.h b/libkms++/plane.h new file mode 100644 index 0000000..890a28f --- /dev/null +++ b/libkms++/plane.h @@ -0,0 +1,32 @@ +#pragma once + +#include "drmobject.h" + +namespace kms +{ + +enum class PlaneType +{ + Overlay = 0, + Primary = 1, + Cursor = 2, +}; + +struct PlanePriv; + +class Plane : public DrmObject +{ +public: + Plane(Card& card, uint32_t id); + ~Plane(); + + void print_short() const; + + bool supports_crtc(Crtc* crtc) const; + + PlaneType plane_type() const; + +private: + PlanePriv* m_priv; +}; +} diff --git a/libkms++/property.cpp b/libkms++/property.cpp new file mode 100644 index 0000000..af6a0ae --- /dev/null +++ b/libkms++/property.cpp @@ -0,0 +1,36 @@ +#include +#include + +#include "kms++.h" + +namespace kms +{ + +struct PropertyPriv +{ + drmModePropertyPtr drm_prop; +}; + +Property::Property(Card& card, uint32_t id) + : DrmObject(card, id, DRM_MODE_OBJECT_PROPERTY) +{ + m_priv = new PropertyPriv(); + m_priv->drm_prop = drmModeGetProperty(card.fd(), id); +} + +Property::~Property() +{ + drmModeFreeProperty(m_priv->drm_prop); + delete m_priv; +} + +void Property::print_short() const +{ + printf("Property %d, %s\n", id(), name()); +} + +const char *Property::name() const +{ + return m_priv->drm_prop->name; +} +} diff --git a/libkms++/property.h b/libkms++/property.h new file mode 100644 index 0000000..d5306d0 --- /dev/null +++ b/libkms++/property.h @@ -0,0 +1,23 @@ +#pragma once + +#include "drmobject.h" + +namespace kms +{ + +struct PropertyPriv; + +class Property : public DrmObject +{ +public: + Property(Card& card, uint32_t id); + ~Property(); + + void print_short() const; + + const char *name() const; + +private: + PropertyPriv* m_priv; +}; +} diff --git a/libkms++/utils/color.cpp b/libkms++/utils/color.cpp new file mode 100644 index 0000000..b5b9001 --- /dev/null +++ b/libkms++/utils/color.cpp @@ -0,0 +1,67 @@ +#include "color.h" + +namespace kms +{ +RGB::RGB() +{ + r = g = b = a = 0; +} + +RGB::RGB(uint8_t r, uint8_t g, uint8_t b) +{ + this->r = r; + this->g = g; + this->b = b; + this->a = 0; +} + +uint16_t RGB::rgb565() const +{ + uint16_t r = (this->r >> 3) << 11; + uint16_t g = (this->g >> 2) << 5; + uint16_t b = (this->b >> 3) << 0; + return r | g | b; +} + +YUV RGB::yuv() const +{ + return YUV(*this); +} + + +YUV::YUV() +{ + y = u = v = a = 0; +} + +YUV::YUV(uint8_t y, uint8_t u, uint8_t v) +{ + this->y = y; + this->u = u; + this->v = v; + this->a = 0; +} + +static inline uint8_t MAKE_YUV_601_Y(uint8_t r, uint8_t g, uint8_t b) +{ + return (((66 * r + 129 * g + 25 * b + 128) >> 8) + 16); +} + +static inline uint8_t MAKE_YUV_601_U(uint8_t r, uint8_t g, uint8_t b) +{ + return (((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128); +} + +static inline uint8_t MAKE_YUV_601_V(uint8_t r, uint8_t g, uint8_t b) +{ + return (((112 * r - 94 * g - 18 * b + 128) >> 8) + 128); +} + +YUV::YUV(const RGB& rgb) +{ + this->y = MAKE_YUV_601_Y(rgb.r, rgb.g, rgb.b); + this->u = MAKE_YUV_601_U(rgb.r, rgb.g, rgb.b); + this->v = MAKE_YUV_601_V(rgb.r, rgb.g, rgb.b); + this->a = rgb.a; +} +} diff --git a/libkms++/utils/color.h b/libkms++/utils/color.h new file mode 100644 index 0000000..1db47e8 --- /dev/null +++ b/libkms++/utils/color.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +namespace kms +{ +struct YUV; + +struct RGB +{ + RGB(); + RGB(uint8_t r, uint8_t g, uint8_t b); + + uint16_t rgb565() const; + YUV yuv() const; + + union { + struct + { + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t a; + }; + + uint32_t raw; + }; +}; + +struct YUV +{ + YUV(); + YUV(uint8_t y, uint8_t u, uint8_t v); + YUV(const RGB& rgb); + + union { + struct + { + uint8_t v; + uint8_t u; + uint8_t y; + uint8_t a; + }; + + uint32_t raw; + }; +}; +} diff --git a/libkms++/utils/conv.cpp b/libkms++/utils/conv.cpp new file mode 100644 index 0000000..d439253 --- /dev/null +++ b/libkms++/utils/conv.cpp @@ -0,0 +1,138 @@ +#include +#include + +#include "framebuffer.h" +#include "color.h" +#include "conv.h" + +namespace kms +{ +static RGB read_rgb(const Framebuffer& fb, int x, int y) +{ + uint32_t *pc = (uint32_t *)(fb.map(0) + fb.stride(0) * y); + + uint32_t c = pc[x]; + + return RGB((c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff); +} + +static YUV read_rgb_as_yuv(const Framebuffer& fb, int x, int y) +{ + RGB rgb = read_rgb(fb, x, y); + return YUV(rgb); +} + +static void fb_rgb_to_packed_yuv(Framebuffer& dst_fb, const Framebuffer& src_fb) +{ + unsigned w = src_fb.width(); + unsigned h = src_fb.height(); + + uint8_t *dst = dst_fb.map(0); + + for (unsigned y = 0; y < h; ++y) { + for (unsigned x = 0; x < w; x += 2) { + YUV yuv1 = read_rgb_as_yuv(src_fb, x + 0, y); + YUV yuv2 = read_rgb_as_yuv(src_fb, x + 1, y); + + switch (dst_fb.format()) { + case DRM_FORMAT_UYVY: + dst[x * 2 + 0] = (yuv1.u + yuv2.u) / 2; + dst[x * 2 + 1] = yuv1.y; + dst[x * 2 + 2] = (yuv1.v + yuv2.v) / 2; + dst[x * 2 + 3] = yuv2.y; + break; + case DRM_FORMAT_YUYV: + dst[x * 2 + 0] = yuv1.y; + dst[x * 2 + 1] = (yuv1.u + yuv2.u) / 2; + dst[x * 2 + 2] = yuv2.y; + dst[x * 2 + 3] = (yuv1.v + yuv2.v) / 2; + break; + + default: + throw std::invalid_argument("fo"); + } + } + + dst += dst_fb.stride(0); + } +} + +static void fb_rgb_to_semiplanar_yuv(Framebuffer& dst_fb, const Framebuffer& src_fb) +{ + unsigned w = src_fb.width(); + unsigned h = src_fb.height(); + + uint8_t *dst_y = dst_fb.map(0); + uint8_t *dst_uv = dst_fb.map(1); + + for (unsigned y = 0; y < h; ++y) { + for (unsigned x = 0; x < w; ++x) { + YUV yuv = read_rgb_as_yuv(src_fb, x, y); + dst_y[x] = yuv.y; + } + + dst_y += dst_fb.stride(0); + } + + for (unsigned y = 0; y < h; y += 2) { + for (unsigned x = 0; x < w; x += 2) { + YUV yuv00 = read_rgb_as_yuv(src_fb, x + 0, y + 0); + YUV yuv01 = read_rgb_as_yuv(src_fb, x + 1, y + 0); + YUV yuv10 = read_rgb_as_yuv(src_fb, x + 0, y + 1); + YUV yuv11 = read_rgb_as_yuv(src_fb, x + 1, y + 1); + + unsigned u = (yuv00.u + yuv01.u + yuv10.u + yuv11.u) / 4; + unsigned v = (yuv00.v + yuv01.v + yuv10.v + yuv11.v) / 4; + + dst_uv[x + 0] = u; + dst_uv[x + 1] = v; + } + + dst_uv += dst_fb.stride(1); + } +} + +static void fb_rgb_to_rgb565(Framebuffer& dst_fb, const Framebuffer& src_fb) +{ + unsigned w = src_fb.width(); + unsigned h = src_fb.height(); + + uint8_t *dst = dst_fb.map(0); + + for (unsigned y = 0; y < h; ++y) { + for (unsigned x = 0; x < w; ++x) { + RGB rgb = read_rgb(src_fb, x, y); + + unsigned r = rgb.r * 32 / 256; + unsigned g = rgb.g * 64 / 256; + unsigned b = rgb.b * 32 / 256; + + ((uint16_t *)dst)[x] = (r << 11) | (g << 5) | (b << 0); + } + + dst += dst_fb.stride(0); + } +} + +void color_convert(Framebuffer& dst, const Framebuffer &src) +{ + switch (dst.format()) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + fb_rgb_to_semiplanar_yuv(dst, src); + break; + + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + fb_rgb_to_packed_yuv(dst, src); + break; + + case DRM_FORMAT_RGB565: + fb_rgb_to_rgb565(dst, src); + break; + + default: + throw std::invalid_argument("fo"); + } +} +} diff --git a/libkms++/utils/conv.h b/libkms++/utils/conv.h new file mode 100644 index 0000000..d1b306a --- /dev/null +++ b/libkms++/utils/conv.h @@ -0,0 +1,8 @@ +#pragma once + +namespace kms +{ +class Framebuffer; + +void color_convert(Framebuffer& dst, const Framebuffer &src); +} diff --git a/libkms++/utils/testpat.cpp b/libkms++/utils/testpat.cpp new file mode 100644 index 0000000..cd35e0e --- /dev/null +++ b/libkms++/utils/testpat.cpp @@ -0,0 +1,162 @@ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "card.h" +#include "framebuffer.h" +#include "testpat.h" +#include "color.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +namespace kms +{ +static void draw_pixel(Framebuffer& buf, unsigned x, unsigned y, RGB color) +{ + static RGB c1; + + switch (buf.format()) { + case DRM_FORMAT_XRGB8888: + { + uint32_t *p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4); + *p = color.raw; + break; + } + case DRM_FORMAT_RGB565: + { + uint16_t *p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2); + *p = color.rgb565(); + break; + } + case DRM_FORMAT_UYVY: + { + if ((x & 1) == 0) { + c1 = color; + return; + } + + uint8_t *p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2); + + YUV yuv1 = c1.yuv(); + YUV yuv2 = color.yuv(); + + p[0] = (yuv1.u + yuv2.u) / 2; + p[1] = yuv1.y; + p[2] = (yuv1.v + yuv2.v) / 2; + p[3] = yuv2.y; + break; + } + case DRM_FORMAT_YUYV: + { + if ((x & 1) == 0) { + c1 = color; + return; + } + + uint8_t *p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2); + + YUV yuv1 = c1.yuv(); + YUV yuv2 = color.yuv(); + + p[0] = yuv1.y; + p[1] = (yuv1.u + yuv2.u) / 2; + p[2] = yuv2.y; + p[3] = (yuv1.v + yuv2.v) / 2; + break; + } + } +} + +static void draw_rgb_test_pattern(Framebuffer& fb) +{ + unsigned x, y; + unsigned w = fb.width(); + unsigned h = fb.height(); + + const unsigned mw = 20; + + const unsigned xm1 = mw; + const unsigned xm2 = w - mw - 1; + const unsigned ym1 = mw; + const unsigned ym2 = h - mw - 1; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + // white margin lines + if (x == xm1 || x == xm2 || y == ym1 || y == ym2) + draw_pixel(fb, x, y, RGB(255, 255, 255)); + // white box outlines to corners + else if ((x == 0 || x == w - 1) && (y < ym1 || y > ym2)) + draw_pixel(fb, x, y, RGB(255, 255, 255)); + // white box outlines to corners + else if ((y == 0 || y == h - 1) && (x < xm1 || x > xm2)) + draw_pixel(fb, x, y, RGB(255, 255, 255)); + // blue bar on the left + else if (x < xm1 && (y > ym1 && y < ym2)) + draw_pixel(fb, x, y, RGB(0, 0, 255)); + // blue bar on the top + else if (y < ym1 && (x > xm1 && x < xm2)) + draw_pixel(fb, x, y, RGB(0, 0, 255)); + // red bar on the right + else if (x > xm2 && (y > ym1 && y < ym2)) + draw_pixel(fb, x, y, RGB(255, 0, 0)); + // red bar on the bottom + else if (y > ym2 && (x > xm1 && x < xm2)) + draw_pixel(fb, x, y, RGB(255, 0, 0)); + // inside the margins + else if (x > xm1 && x < xm2 && y > ym1 && y < ym2) { + // diagonal line + if (x == y || w - x == h - y) + draw_pixel(fb, x, y, RGB(255, 255, 255)); + // diagonal line + else if (w - x == y || x == h - y) + draw_pixel(fb, x, y, RGB(255, 255, 255)); + else { + int t = (x - xm1 - 1) * 3 / (xm2 - xm1 - 1); + unsigned r = 0, g = 0, b = 0; + + unsigned c = (y - ym1 - 1) % 256; + + switch (t) { + case 0: + r = c; + break; + case 1: + g = c; + break; + case 2: + b = c; + break; + } + + draw_pixel(fb, x, y, RGB(r, g, b)); + } + // black corners + } else { + draw_pixel(fb, x, y, RGB(0, 0, 0)); + } + } + } +} + +void draw_test_pattern(Framebuffer& fb) +{ + using namespace std::chrono; + + auto t1 = high_resolution_clock::now(); + + draw_rgb_test_pattern(fb); + + auto t2 = high_resolution_clock::now(); + auto time_span = duration_cast(t2 - t1); + + printf("draw took %lu us\n", time_span.count()); +} +} diff --git a/libkms++/utils/testpat.h b/libkms++/utils/testpat.h new file mode 100644 index 0000000..b60271a --- /dev/null +++ b/libkms++/utils/testpat.h @@ -0,0 +1,6 @@ +#pragma once + +namespace kms +{ +void draw_test_pattern(Framebuffer& fb); +} diff --git a/lua/CMakeLists.txt b/lua/CMakeLists.txt new file mode 100644 index 0000000..ba70561 --- /dev/null +++ b/lua/CMakeLists.txt @@ -0,0 +1,20 @@ +set(SWIG_EXECUTABLE "swig3.0") +find_package(SWIG 3.0 REQUIRED) +include(${SWIG_USE_FILE}) + +include_directories(${LIBDRM_INCLUDE_DIRS}) +link_directories(${LIBDRM_LIBRARY_DIRS}) + +pkg_search_module(LUA REQUIRED lua5.2 lua) +include_directories(${LUA_INCLUDE_DIRS}) +link_directories(${LUA_LIBRARY_DIRS}) + +#include_directories(libkms) +#XXX +include_directories(../libkms++) + +set_source_files_properties(luakms.i PROPERTIES CPLUSPLUS ON) +swig_add_module(luakms lua luakms.i) +swig_link_libraries(luakms kms++ ${LIBDRM_LIBRARIES} ${LUA_LIBRARIES}) + +add_custom_target(luaextras SOURCES test.lua) diff --git a/lua/luakms.i b/lua/luakms.i new file mode 100644 index 0000000..60931f1 --- /dev/null +++ b/lua/luakms.i @@ -0,0 +1,20 @@ +%module libluakms +%{ +#include "kms++.h" +#include "utils/testpat.h" +using namespace kms; +%} + +%include "stdint.i" + +%include "decls.h" +%include "drmobject.h" +%include "atomicreq.h" +%include "crtc.h" +%include "card.h" +%include "property.h" +%include "framebuffer.h" +%include "plane.h" +%include "connector.h" +%include "encoder.h" +%include "utils/testpat.h" diff --git a/lua/test.lua b/lua/test.lua new file mode 100755 index 0000000..bc6c4fc --- /dev/null +++ b/lua/test.lua @@ -0,0 +1,21 @@ +#!/usr/bin/lua + +require("libluakms") +require("bit32") + +card = libluakms.Card() +card:print_short() + +conn = card:get_first_connected_connector() + +mode = conn:get_default_mode() + +fb = libluakms.Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); +libluakms.draw_test_pattern(fb); + +crtc = conn:get_current_crtc() + +crtc:set_mode(conn, fb, mode) + +print("Press enter to exit") +io.read() diff --git a/py/CMakeLists.txt b/py/CMakeLists.txt new file mode 100644 index 0000000..39e6ec6 --- /dev/null +++ b/py/CMakeLists.txt @@ -0,0 +1,26 @@ +set(SWIG_EXECUTABLE "swig3.0") +find_package(SWIG 3.0 REQUIRED) +include(${SWIG_USE_FILE}) + +include_directories(${LIBDRM_INCLUDE_DIRS}) +link_directories(${LIBDRM_LIBRARY_DIRS}) + +pkg_check_modules(PYTHON python-3.4 REQUIRED) +include_directories(${PYTHON_INCLUDE_DIRS}) + +#include_directories(libkms) +#XXX +include_directories(../libkms++) + +#set(CMAKE_SWIG_FLAGS "-I../../libkms") + +set_source_files_properties(pykms.i PROPERTIES CPLUSPLUS ON) +swig_add_module(pykms python pykms.i) +swig_link_libraries(pykms kms++ ${LIBDRM_LIBRARIES} ${PYTHON_LIBRARIES}) + +add_custom_target(pyextras SOURCES test.py functest.py) + +add_test(NAME pytest COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/functest.py") +set_property(TEST pytest PROPERTY + ENVIRONMENT "PYTHONPATH=." "LD_LIBRARY_PATH=." +) diff --git a/py/functest.py b/py/functest.py new file mode 100755 index 0000000..54c3363 --- /dev/null +++ b/py/functest.py @@ -0,0 +1,19 @@ +#!/usr/bin/python3.4 + +import pykms + +card = pykms.Card() + +conn = card.get_first_connected_connector() + +mode = conn.get_default_mode() + +fb = pykms.Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); +fb.draw_test_pattern(0); + +crtc = conn.get_current_crtc() + +crtc.set_mode(conn, fb, mode) + +print("OK") + diff --git a/py/pykms.i b/py/pykms.i new file mode 100644 index 0000000..6a6ad11 --- /dev/null +++ b/py/pykms.i @@ -0,0 +1,20 @@ +%module pykms +%{ +#include "kms++.h" +#include "utils/testpat.h" +using namespace kms; +%} + +%include "stdint.i" + +%include "decls.h" +%include "drmobject.h" +%include "atomicreq.h" +%include "crtc.h" +%include "card.h" +%include "property.h" +%include "framebuffer.h" +%include "plane.h" +%include "connector.h" +%include "encoder.h" +%include "utils/testpat.h" diff --git a/py/test.py b/py/test.py new file mode 100755 index 0000000..a104ba5 --- /dev/null +++ b/py/test.py @@ -0,0 +1,19 @@ +#!/usr/bin/python3.4 + +import pykms + +card = pykms.Card() +card.print_short() + +conn = card.get_first_connected_connector() + +mode = conn.get_default_mode() + +fb = pykms.Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); +pykms.draw_test_pattern(fb); + +crtc = conn.get_current_crtc() + +crtc.set_mode(conn, fb, mode) + +input("press enter to exit\n") diff --git a/test.h b/test.h new file mode 100644 index 0000000..090ff38 --- /dev/null +++ b/test.h @@ -0,0 +1,15 @@ +#pragma once + +#define unlikely(x) __builtin_expect(!!(x), 0) + +static void ASSERT_FAIL(const char *cond, const char *file, + unsigned int line, const char *func) __attribute__ ((__noreturn__)); + +static void ASSERT_FAIL(const char *cond, const char *file, + unsigned int line, const char *func) +{ + fprintf(stderr, "%s:%d: %s: ASSERT(%s) failed\n", file, line, func, cond); + abort(); +} + +#define ASSERT(x) if (unlikely(!(x))) { ASSERT_FAIL( __STRING(x), __FILE__, __LINE__, __PRETTY_FUNCTION__); } diff --git a/testpat/CMakeLists.txt b/testpat/CMakeLists.txt new file mode 100644 index 0000000..aed4371 --- /dev/null +++ b/testpat/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories(${LIBDRM_INCLUDE_DIRS}) +link_directories(${LIBDRM_LIBRARY_DIRS}) + +add_executable (testpat testpat.cpp) + +target_link_libraries(testpat kms++ ${LIBDRM_LIBRARIES}) diff --git a/testpat/testpat.cpp b/testpat/testpat.cpp new file mode 100644 index 0000000..5632224 --- /dev/null +++ b/testpat/testpat.cpp @@ -0,0 +1,88 @@ +#include +#include + +#include "kms++.h" +#include "utils/testpat.h" + +#include "../test.h" + +using namespace std; +using namespace kms; + +int main() +{ + Card card; + + if (card.master() == false) + printf("Not DRM master, modeset may fail\n"); + + //card.print_short(); + + auto connectors = card.get_connectors(); + + vector fbs; + vector used_crtcs; + + for (auto conn : connectors) + { + if (conn->connected() == false) + continue; + + Crtc* crtc = conn->get_current_crtc(); + if (!crtc) { + vector list = conn->get_possible_crtcs(); + for (auto c : list) { + if (find(used_crtcs.begin(), used_crtcs.end(), c) == used_crtcs.end()) { + crtc = c; + break; + } + } + } + used_crtcs.push_back(crtc); + + ASSERT(crtc); + + int r; + + // RG16 XR24 UYVY YUYV NV12 + + auto mode = conn->get_default_mode(); + + auto fb = new Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); + draw_test_pattern(*fb); + fbs.push_back(fb); + + r = crtc->set_mode(conn, *fb, mode); + ASSERT(r == 0); + + + + Plane* plane = 0; + + for (Plane* p : crtc->get_possible_planes()) { + if (p->plane_type() == PlaneType::Overlay) { + plane = p; + break; + } + } + + if (plane) { + auto planefb = new Framebuffer(card, 400, 400, "YUYV"); + draw_test_pattern(*planefb); + fbs.push_back(planefb); + + r = crtc->set_plane(plane, *planefb, + 0, 0, planefb->width(), planefb->height(), + 0, 0, planefb->width(), planefb->height()); + + ASSERT(r == 0); + } + } + + printf("press enter to exit\n"); + + getchar(); + + for(auto fb : fbs) + delete fb; +} -- cgit v1.2.3