diff options
Diffstat (limited to 'kms++/src')
| -rw-r--r-- | kms++/src/atomicreq.cpp | 133 | ||||
| -rw-r--r-- | kms++/src/blob.cpp | 50 | ||||
| -rw-r--r-- | kms++/src/card.cpp | 430 | ||||
| -rw-r--r-- | kms++/src/connector.cpp | 298 | ||||
| -rw-r--r-- | kms++/src/crtc.cpp | 196 | ||||
| -rw-r--r-- | kms++/src/dmabufframebuffer.cpp | 161 | ||||
| -rw-r--r-- | kms++/src/drmobject.cpp | 32 | ||||
| -rw-r--r-- | kms++/src/drmpropobject.cpp | 101 | ||||
| -rw-r--r-- | kms++/src/dumbframebuffer.cpp | 143 | ||||
| -rw-r--r-- | kms++/src/encoder.cpp | 87 | ||||
| -rw-r--r-- | kms++/src/extframebuffer.cpp | 64 | ||||
| -rw-r--r-- | kms++/src/framebuffer.cpp | 71 | ||||
| -rw-r--r-- | kms++/src/helpers.cpp | 72 | ||||
| -rw-r--r-- | kms++/src/helpers.h | 12 | ||||
| -rw-r--r-- | kms++/src/mode_cvt.cpp | 150 | ||||
| -rw-r--r-- | kms++/src/modedb.cpp | 58 | ||||
| -rw-r--r-- | kms++/src/modedb_cea.cpp | 465 | ||||
| -rw-r--r-- | kms++/src/modedb_dmt.cpp | 203 | ||||
| -rw-r--r-- | kms++/src/omap/omapcard.cpp | 23 | ||||
| -rw-r--r-- | kms++/src/omap/omapframebuffer.cpp | 193 | ||||
| -rw-r--r-- | kms++/src/pixelformats.cpp | 1186 | ||||
| -rw-r--r-- | kms++/src/plane.cpp | 154 | ||||
| -rw-r--r-- | kms++/src/property.cpp | 85 | ||||
| -rw-r--r-- | kms++/src/videomode.cpp | 272 |
24 files changed, 4639 insertions, 0 deletions
diff --git a/kms++/src/atomicreq.cpp b/kms++/src/atomicreq.cpp new file mode 100644 index 0000000..1ef4f7d --- /dev/null +++ b/kms++/src/atomicreq.cpp @@ -0,0 +1,133 @@ +#include <cassert> +#include <stdexcept> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <kms++/kms++.h> + +#ifndef DRM_CLIENT_CAP_ATOMIC + +#define DRM_MODE_ATOMIC_TEST_ONLY 0 +#define DRM_MODE_ATOMIC_NONBLOCK 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 + +using namespace std; + +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(DrmPropObject* ob, Property* prop, uint64_t value) +{ + add(ob->id(), prop->id(), value); +} + +void AtomicReq::add(kms::DrmPropObject* ob, const string& prop, uint64_t value) +{ + Property* p = ob->get_prop(prop); + + if (!p) + throw runtime_error("Property not found"); + + add(ob, p, value); +} + +void AtomicReq::add(kms::DrmPropObject* ob, const map<string, uint64_t>& values) +{ + for (const auto& kvp : values) + add(ob, kvp.first, kvp.second); +} + +void AtomicReq::add_display(Connector* conn, Crtc* crtc, Blob* videomode, Plane* primary, Framebuffer* fb) +{ + add(conn, { + { "CRTC_ID", crtc->id() }, + }); + + add(crtc, { + { "ACTIVE", 1 }, + { "MODE_ID", videomode->id() }, + }); + + add(primary, { + { "FB_ID", fb->id() }, + { "CRTC_ID", crtc->id() }, + { "SRC_X", 0 << 16 }, + { "SRC_Y", 0 << 16 }, + { "SRC_W", fb->width() << 16 }, + { "SRC_H", fb->height() << 16 }, + { "CRTC_X", 0 }, + { "CRTC_Y", 0 }, + { "CRTC_W", fb->width() }, + { "CRTC_H", fb->height() }, + }); +} + +int AtomicReq::test(bool allow_modeset) +{ + uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY; + + if (allow_modeset) + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + + return drmModeAtomicCommit(m_card.fd(), m_req, flags, 0); +} + +int AtomicReq::commit(void* data, bool allow_modeset) +{ + uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK; + + if (allow_modeset) + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + + return drmModeAtomicCommit(m_card.fd(), m_req, flags, data); +} + +int AtomicReq::commit_sync(bool allow_modeset) +{ + uint32_t flags = 0; + + if (allow_modeset) + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + + return drmModeAtomicCommit(m_card.fd(), m_req, flags, 0); +} +} // namespace kms diff --git a/kms++/src/blob.cpp b/kms++/src/blob.cpp new file mode 100644 index 0000000..69914f6 --- /dev/null +++ b/kms++/src/blob.cpp @@ -0,0 +1,50 @@ +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <kms++/kms++.h> + +using namespace std; + +namespace kms +{ +Blob::Blob(Card& card, uint32_t blob_id) + : DrmObject(card, blob_id, DRM_MODE_OBJECT_BLOB), m_created(false) +{ + // XXX should we verify that the blob_id is a blob object? +} + +Blob::Blob(Card& card, void* data, size_t len) + : DrmObject(card, DRM_MODE_OBJECT_BLOB), m_created(true) +{ + uint32_t id; + + int r = drmModeCreatePropertyBlob(card.fd(), data, len, &id); + if (r) + throw invalid_argument("FAILED TO CREATE PROP\n"); + + set_id(id); +} + +Blob::~Blob() +{ + if (m_created) + drmModeDestroyPropertyBlob(card().fd(), id()); +} + +vector<uint8_t> Blob::data() +{ + drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(card().fd(), id()); + + if (!blob) + throw invalid_argument("Blob data not available"); + + uint8_t* data = static_cast<uint8_t*>(blob->data); + + auto v = vector<uint8_t>(data, data + blob->length); + + drmModeFreePropertyBlob(blob); + + return v; +} + +} // namespace kms diff --git a/kms++/src/card.cpp b/kms++/src/card.cpp new file mode 100644 index 0000000..f67f8f8 --- /dev/null +++ b/kms++/src/card.cpp @@ -0,0 +1,430 @@ +#include <cstdio> +#include <unistd.h> +#include <fcntl.h> +#include <utility> +#include <stdexcept> +#include <cstring> +#include <algorithm> +#include <cerrno> +#include <algorithm> +#include <glob.h> + +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/types.h> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <kms++/kms++.h> + +using namespace std; + +namespace kms +{ +static vector<string> glob(const string& pattern) +{ + glob_t glob_result; + memset(&glob_result, 0, sizeof(glob_result)); + + int r = glob(pattern.c_str(), 0, NULL, &glob_result); + if (r != 0) { + globfree(&glob_result); + throw runtime_error("failed to find DRM cards"); + } + + vector<string> filenames; + for (size_t i = 0; i < glob_result.gl_pathc; ++i) + filenames.push_back(string(glob_result.gl_pathv[i])); + + globfree(&glob_result); + + return filenames; +} + +static int open_first_kms_device() +{ + vector<string> paths = glob("/dev/dri/card*"); + + for (const string& path : paths) { + int fd = open(path.c_str(), O_RDWR | O_CLOEXEC); + if (fd < 0) + throw invalid_argument(string(strerror(errno)) + " opening device " + path); + + auto res = drmModeGetResources(fd); + if (!res) { + close(fd); + continue; + } + + bool has_kms = res->count_crtcs > 0 && res->count_connectors > 0 && res->count_encoders > 0; + + drmModeFreeResources(res); + + if (has_kms) + return fd; + + close(fd); + } + + throw runtime_error("No modesetting DRM card found"); +} + +static int open_device_by_path(const string& path) +{ + int fd = open(path.c_str(), O_RDWR | O_CLOEXEC); + if (fd < 0) + throw invalid_argument(string(strerror(errno)) + " opening device " + path); + return fd; +} + +// open Nth DRM card with the given driver name +static int open_device_by_driver(string name, uint32_t idx) +{ + transform(name.begin(), name.end(), name.begin(), ::tolower); + + uint32_t num_matches = 0; + vector<string> paths = glob("/dev/dri/card*"); + + for (const string& path : paths) { + int fd = open_device_by_path(path); + + drmVersionPtr ver = drmGetVersion(fd); + string drv_name = string(ver->name, ver->name_len); + drmFreeVersion(ver); + + transform(drv_name.begin(), drv_name.end(), drv_name.begin(), ::tolower); + + if (name == drv_name) { + if (idx == num_matches) + return fd; + num_matches++; + } + + close(fd); + } + + throw invalid_argument("Failed to find a DRM device " + name + ":" + to_string(idx)); +} + +std::unique_ptr<Card> Card::open_named_card(const std::string& name) +{ + int fd = drmOpen(name.c_str(), 0); + + if (fd < 0) + throw invalid_argument(string(strerror(errno)) + " opening card \"" + name + "\""); + + return std::unique_ptr<Card>(new Card(fd, true)); +} + +Card::Card(const std::string& dev_path) +{ + const char* drv_p = getenv("KMSXX_DRIVER"); + const char* dev_p = getenv("KMSXX_DEVICE"); + + if (!dev_path.empty()) { + m_fd = open_device_by_path(dev_path); + } else if (dev_p) { + string dev(dev_p); + m_fd = open_device_by_path(dev); + } else if (drv_p) { + string drv(drv_p); + + auto isplit = find(drv.begin(), drv.end(), ':'); + + if (isplit == drv.begin()) + throw runtime_error("Invalid KMSXX_DRIVER"); + + string name; + uint32_t num = 0; + + if (isplit == drv.end()) { + name = drv; + } else { + name = string(drv.begin(), isplit); + string numstr = string(isplit + 1, drv.end()); + num = stoul(numstr); + } + + m_fd = open_device_by_driver(name, num); + } else { + m_fd = open_first_kms_device(); + } + + setup(); +} + +Card::Card(const std::string& driver, uint32_t idx) +{ + m_fd = open_device_by_driver(driver, idx); + + setup(); +} + +Card::Card(int fd, bool take_ownership) +{ + if (take_ownership) { + m_fd = fd; + } else { + m_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); + + if (m_fd < 0) + throw invalid_argument(string(strerror(errno)) + " duplicating fd"); + } + + setup(); +} + +void Card::setup() +{ + drmVersionPtr ver = drmGetVersion(m_fd); + m_version.major = ver->version_major; + m_version.minor = ver->version_minor; + m_version.patchlevel = ver->version_patchlevel; + m_version.name = string(ver->name, ver->name_len); + m_version.date = string(ver->date, ver->date_len); + m_version.desc = string(ver->desc, ver->desc_len); + drmFreeVersion(ver); + + struct stat stats; + int r; + + r = fstat(m_fd, &stats); + if (r < 0) + throw invalid_argument("Can't stat device (" + string(strerror(errno)) + ")"); + + m_minor = minor(stats.st_dev); + + r = drmSetMaster(m_fd); + m_is_master = r == 0; + + if (getenv("KMSXX_DISABLE_UNIVERSAL_PLANES") == 0) { + r = drmSetClientCap(m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + m_has_universal_planes = r == 0; + } else { + m_has_universal_planes = false; + } + +#ifdef DRM_CLIENT_CAP_ATOMIC + if (getenv("KMSXX_DISABLE_ATOMIC") == 0) { + r = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1); + m_has_atomic = r == 0; + } else { + m_has_atomic = false; + } +#else + m_has_atomic = false; +#endif + + uint64_t has_dumb; + r = drmGetCap(m_fd, DRM_CAP_DUMB_BUFFER, &has_dumb); + m_has_dumb = r == 0 && has_dumb; + + auto res = drmModeGetResources(m_fd); + if (res) { + for (int i = 0; i < res->count_connectors; ++i) { + uint32_t id = res->connectors[i]; + auto ob = new Connector(*this, id, i); + m_obmap[id] = ob; + m_connectors.push_back(ob); + } + + for (int i = 0; i < res->count_crtcs; ++i) { + uint32_t id = res->crtcs[i]; + auto ob = new Crtc(*this, id, i); + m_obmap[id] = ob; + m_crtcs.push_back(ob); + } + + for (int i = 0; i < res->count_encoders; ++i) { + uint32_t id = res->encoders[i]; + auto ob = new Encoder(*this, id, i); + m_obmap[id] = ob; + m_encoders.push_back(ob); + } + + drmModeFreeResources(res); + + auto planeRes = drmModeGetPlaneResources(m_fd); + if (planeRes) { + for (uint i = 0; i < planeRes->count_planes; ++i) { + uint32_t id = planeRes->planes[i]; + auto ob = new Plane(*this, id, i); + m_obmap[id] = ob; + m_planes.push_back(ob); + } + + 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()) { + auto prop = new Property(*this, prop_id); + m_obmap[prop_id] = prop; + m_properties.push_back(prop); + } + } + + drmModeFreeObjectProperties(props); + } + + for (auto pair : m_obmap) + pair.second->setup(); +} + +Card::~Card() +{ + restore_modes(); + + while (m_framebuffers.size() > 0) + delete m_framebuffers.back(); + + for (auto pair : m_obmap) + delete pair.second; + + close(m_fd); +} + +void Card::drop_master() +{ + drmDropMaster(fd()); + m_is_master = false; +} + +bool Card::has_kms() const +{ + return m_connectors.size() > 0 && m_encoders.size() > 0 && m_crtcs.size() > 0; +} + +void Card::restore_modes() +{ + for (auto conn : get_connectors()) + conn->restore_mode(); +} + +Connector* Card::get_first_connected_connector() const +{ + for (auto c : m_connectors) { + if (c->connected()) + return c; + } + + throw invalid_argument("no connected connectors"); +} + +DrmObject* Card::get_object(uint32_t id) const +{ + auto iter = m_obmap.find(id); + if (iter != m_obmap.end()) + return iter->second; + return nullptr; +} + +std::vector<kms::DrmObject*> Card::get_objects() const +{ + vector<DrmObject*> v; + for (auto pair : m_obmap) + v.push_back(pair.second); + return v; +} + +Connector* Card::get_connector(uint32_t id) const +{ + return dynamic_cast<Connector*>(get_object(id)); +} +Crtc* Card::get_crtc(uint32_t id) const +{ + return dynamic_cast<Crtc*>(get_object(id)); +} +Encoder* Card::get_encoder(uint32_t id) const +{ + return dynamic_cast<Encoder*>(get_object(id)); +} +Property* Card::get_prop(uint32_t id) const +{ + return dynamic_cast<Property*>(get_object(id)); +} +Plane* Card::get_plane(uint32_t id) const +{ + return dynamic_cast<Plane*>(get_object(id)); +} + +std::vector<kms::Pipeline> Card::get_connected_pipelines() +{ + vector<Pipeline> outputs; + + for (auto conn : get_connectors()) { + if (conn->connected() == false) + continue; + + Crtc* crtc = conn->get_current_crtc(); + + if (!crtc) { + for (auto possible : conn->get_possible_crtcs()) { + if (find_if(outputs.begin(), outputs.end(), [possible](Pipeline out) { return out.crtc == possible; }) == outputs.end()) { + crtc = possible; + break; + } + } + } + + if (!crtc) + throw invalid_argument(string("Connector #") + + to_string(conn->idx()) + + " has no possible crtcs"); + + outputs.push_back(Pipeline{ crtc, conn }); + } + + return outputs; +} + +static void page_flip_handler(int fd, unsigned int frame, + unsigned int sec, unsigned int usec, + void* data) +{ + auto handler = static_cast<PageFlipHandlerBase*>(data); + double time = sec + usec / 1000000.0; + handler->handle_page_flip(frame, time); +} + +void Card::call_page_flip_handlers() +{ + drmEventContext ev{}; + ev.version = DRM_EVENT_CONTEXT_VERSION; + ev.page_flip_handler = page_flip_handler; + + drmHandleEvent(fd(), &ev); +} + +int Card::disable_all() +{ + AtomicReq req(*this); + + for (Crtc* c : m_crtcs) { + req.add(c, { + { "ACTIVE", 0 }, + }); + } + + for (Plane* p : m_planes) { + req.add(p, { + { "FB_ID", 0 }, + { "CRTC_ID", 0 }, + }); + } + + return req.commit_sync(true); +} + +} // namespace kms diff --git a/kms++/src/connector.cpp b/kms++/src/connector.cpp new file mode 100644 index 0000000..cf6243c --- /dev/null +++ b/kms++/src/connector.cpp @@ -0,0 +1,298 @@ +#include <cstdio> +#include <unistd.h> +#include <fcntl.h> +#include <cassert> +#include <cmath> + +#include <kms++/kms++.h> +#include "helpers.h" + +using namespace std; + +namespace kms +{ +#ifndef DRM_MODE_CONNECTOR_DPI +#define DRM_MODE_CONNECTOR_DPI 17 +#endif +#ifndef DRM_MODE_CONNECTOR_WRITEBACK +#define DRM_MODE_CONNECTOR_WRITEBACK 18 +#endif +#ifndef DRM_MODE_CONNECTOR_SPI +#define DRM_MODE_CONNECTOR_SPI 19 +#endif +#ifndef DRM_MODE_CONNECTOR_USB +#define DRM_MODE_CONNECTOR_USB 20 +#endif + +static const map<int, string> connector_names = { + { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, + { DRM_MODE_CONNECTOR_VGA, "VGA" }, + { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, + { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, + { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, + { DRM_MODE_CONNECTOR_Composite, "Composite" }, + { DRM_MODE_CONNECTOR_SVIDEO, "S-Video" }, + { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, + { DRM_MODE_CONNECTOR_Component, "Component" }, + { DRM_MODE_CONNECTOR_9PinDIN, "9-Pin-DIN" }, + { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, + { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, + { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, + { DRM_MODE_CONNECTOR_TV, "TV" }, + { DRM_MODE_CONNECTOR_eDP, "eDP" }, + { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, + { DRM_MODE_CONNECTOR_DSI, "DSI" }, + { DRM_MODE_CONNECTOR_DPI, "DPI" }, + { DRM_MODE_CONNECTOR_WRITEBACK, "Writeback" }, + { DRM_MODE_CONNECTOR_SPI, "SPI" }, + { DRM_MODE_CONNECTOR_USB, "USB" }, +}; + +static const map<int, string> connection_str = { + { 0, "<unknown>" }, + { DRM_MODE_CONNECTED, "Connected" }, + { DRM_MODE_DISCONNECTED, "Disconnected" }, + { DRM_MODE_UNKNOWNCONNECTION, "Unknown" }, +}; + +static const map<int, string> subpix_str = { +#define DEF_SUBPIX(c) \ + { \ + DRM_MODE_SUBPIXEL_##c, #c \ + } + DEF_SUBPIX(UNKNOWN), + DEF_SUBPIX(HORIZONTAL_RGB), + DEF_SUBPIX(HORIZONTAL_BGR), + DEF_SUBPIX(VERTICAL_RGB), + DEF_SUBPIX(VERTICAL_BGR), + DEF_SUBPIX(NONE), +#undef DEF_SUBPIX +}; + +struct ConnectorPriv { + drmModeConnectorPtr drm_connector; +}; + +Connector::Connector(Card& card, uint32_t id, uint32_t idx) + : DrmPropObject(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); + + // XXX drmModeGetConnector() does forced probe, which seems to change (at least) EDID blob id. + // XXX So refresh the props again here. + refresh_props(); + + const auto& name = connector_names.at(m_priv->drm_connector->connector_type); + m_fullname = name + "-" + to_string(m_priv->drm_connector->connector_type_id); +} + +Connector::~Connector() +{ + drmModeFreeConnector(m_priv->drm_connector); + delete m_priv; +} + +void Connector::refresh() +{ + drmModeFreeConnector(m_priv->drm_connector); + + m_priv->drm_connector = drmModeGetConnector(this->card().fd(), this->id()); + assert(m_priv->drm_connector); + + // XXX drmModeGetConnector() does forced probe, which seems to change (at least) EDID blob id. + // XXX So refresh the props again here. + refresh_props(); + + const auto& name = connector_names.at(m_priv->drm_connector->connector_type); + m_fullname = name + "-" + to_string(m_priv->drm_connector->connector_type_id); +} + +void Connector::setup() +{ + if (m_priv->drm_connector->encoder_id != 0) + m_current_encoder = card().get_encoder(m_priv->drm_connector->encoder_id); + else + m_current_encoder = 0; + + if (m_current_encoder) + m_saved_crtc = m_current_encoder->get_crtc(); + else + m_saved_crtc = 0; +} + +void Connector::restore_mode() +{ + if (m_saved_crtc) + m_saved_crtc->restore_mode(this); +} + +Videomode Connector::get_default_mode() const +{ + if (m_priv->drm_connector->count_modes == 0) + return Videomode(); + + drmModeModeInfo drmmode = m_priv->drm_connector->modes[0]; + + return drm_mode_to_video_mode(drmmode); +} + +Videomode Connector::get_mode(const string& mode) const +{ + auto c = m_priv->drm_connector; + + size_t idx = mode.find('@'); + + string name = idx == string::npos ? mode : mode.substr(0, idx); + float vrefresh = idx == string::npos ? 0.0 : stod(mode.substr(idx + 1)); + + for (int i = 0; i < c->count_modes; i++) { + Videomode m = drm_mode_to_video_mode(c->modes[i]); + + if (m.name != name) + continue; + + if (vrefresh && vrefresh != m.calculated_vrefresh()) + continue; + + return m; + } + + throw invalid_argument(mode + ": mode not found"); +} + +Videomode Connector::get_mode(unsigned xres, unsigned yres, float vrefresh, bool ilace) const +{ + auto c = m_priv->drm_connector; + + for (int i = 0; i < c->count_modes; i++) { + Videomode m = drm_mode_to_video_mode(c->modes[i]); + + if (m.hdisplay != xres || m.vdisplay != yres) + continue; + + if (ilace != m.interlace()) + continue; + + if (vrefresh && vrefresh != m.calculated_vrefresh()) + continue; + + return m; + } + + // If not found, do another round using rounded vrefresh + + for (int i = 0; i < c->count_modes; i++) { + Videomode m = drm_mode_to_video_mode(c->modes[i]); + + if (m.hdisplay != xres || m.vdisplay != yres) + continue; + + if (ilace != m.interlace()) + continue; + + if (vrefresh && vrefresh != roundf(m.calculated_vrefresh())) + continue; + + return m; + } + + throw invalid_argument("mode not found"); +} + +bool Connector::connected() const +{ + return m_priv->drm_connector->connection == DRM_MODE_CONNECTED || + m_priv->drm_connector->connection == DRM_MODE_UNKNOWNCONNECTION; +} + +ConnectorStatus Connector::connector_status() const +{ + switch (m_priv->drm_connector->connection) { + case DRM_MODE_CONNECTED: + return ConnectorStatus::Connected; + case DRM_MODE_DISCONNECTED: + return ConnectorStatus::Disconnected; + default: + return ConnectorStatus::Unknown; + } +} + +vector<Crtc*> Connector::get_possible_crtcs() const +{ + vector<Crtc*> 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; +} + +Crtc* Connector::get_current_crtc() const +{ + if (m_current_encoder) + return m_current_encoder->get_crtc(); + else + return 0; +} + +uint32_t Connector::connector_type() const +{ + return m_priv->drm_connector->connector_type; +} + +uint32_t Connector::connector_type_id() const +{ + return m_priv->drm_connector->connector_type_id; +} + +uint32_t Connector::mmWidth() const +{ + return m_priv->drm_connector->mmWidth; +} + +uint32_t Connector::mmHeight() const +{ + return m_priv->drm_connector->mmHeight; +} + +uint32_t Connector::subpixel() const +{ + return m_priv->drm_connector->subpixel; +} + +const string& Connector::subpixel_str() const +{ + return subpix_str.at(subpixel()); +} + +std::vector<Videomode> Connector::get_modes() const +{ + vector<Videomode> modes; + + for (int i = 0; i < m_priv->drm_connector->count_modes; i++) + modes.push_back(drm_mode_to_video_mode( + m_priv->drm_connector->modes[i])); + + return modes; +} + +std::vector<Encoder*> Connector::get_encoders() const +{ + vector<Encoder*> encoders; + + for (int i = 0; i < m_priv->drm_connector->count_encoders; i++) { + auto enc = card().get_encoder(m_priv->drm_connector->encoders[i]); + encoders.push_back(enc); + } + return encoders; +} + +} // namespace kms diff --git a/kms++/src/crtc.cpp b/kms++/src/crtc.cpp new file mode 100644 index 0000000..ca00252 --- /dev/null +++ b/kms++/src/crtc.cpp @@ -0,0 +1,196 @@ +#include <cstdio> +#include <unistd.h> +#include <fcntl.h> +#include <cassert> + +#include <kms++/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) + : DrmPropObject(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::refresh() +{ + drmModeFreeCrtc(m_priv->drm_crtc); + + m_priv->drm_crtc = drmModeGetCrtc(this->card().fd(), this->id()); + assert(m_priv->drm_crtc); +} + +void Crtc::setup() +{ + for (Plane* plane : card().get_planes()) { + if (plane->supports_crtc(this)) + m_possible_planes.push_back(plane); + } +} + +void Crtc::restore_mode(Connector* conn) +{ + auto c = m_priv->drm_crtc; + + uint32_t conns[] = { conn->id() }; + + drmModeSetCrtc(card().fd(), id(), c->buffer_id, + c->x, c->y, + conns, 1, &c->mode); +} + +int Crtc::set_mode(Connector* conn, const Videomode& mode) +{ + AtomicReq req(card()); + + unique_ptr<Blob> blob = mode.to_blob(card()); + + req.add(conn, { + { "CRTC_ID", this->id() }, + }); + + req.add(this, { + { "ACTIVE", 1 }, + { "MODE_ID", blob->id() }, + }); + + int r = req.commit_sync(true); + + refresh(); + + return r; +} + +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); +} + +int Crtc::disable_mode() +{ + return drmModeSetCrtc(card().fd(), id(), 0, 0, 0, 0, 0, 0); +} + +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)); +} + +int Crtc::disable_plane(Plane* plane) +{ + return drmModeSetPlane(card().fd(), plane->id(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +} + +Plane* Crtc::get_primary_plane() +{ + Plane* primary = nullptr; + + for (Plane* p : get_possible_planes()) { + if (p->plane_type() != PlaneType::Primary) + continue; + + if (p->crtc_id() == id()) + return p; + + primary = p; + } + + if (primary) + return primary; + + throw invalid_argument(string("No primary plane for crtc ") + to_string(id())); +} + +int Crtc::page_flip(Framebuffer& fb, void* data) +{ + return drmModePageFlip(card().fd(), id(), fb.id(), DRM_MODE_PAGE_FLIP_EVENT, data); +} + +uint32_t Crtc::buffer_id() const +{ + return m_priv->drm_crtc->buffer_id; +} + +uint32_t Crtc::x() const +{ + return m_priv->drm_crtc->x; +} + +uint32_t Crtc::y() const +{ + return m_priv->drm_crtc->y; +} + +uint32_t Crtc::width() const +{ + return m_priv->drm_crtc->width; +} + +uint32_t Crtc::height() const +{ + return m_priv->drm_crtc->height; +} + +int Crtc::mode_valid() const +{ + return m_priv->drm_crtc->mode_valid; +} + +Videomode Crtc::mode() const +{ + return drm_mode_to_video_mode(m_priv->drm_crtc->mode); +} + +int Crtc::legacy_gamma_size() const +{ + return m_priv->drm_crtc->gamma_size; +} + +void Crtc::legacy_gamma_set(vector<tuple<uint16_t, uint16_t, uint16_t>> v) +{ + uint32_t len = v.size(); + vector<uint16_t> red(len); + vector<uint16_t> green(len); + vector<uint16_t> blue(len); + + for (uint32_t i = 0; i < len; ++i) { + red[i] = get<0>(v[i]); + green[i] = get<1>(v[i]); + blue[i] = get<2>(v[i]); + } + + drmModeCrtcSetGamma(card().fd(), id(), len, red.data(), green.data(), blue.data()); +} + +} // namespace kms diff --git a/kms++/src/dmabufframebuffer.cpp b/kms++/src/dmabufframebuffer.cpp new file mode 100644 index 0000000..33d65f7 --- /dev/null +++ b/kms++/src/dmabufframebuffer.cpp @@ -0,0 +1,161 @@ + +#include <cstring> +#include <cerrno> + +#include <stdexcept> +#include <sys/mman.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <linux/dma-buf.h> + +#include <kms++/kms++.h> + +using namespace std; + +namespace kms +{ +DmabufFramebuffer::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) + : DmabufFramebuffer(card, width, height, fourcc_str_to_pixel_format(format), fds, pitches, offsets, modifiers) +{ +} + +DmabufFramebuffer::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) + : Framebuffer(card, width, height) +{ + int r; + + m_format = format; + + const PixelFormatInfo& format_info = get_pixel_format_info(format); + + m_num_planes = format_info.num_planes; + + if (fds.size() != m_num_planes || pitches.size() != m_num_planes || offsets.size() != m_num_planes) + throw std::invalid_argument("the size of fds, pitches and offsets has to match number of planes"); + + for (int i = 0; i < format_info.num_planes; ++i) { + FramebufferPlane& plane = m_planes.at(i); + + plane.prime_fd = dup(fds[i]); + + r = drmPrimeFDToHandle(card.fd(), plane.prime_fd, &plane.handle); + if (r) + throw invalid_argument(string("drmPrimeFDToHandle: ") + strerror(errno)); + + plane.stride = pitches[i]; + plane.offset = offsets[i]; + plane.modifier = modifiers.empty() ? 0 : modifiers[i]; + plane.size = plane.stride * height; + plane.map = 0; + } + + uint32_t id; + uint32_t bo_handles[4] = { m_planes[0].handle, m_planes[1].handle, m_planes[2].handle, m_planes[3].handle }; + pitches.resize(4); + offsets.resize(4); + + if (modifiers.empty()) { + r = drmModeAddFB2(card.fd(), width, height, pixel_format_to_fourcc(format), + bo_handles, pitches.data(), offsets.data(), &id, 0); + if (r) + throw invalid_argument(string("drmModeAddFB2 failed: ") + strerror(errno)); + } else { + modifiers.resize(4); + r = drmModeAddFB2WithModifiers(card.fd(), width, height, pixel_format_to_fourcc(format), + bo_handles, pitches.data(), offsets.data(), modifiers.data(), &id, DRM_MODE_FB_MODIFIERS); + if (r) + throw invalid_argument(string("drmModeAddFB2WithModifiers failed: ") + strerror(errno)); + } + + set_id(id); +} + +DmabufFramebuffer::~DmabufFramebuffer() +{ + drmModeRmFB(card().fd(), id()); + + for (uint i = 0; i < m_num_planes; ++i) { + FramebufferPlane& plane = m_planes.at(i); + + if (plane.map) + munmap(plane.map, plane.size); + + if (plane.prime_fd >= 0) + ::close(plane.prime_fd); + } +} + +uint8_t* DmabufFramebuffer::map(unsigned plane) +{ + FramebufferPlane& p = m_planes.at(plane); + + if (p.map) + return p.map; + + p.map = static_cast<uint8_t*>(mmap(0, p.size, PROT_READ | PROT_WRITE, MAP_SHARED, + p.prime_fd, 0)); + if (p.map == MAP_FAILED) + throw invalid_argument(string("mmap failed: ") + strerror(errno)); + + return p.map; +} + +int DmabufFramebuffer::prime_fd(unsigned plane) +{ + FramebufferPlane& p = m_planes.at(plane); + + return p.prime_fd; +} + +void DmabufFramebuffer::begin_cpu_access(CpuAccess access) +{ + if (m_sync_flags != 0) + throw runtime_error("begin_cpu sync already started"); + + switch (access) { + case CpuAccess::Read: + m_sync_flags = DMA_BUF_SYNC_READ; + break; + case CpuAccess::Write: + m_sync_flags = DMA_BUF_SYNC_WRITE; + break; + case CpuAccess::ReadWrite: + m_sync_flags = DMA_BUF_SYNC_RW; + break; + } + + dma_buf_sync dbs{ + .flags = DMA_BUF_SYNC_START | m_sync_flags + }; + + for (uint32_t p = 0; p < m_num_planes; ++p) { + int r = ioctl(prime_fd(p), DMA_BUF_IOCTL_SYNC, &dbs); + if (r) + throw runtime_error("DMA_BUF_IOCTL_SYNC failed"); + } +} + +void DmabufFramebuffer::end_cpu_access() +{ + if (m_sync_flags == 0) + throw runtime_error("begin_cpu sync not started"); + + dma_buf_sync dbs{ + .flags = DMA_BUF_SYNC_END | m_sync_flags + }; + + for (uint32_t p = 0; p < m_num_planes; ++p) { + int r = ioctl(prime_fd(p), DMA_BUF_IOCTL_SYNC, &dbs); + if (r) + throw runtime_error("DMA_BUF_IOCTL_SYNC failed"); + } + + m_sync_flags = 0; +} + +} // namespace kms diff --git a/kms++/src/drmobject.cpp b/kms++/src/drmobject.cpp new file mode 100644 index 0000000..f01c8af --- /dev/null +++ b/kms++/src/drmobject.cpp @@ -0,0 +1,32 @@ +#include <cstring> +#include <iostream> +#include <stdexcept> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <kms++/kms++.h> + +using namespace std; + +namespace kms +{ +DrmObject::DrmObject(Card& card, uint32_t object_type) + : m_card(card), m_id(-1), m_object_type(object_type), m_idx(0) +{ +} + +DrmObject::DrmObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx) + : m_card(card), m_id(id), m_object_type(object_type), m_idx(idx) +{ +} + +DrmObject::~DrmObject() +{ +} + +void DrmObject::set_id(uint32_t id) +{ + m_id = id; +} +} // namespace kms diff --git a/kms++/src/drmpropobject.cpp b/kms++/src/drmpropobject.cpp new file mode 100644 index 0000000..616aef7 --- /dev/null +++ b/kms++/src/drmpropobject.cpp @@ -0,0 +1,101 @@ +#include <cstring> +#include <iostream> +#include <stdexcept> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <kms++/kms++.h> + +using namespace std; + +namespace kms +{ +DrmPropObject::DrmPropObject(Card& card, uint32_t object_type) + : DrmObject(card, object_type) +{ +} + +DrmPropObject::DrmPropObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx) + : DrmObject(card, id, object_type, idx) +{ + refresh_props(); +} + +DrmPropObject::~DrmPropObject() +{ +} + +void DrmPropObject::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); +} + +Property* DrmPropObject::get_prop(const string& name) const +{ + for (auto pair : m_prop_values) { + auto prop = card().get_prop(pair.first); + + if (name == prop->name()) + return prop; + } + + return nullptr; +} + +uint64_t DrmPropObject::get_prop_value(uint32_t id) const +{ + return m_prop_values.at(id); +} + +uint64_t DrmPropObject::get_prop_value(const string& name) const +{ + for (auto pair : m_prop_values) { + auto prop = card().get_prop(pair.first); + if (name == prop->name()) + return m_prop_values.at(prop->id()); + } + + throw invalid_argument("property not found: " + name); +} + +unique_ptr<Blob> DrmPropObject::get_prop_value_as_blob(const string& name) const +{ + uint32_t blob_id = (uint32_t)get_prop_value(name); + + return unique_ptr<Blob>(new Blob(card(), blob_id)); +} + +int DrmPropObject::set_prop_value(Property* prop, uint64_t value) +{ + return drmModeObjectSetProperty(card().fd(), this->id(), this->object_type(), prop->id(), value); +} + +int DrmPropObject::set_prop_value(uint32_t id, uint64_t value) +{ + return drmModeObjectSetProperty(card().fd(), this->id(), this->object_type(), id, value); +} + +int DrmPropObject::set_prop_value(const string& name, uint64_t value) +{ + Property* prop = get_prop(name); + + if (prop == nullptr) + throw invalid_argument("property not found: " + name); + + return set_prop_value(prop->id(), value); +} + +} // namespace kms diff --git a/kms++/src/dumbframebuffer.cpp b/kms++/src/dumbframebuffer.cpp new file mode 100644 index 0000000..421dac4 --- /dev/null +++ b/kms++/src/dumbframebuffer.cpp @@ -0,0 +1,143 @@ + +#include <cstring> +#include <cerrno> +#include <stdexcept> +#include <sys/mman.h> +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <fcntl.h> +#include <unistd.h> +#include <drm_fourcc.h> +#include <drm.h> +#include <drm_mode.h> + +#include <kms++/kms++.h> + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +using namespace std; + +namespace kms +{ +DumbFramebuffer::DumbFramebuffer(Card& card, uint32_t width, uint32_t height, const string& fourcc) + : DumbFramebuffer(card, width, height, fourcc_str_to_pixel_format(fourcc)) +{ +} + +DumbFramebuffer::DumbFramebuffer(Card& card, uint32_t width, uint32_t height, PixelFormat format) + : Framebuffer(card, width, height), m_format(format) +{ + int r; + + const PixelFormatInfo& format_info = get_pixel_format_info(m_format); + + m_num_planes = format_info.num_planes; + + for (int i = 0; i < format_info.num_planes; ++i) { + FramebufferPlane& plane = m_planes.at(i); + + auto [w, h, bpp] = format_info.dumb_size(width, height, i); + + /* create dumb buffer */ + struct drm_mode_create_dumb creq = drm_mode_create_dumb(); + creq.width = w; + creq.height = h; + creq.bpp = bpp; + r = drmIoctl(card.fd(), DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (r) + throw invalid_argument(string("DRM_IOCTL_MODE_CREATE_DUMB failed: ") + strerror(errno)); + + plane.handle = creq.handle; + plane.stride = creq.pitch; + plane.size = creq.height * creq.pitch; + plane.offset = 0; + plane.map = 0; + plane.prime_fd = -1; + } + + /* create framebuffer object for the dumb-buffer */ + uint32_t bo_handles[4] = { + m_planes[0].handle, + m_planes[1].handle, + m_planes[2].handle, + m_planes[3].handle, + }; + uint32_t pitches[4] = { + m_planes[0].stride, + m_planes[1].stride, + m_planes[2].stride, + m_planes[3].stride, + }; + uint32_t offsets[4] = { + m_planes[0].offset, + m_planes[1].offset, + m_planes[2].offset, + m_planes[3].offset, + }; + uint32_t id; + r = drmModeAddFB2(card.fd(), width, height, pixel_format_to_fourcc(format), + bo_handles, pitches, offsets, &id, 0); + if (r) + throw invalid_argument(string("drmModeAddFB2 failed: ") + strerror(errno)); + + set_id(id); +} + +DumbFramebuffer::~DumbFramebuffer() +{ + /* delete framebuffer */ + drmModeRmFB(card().fd(), id()); + + for (uint i = 0; i < m_num_planes; ++i) { + FramebufferPlane& plane = m_planes.at(i); + + /* unmap buffer */ + if (plane.map) + 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); + if (plane.prime_fd >= 0) + ::close(plane.prime_fd); + } +} + +uint8_t* DumbFramebuffer::map(unsigned plane) +{ + FramebufferPlane& p = m_planes.at(plane); + + if (p.map) + return p.map; + + /* prepare buffer for memory mapping */ + struct drm_mode_map_dumb mreq = drm_mode_map_dumb(); + mreq.handle = p.handle; + int r = drmIoctl(card().fd(), DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (r) + throw invalid_argument(string("DRM_IOCTL_MODE_MAP_DUMB failed: ") + strerror(errno)); + + /* perform actual memory mapping */ + p.map = static_cast<uint8_t*>(mmap(0, p.size, PROT_READ | PROT_WRITE, MAP_SHARED, + card().fd(), mreq.offset)); + if (p.map == MAP_FAILED) + throw invalid_argument(string("mmap failed: ") + strerror(errno)); + + return p.map; +} + +int DumbFramebuffer::prime_fd(unsigned int plane) +{ + if (m_planes.at(plane).prime_fd >= 0) + return m_planes.at(plane).prime_fd; + + int r = drmPrimeHandleToFD(card().fd(), m_planes.at(plane).handle, + DRM_CLOEXEC | O_RDWR, &m_planes.at(plane).prime_fd); + if (r) + throw std::runtime_error("drmPrimeHandleToFD failed"); + + return m_planes.at(plane).prime_fd; +} + +} // namespace kms diff --git a/kms++/src/encoder.cpp b/kms++/src/encoder.cpp new file mode 100644 index 0000000..8144b9f --- /dev/null +++ b/kms++/src/encoder.cpp @@ -0,0 +1,87 @@ +#include <cstdio> +#include <iostream> +#include <unistd.h> +#include <fcntl.h> +#include <cassert> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <kms++/kms++.h> + +using namespace std; + +namespace kms +{ +struct EncoderPriv { + drmModeEncoderPtr drm_encoder; +}; + +static const map<int, string> encoder_types = { +#define DEF_ENC(c) \ + { \ + DRM_MODE_ENCODER_##c, #c \ + } + DEF_ENC(NONE), + DEF_ENC(DAC), + DEF_ENC(TMDS), + DEF_ENC(LVDS), + DEF_ENC(TVDAC), + DEF_ENC(VIRTUAL), + DEF_ENC(DSI), + { 7, "DPMST" }, + { 8, "DPI" }, +#undef DEF_ENC +}; + +Encoder::Encoder(Card& card, uint32_t id, uint32_t idx) + : DrmPropObject(card, id, DRM_MODE_OBJECT_ENCODER, idx) +{ + 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::refresh() +{ + drmModeFreeEncoder(m_priv->drm_encoder); + + m_priv->drm_encoder = drmModeGetEncoder(this->card().fd(), this->id()); + assert(m_priv->drm_encoder); +} + +Crtc* Encoder::get_crtc() const +{ + if (m_priv->drm_encoder->crtc_id) + return card().get_crtc(m_priv->drm_encoder->crtc_id); + else + return 0; +} + +vector<Crtc*> Encoder::get_possible_crtcs() const +{ + unsigned bits = m_priv->drm_encoder->possible_crtcs; + vector<Crtc*> crtcs; + + for (int idx = 0; bits; idx++, bits >>= 1) { + if ((bits & 1) == 0) + continue; + + auto crtc = card().get_crtcs()[idx]; + crtcs.push_back(crtc); + } + + return crtcs; +} + +const string& Encoder::get_encoder_type() const +{ + return encoder_types.at(m_priv->drm_encoder->encoder_type); +} + +} // namespace kms diff --git a/kms++/src/extframebuffer.cpp b/kms++/src/extframebuffer.cpp new file mode 100644 index 0000000..d67cbad --- /dev/null +++ b/kms++/src/extframebuffer.cpp @@ -0,0 +1,64 @@ + +#include <cstring> +#include <cerrno> + +#include <stdexcept> +#include <sys/mman.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <kms++/kms++.h> + +using namespace std; + +namespace kms +{ +ExtFramebuffer::ExtFramebuffer(Card& card, uint32_t width, uint32_t height, PixelFormat format, + vector<uint32_t> handles, vector<uint32_t> pitches, vector<uint32_t> offsets, vector<uint64_t> modifiers) + : Framebuffer(card, width, height) +{ + m_format = format; + + const PixelFormatInfo& format_info = get_pixel_format_info(format); + + m_num_planes = format_info.num_planes; + + if (handles.size() != m_num_planes || pitches.size() != m_num_planes || offsets.size() != m_num_planes) + throw std::invalid_argument("the size of handles, pitches and offsets has to match number of planes"); + + for (int i = 0; i < format_info.num_planes; ++i) { + FramebufferPlane& plane = m_planes.at(i); + + plane.handle = handles[i]; + plane.stride = pitches[i]; + plane.offset = offsets[i]; + plane.modifier = modifiers.empty() ? 0 : modifiers[i]; + plane.size = plane.stride * height; + plane.map = 0; + } + + uint32_t id; + handles.resize(4); + pitches.resize(4); + offsets.resize(4); + int r; + + if (modifiers.empty()) { + r = drmModeAddFB2(card.fd(), width, height, pixel_format_to_fourcc(format), handles.data(), pitches.data(), offsets.data(), &id, 0); + } else { + modifiers.resize(4); + r = drmModeAddFB2WithModifiers(card.fd(), width, height, pixel_format_to_fourcc(format), handles.data(), pitches.data(), offsets.data(), modifiers.data(), &id, DRM_MODE_FB_MODIFIERS); + } + + if (r) + throw std::invalid_argument(string("Failed to create ExtFramebuffer: ") + strerror(r)); + + set_id(id); +} + +ExtFramebuffer::~ExtFramebuffer() +{ + drmModeRmFB(card().fd(), id()); +} + +} // namespace kms diff --git a/kms++/src/framebuffer.cpp b/kms++/src/framebuffer.cpp new file mode 100644 index 0000000..3d251ce --- /dev/null +++ b/kms++/src/framebuffer.cpp @@ -0,0 +1,71 @@ +#include <algorithm> +#include <cstring> +#include <stdexcept> +#include <sys/mman.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <kms++/kms++.h> + +using namespace std; + +namespace kms +{ +Framebuffer::Framebuffer(Card& card, uint32_t width, uint32_t height) + : DrmObject(card, DRM_MODE_OBJECT_FB), m_width(width), m_height(height) +{ + card.m_framebuffers.push_back(this); +} + +Framebuffer::Framebuffer(Card& card, uint32_t id) + : DrmObject(card, id, DRM_MODE_OBJECT_FB) +{ + auto fb = drmModeGetFB2(card.fd(), id); + + if (fb) { + m_width = fb->width; + m_height = fb->height; + m_fourcc = fb->pixel_format; + try { + m_format = fourcc_to_pixel_format(m_fourcc); + } catch (const invalid_argument& e) { + m_format = PixelFormat::Undefined; + } + + drmModeFreeFB2(fb); + } else { + m_width = m_height = 0; + } + + card.m_framebuffers.push_back(this); +} + +void Framebuffer::flush(uint32_t x, uint32_t y, uint32_t width, uint32_t height) +{ + drmModeClip clip{}; + clip.x1 = x; + clip.y1 = y; + clip.x2 = x + width; + clip.y2 = y + height; + + drmModeDirtyFB(card().fd(), id(), &clip, 1); +} + +void Framebuffer::flush() +{ + drmModeClip clip{}; + clip.x1 = clip.y1 = 0; + clip.x2 = width(); + clip.y2 = height(); + + drmModeDirtyFB(card().fd(), id(), &clip, 1); +} + +Framebuffer::~Framebuffer() +{ + auto& fbs = card().m_framebuffers; + auto iter = find(fbs.begin(), fbs.end(), this); + card().m_framebuffers.erase(iter); +} + +} // namespace kms diff --git a/kms++/src/helpers.cpp b/kms++/src/helpers.cpp new file mode 100644 index 0000000..9079a23 --- /dev/null +++ b/kms++/src/helpers.cpp @@ -0,0 +1,72 @@ + +#include <kms++/kms++.h> +#include "helpers.h" +#include <cstring> + +#define CPY(field) dst.field = src.field + +namespace kms +{ +Videomode drm_mode_to_video_mode(const drmModeModeInfo& drmmode) +{ + Videomode mode = {}; + + auto& src = drmmode; + auto& dst = mode; + + CPY(clock); + + CPY(hdisplay); + CPY(hsync_start); + CPY(hsync_end); + CPY(htotal); + CPY(hskew); + + CPY(vdisplay); + CPY(vsync_start); + CPY(vsync_end); + CPY(vtotal); + CPY(vscan); + + CPY(vrefresh); + + CPY(flags); + CPY(type); + + mode.name = drmmode.name; + + return mode; +} + +drmModeModeInfo video_mode_to_drm_mode(const Videomode& mode) +{ + drmModeModeInfo drmmode = {}; + + auto& src = mode; + auto& dst = drmmode; + + CPY(clock); + + CPY(hdisplay); + CPY(hsync_start); + CPY(hsync_end); + CPY(htotal); + CPY(hskew); + + CPY(vdisplay); + CPY(vsync_start); + CPY(vsync_end); + CPY(vtotal); + CPY(vscan); + + CPY(vrefresh); + + CPY(flags); + CPY(type); + + strncpy(drmmode.name, mode.name.c_str(), sizeof(drmmode.name)); + drmmode.name[sizeof(drmmode.name) - 1] = 0; + + return drmmode; +} +} // namespace kms diff --git a/kms++/src/helpers.h b/kms++/src/helpers.h new file mode 100644 index 0000000..58926bf --- /dev/null +++ b/kms++/src/helpers.h @@ -0,0 +1,12 @@ +#pragma once + +#include <xf86drm.h> +#include <xf86drmMode.h> + +namespace kms +{ +struct Videomode; + +Videomode drm_mode_to_video_mode(const drmModeModeInfo& drmmode); +drmModeModeInfo video_mode_to_drm_mode(const Videomode& mode); +} // namespace kms diff --git a/kms++/src/mode_cvt.cpp b/kms++/src/mode_cvt.cpp new file mode 100644 index 0000000..a7a10b8 --- /dev/null +++ b/kms++/src/mode_cvt.cpp @@ -0,0 +1,150 @@ +// Supports CVT 1.2 reduced blanking modes v1 and v2 + +#include <kms++/kms++.h> +#include <cmath> + +using namespace std; + +namespace kms +{ +static float CELL_GRAN = 8; +static float CELL_GRAN_RND = round(CELL_GRAN); + +struct CVTConsts { + float CLOCK_STEP; + float MIN_V_BPORCH; + float RB_H_BLANK; + float RB_H_FPORCH; + float RB_H_SYNC; + float RB_H_BPORCH; + float RB_MIN_V_BLANK; + float RB_V_FPORCH; + float REFRESH_MULTIPLIER; +}; + +static const CVTConsts cvt_consts_v1 = { + .CLOCK_STEP = 0.25, // Fixed + .MIN_V_BPORCH = 6, // Min + .RB_H_BLANK = 160, // Fixed + .RB_H_FPORCH = 48, // Fixed + .RB_H_SYNC = 32, // Fixed + .RB_H_BPORCH = 80, // Fixed + .RB_MIN_V_BLANK = 460, // Min + .RB_V_FPORCH = 3, // Fixed + .REFRESH_MULTIPLIER = 1, // Fixed +}; + +static const CVTConsts cvt_consts_v2 = { + .CLOCK_STEP = 0.001, // Fixed + .MIN_V_BPORCH = 6, // Fixed + .RB_H_BLANK = 80, // Fixed + .RB_H_FPORCH = 8, // Fixed + .RB_H_SYNC = 32, // Fixed + .RB_H_BPORCH = 40, // Fixed + .RB_MIN_V_BLANK = 460, // Min + .RB_V_FPORCH = 1, // Min + .REFRESH_MULTIPLIER = 1, // or 1000/1001 +}; + +Videomode videomode_from_cvt(uint32_t hact, uint32_t vact, uint32_t refresh, bool ilace, bool reduced_v2, bool video_optimized) +{ + CVTConsts c = reduced_v2 ? cvt_consts_v2 : cvt_consts_v1; + + if (video_optimized) + c.REFRESH_MULTIPLIER = 1000.0 / 1001.0; + + bool INT_RQD = ilace; + + float H_PIXELS = hact; + float V_LINES = vact; + float IP_FREQ_RQD = refresh ? refresh : 60; + if (ilace) + IP_FREQ_RQD /= 2; + + float V_SYNC_RND; + + if (reduced_v2) { + V_SYNC_RND = 8; + } else { + if (hact * 3 == vact * 4) + V_SYNC_RND = 4; + else if (hact * 9 == vact * 16) + V_SYNC_RND = 5; + else if (hact * 10 == vact * 16) + V_SYNC_RND = 6; + else if (hact == 1280 && (vact == 1024 || vact == 768)) + V_SYNC_RND = 7; + else + V_SYNC_RND = 10; + } + + // 5.2.1 + float V_FIELD_RATE_RQD = INT_RQD ? IP_FREQ_RQD * 2 : IP_FREQ_RQD; + + // 5.2.2 + float H_PIXELS_RND = floor(H_PIXELS / CELL_GRAN_RND) * CELL_GRAN_RND; + + // 5.2.3 + float LEFT_MARGIN = 0; + float RIGHT_MARGIN = 0; + + // 5.2.4 + float TOTAL_ACTIVE_PIXELS = H_PIXELS_RND + LEFT_MARGIN + RIGHT_MARGIN; + + // 5.2.5 + float V_LINES_RND = INT_RQD ? floor(V_LINES / 2) : floor(V_LINES); + + // 5.2.6 + float TOP_MARGIN = 0; + float BOT_MARGIN = 0; + + // 5.2.7 + float INTERLACE = INT_RQD ? 0.5 : 0; + + // 5.4.8 + float H_PERIOD_EST = ((1000000 / V_FIELD_RATE_RQD) - c.RB_MIN_V_BLANK) / (V_LINES_RND + TOP_MARGIN + BOT_MARGIN); + + // 5.4.9 + float VBI_LINES = floor(c.RB_MIN_V_BLANK / H_PERIOD_EST) + 1; + + // 5.4.10 + float RB_MIN_VBI = c.RB_V_FPORCH + V_SYNC_RND + c.MIN_V_BPORCH; + float ACT_VBI_LINES = VBI_LINES < RB_MIN_VBI ? RB_MIN_VBI : VBI_LINES; + + // 5.4.11 + float TOTAL_V_LINES = ACT_VBI_LINES + V_LINES_RND + TOP_MARGIN + BOT_MARGIN + INTERLACE; + + // 5.4.12 + float TOTAL_PIXELS = c.RB_H_BLANK + TOTAL_ACTIVE_PIXELS; + + // 5.4.13 + float ACT_PIXEL_FREQ = c.CLOCK_STEP * floor((V_FIELD_RATE_RQD * TOTAL_V_LINES * TOTAL_PIXELS / 1000000 * c.REFRESH_MULTIPLIER) / c.CLOCK_STEP); + + // 5.4.14 + //float ACT_H_FREQ = 1000 * ACT_PIXEL_FREQ / TOTAL_PIXELS; + + // 5.4.15 + //float ACT_FIELD_RATE = 1000 * ACT_H_FREQ / TOTAL_V_LINES; + + // 5.4.16 + //float ACT_FRAME_RATE = INT_RQD ? ACT_FIELD_RATE / 2 : ACT_FIELD_RATE; + + // 3.4.3.7 Adjust vfp + if (reduced_v2) + c.RB_V_FPORCH = ACT_VBI_LINES - V_SYNC_RND - c.MIN_V_BPORCH; + + Videomode mode; + + mode = videomode_from_timings(ACT_PIXEL_FREQ * 1000, + H_PIXELS_RND, c.RB_H_FPORCH, c.RB_H_SYNC, c.RB_H_BPORCH, + V_LINES_RND * (INT_RQD ? 2 : 1), c.RB_V_FPORCH, V_SYNC_RND, ACT_VBI_LINES - V_SYNC_RND - c.RB_V_FPORCH); + + mode.set_hsync(SyncPolarity::Positive); + mode.set_vsync(SyncPolarity::Negative); + + mode.set_interlace(INT_RQD); + + return mode; +} + +} // namespace kms diff --git a/kms++/src/modedb.cpp b/kms++/src/modedb.cpp new file mode 100644 index 0000000..081100a --- /dev/null +++ b/kms++/src/modedb.cpp @@ -0,0 +1,58 @@ +#include <xf86drm.h> +#include <stdexcept> +#include <cmath> + +#include <kms++/modedb.h> + +using namespace std; + +namespace kms +{ +static const Videomode& find_from_table(const Videomode* modes, uint32_t width, uint32_t height, float vrefresh, bool ilace) +{ + for (unsigned i = 0; modes[i].clock; ++i) { + const Videomode& m = modes[i]; + + if (m.hdisplay != width || m.vdisplay != height) + continue; + + if (ilace != m.interlace()) + continue; + + if (vrefresh && vrefresh != m.calculated_vrefresh()) + continue; + + return m; + } + + // If not found, do another round using rounded vrefresh + + for (unsigned i = 0; modes[i].clock; ++i) { + const Videomode& m = modes[i]; + + if (m.hdisplay != width || m.vdisplay != height) + continue; + + if (ilace != m.interlace()) + continue; + + if (vrefresh && vrefresh != roundf(m.calculated_vrefresh())) + continue; + + return m; + } + + throw invalid_argument("mode not found"); +} + +const Videomode& find_dmt(uint32_t width, uint32_t height, float vrefresh, bool ilace) +{ + return find_from_table(dmt_modes, width, height, vrefresh, ilace); +} + +const Videomode& find_cea(uint32_t width, uint32_t height, float vrefresh, bool ilace) +{ + return find_from_table(cea_modes, width, height, vrefresh, ilace); +} + +} // namespace kms diff --git a/kms++/src/modedb_cea.cpp b/kms++/src/modedb_cea.cpp new file mode 100644 index 0000000..b09472f --- /dev/null +++ b/kms++/src/modedb_cea.cpp @@ -0,0 +1,465 @@ +/* From Linux kernel: drm_edid.c */ +/* + * Copyright (c) 2006 Luc Verhaegen (quirks list) + * Copyright (c) 2007-2008 Intel Corporation + * Jesse Barnes <jesse.barnes@intel.com> + * Copyright 2010 Red Hat, Inc. + * + * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from + * FB layer. + * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <kms++/modedb.h> + +#include <xf86drm.h> + +namespace kms +{ +#define DIV_ROUND(n, d) (((n) + (d) / 2) / (d)) + +#define DRM_MODE(nm, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs, f) \ + .name = nm, .clock = (c), \ + .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), .htotal = (ht), .hskew = (hsk), \ + .vdisplay = (vd), .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), .vscan = (vs), \ + .vrefresh = DIV_ROUND(c * 1000, ht * vt) * (((f)&DRM_MODE_FLAG_INTERLACE) ? 2 : 1), \ + .flags = (f), .type = 0 + +/* + * Probably taken from CEA-861 spec. + * This table is converted from xorg's hw/xfree86/modes/xf86EdidModes.c. + */ +const Videomode cea_modes[] = { + /* 1 - 640x480@60Hz */ + { + DRM_MODE("640x480", 25175, 640, 656, + 752, 800, 0, 480, 490, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 2 - 720x480@60Hz */ + { + DRM_MODE("720x480", 27000, 720, 736, + 798, 858, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 3 - 720x480@60Hz */ + { + DRM_MODE("720x480", 27000, 720, 736, + 798, 858, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 4 - 1280x720@60Hz */ + { + DRM_MODE("1280x720", 74250, 1280, 1390, + 1430, 1650, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 5 - 1920x1080i@60Hz */ + { + DRM_MODE("1920x1080i", 74250, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | + DRM_MODE_FLAG_INTERLACE), + }, + /* 6 - 720(1440)x480i@60Hz */ + { + DRM_MODE("720x480i", 13500, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 7 - 720(1440)x480i@60Hz */ + { + DRM_MODE("720x480i", 13500, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 8 - 720(1440)x240@60Hz */ + { + DRM_MODE("720x240", 13500, 720, 739, + 801, 858, 0, 240, 244, 247, 262, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_DBLCLK), + }, + /* 9 - 720(1440)x240@60Hz */ + { + DRM_MODE("720x240", 13500, 720, 739, + 801, 858, 0, 240, 244, 247, 262, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_DBLCLK), + }, + /* 10 - 2880x480i@60Hz */ + { + DRM_MODE("2880x480i", 54000, 2880, 2956, + 3204, 3432, 0, 480, 488, 494, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE), + }, + /* 11 - 2880x480i@60Hz */ + { + DRM_MODE("2880x480i", 54000, 2880, 2956, + 3204, 3432, 0, 480, 488, 494, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE), + }, + /* 12 - 2880x240@60Hz */ + { + DRM_MODE("2880x240", 54000, 2880, 2956, + 3204, 3432, 0, 240, 244, 247, 262, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 13 - 2880x240@60Hz */ + { + DRM_MODE("2880x240", 54000, 2880, 2956, + 3204, 3432, 0, 240, 244, 247, 262, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 14 - 1440x480@60Hz */ + { + DRM_MODE("1440x480", 54000, 1440, 1472, + 1596, 1716, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 15 - 1440x480@60Hz */ + { + DRM_MODE("1440x480", 54000, 1440, 1472, + 1596, 1716, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 16 - 1920x1080@60Hz */ + { + DRM_MODE("1920x1080", 148500, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 17 - 720x576@50Hz */ + { + DRM_MODE("720x576", 27000, 720, 732, + 796, 864, 0, 576, 581, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 18 - 720x576@50Hz */ + { + DRM_MODE("720x576", 27000, 720, 732, + 796, 864, 0, 576, 581, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 19 - 1280x720@50Hz */ + { + DRM_MODE("1280x720", 74250, 1280, 1720, + 1760, 1980, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 20 - 1920x1080i@50Hz */ + { + DRM_MODE("1920x1080i", 74250, 1920, 2448, + 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | + DRM_MODE_FLAG_INTERLACE), + }, + /* 21 - 720(1440)x576i@50Hz */ + { + DRM_MODE("720x576i", 13500, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 22 - 720(1440)x576i@50Hz */ + { + DRM_MODE("720x576i", 13500, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 23 - 720(1440)x288@50Hz */ + { + DRM_MODE("720x288", 13500, 720, 732, + 795, 864, 0, 288, 290, 293, 312, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_DBLCLK), + }, + /* 24 - 720(1440)x288@50Hz */ + { + DRM_MODE("720x288", 13500, 720, 732, + 795, 864, 0, 288, 290, 293, 312, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_DBLCLK), + }, + /* 25 - 2880x576i@50Hz */ + { + DRM_MODE("2880x576i", 54000, 2880, 2928, + 3180, 3456, 0, 576, 580, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE), + }, + /* 26 - 2880x576i@50Hz */ + { + DRM_MODE("2880x576i", 54000, 2880, 2928, + 3180, 3456, 0, 576, 580, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE), + }, + /* 27 - 2880x288@50Hz */ + { + DRM_MODE("2880x288", 54000, 2880, 2928, + 3180, 3456, 0, 288, 290, 293, 312, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 28 - 2880x288@50Hz */ + { + DRM_MODE("2880x288", 54000, 2880, 2928, + 3180, 3456, 0, 288, 290, 293, 312, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 29 - 1440x576@50Hz */ + { + DRM_MODE("1440x576", 54000, 1440, 1464, + 1592, 1728, 0, 576, 581, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 30 - 1440x576@50Hz */ + { + DRM_MODE("1440x576", 54000, 1440, 1464, + 1592, 1728, 0, 576, 581, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 31 - 1920x1080@50Hz */ + { + DRM_MODE("1920x1080", 148500, 1920, 2448, + 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 32 - 1920x1080@24Hz */ + { + DRM_MODE("1920x1080", 74250, 1920, 2558, + 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 33 - 1920x1080@25Hz */ + { + DRM_MODE("1920x1080", 74250, 1920, 2448, + 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 34 - 1920x1080@30Hz */ + { + DRM_MODE("1920x1080", 74250, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 35 - 2880x480@60Hz */ + { + DRM_MODE("2880x480", 108000, 2880, 2944, + 3192, 3432, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 36 - 2880x480@60Hz */ + { + DRM_MODE("2880x480", 108000, 2880, 2944, + 3192, 3432, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 37 - 2880x576@50Hz */ + { + DRM_MODE("2880x576", 108000, 2880, 2928, + 3184, 3456, 0, 576, 581, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 38 - 2880x576@50Hz */ + { + DRM_MODE("2880x576", 108000, 2880, 2928, + 3184, 3456, 0, 576, 581, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 39 - 1920x1080i@50Hz */ + { + DRM_MODE("1920x1080i", 72000, 1920, 1952, + 2120, 2304, 0, 1080, 1126, 1136, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE), + }, + /* 40 - 1920x1080i@100Hz */ + { + DRM_MODE("1920x1080i", 148500, 1920, 2448, + 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | + DRM_MODE_FLAG_INTERLACE), + }, + /* 41 - 1280x720@100Hz */ + { + DRM_MODE("1280x720", 148500, 1280, 1720, + 1760, 1980, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 42 - 720x576@100Hz */ + { + DRM_MODE("720x576", 54000, 720, 732, + 796, 864, 0, 576, 581, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 43 - 720x576@100Hz */ + { + DRM_MODE("720x576", 54000, 720, 732, + 796, 864, 0, 576, 581, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 44 - 720(1440)x576i@100Hz */ + { + DRM_MODE("720x576i", 27000, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 45 - 720(1440)x576i@100Hz */ + { + DRM_MODE("720x576i", 27000, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 46 - 1920x1080i@120Hz */ + { + DRM_MODE("1920x1080i", 148500, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | + DRM_MODE_FLAG_INTERLACE), + }, + /* 47 - 1280x720@120Hz */ + { + DRM_MODE("1280x720", 148500, 1280, 1390, + 1430, 1650, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 48 - 720x480@120Hz */ + { + DRM_MODE("720x480", 54000, 720, 736, + 798, 858, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 49 - 720x480@120Hz */ + { + DRM_MODE("720x480", 54000, 720, 736, + 798, 858, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 50 - 720(1440)x480i@120Hz */ + { + DRM_MODE("720x480i", 27000, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 51 - 720(1440)x480i@120Hz */ + { + DRM_MODE("720x480i", 27000, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 52 - 720x576@200Hz */ + { + DRM_MODE("720x576", 108000, 720, 732, + 796, 864, 0, 576, 581, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 53 - 720x576@200Hz */ + { + DRM_MODE("720x576", 108000, 720, 732, + 796, 864, 0, 576, 581, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 54 - 720(1440)x576i@200Hz */ + { + DRM_MODE("720x576i", 54000, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 55 - 720(1440)x576i@200Hz */ + { + DRM_MODE("720x576i", 54000, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 56 - 720x480@240Hz */ + { + DRM_MODE("720x480", 108000, 720, 736, + 798, 858, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 57 - 720x480@240Hz */ + { + DRM_MODE("720x480", 108000, 720, 736, + 798, 858, 0, 480, 489, 495, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + }, + /* 58 - 720(1440)x480i@240 */ + { + DRM_MODE("720x480i", 54000, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 59 - 720(1440)x480i@240 */ + { + DRM_MODE("720x480i", 54000, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), + }, + /* 60 - 1280x720@24Hz */ + { + DRM_MODE("1280x720", 59400, 1280, 3040, + 3080, 3300, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 61 - 1280x720@25Hz */ + { + DRM_MODE("1280x720", 74250, 1280, 3700, + 3740, 3960, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 62 - 1280x720@30Hz */ + { + DRM_MODE("1280x720", 74250, 1280, 3040, + 3080, 3300, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 63 - 1920x1080@120Hz */ + { + DRM_MODE("1920x1080", 297000, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* 64 - 1920x1080@100Hz */ + { + DRM_MODE("1920x1080", 297000, 1920, 2448, + 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + }, + /* TERMINATOR */ + {}, +}; + +} // namespace kms diff --git a/kms++/src/modedb_dmt.cpp b/kms++/src/modedb_dmt.cpp new file mode 100644 index 0000000..21fe1c2 --- /dev/null +++ b/kms++/src/modedb_dmt.cpp @@ -0,0 +1,203 @@ +/* Generated from DMTr1 v13.pdf */ + +#include <kms++/modedb.h> + +#include <xf86drm.h> + +namespace kms +{ +#define DIV_ROUND(n, d) (((n) + (d) / 2) / (d)) + +// hd, hss, hse, ht, vd, vss, vse, vt + +#define DRM_MODE(nm, c, hact, hfp, hsw, hbp, vact, vfp, vsw, vbp, f) \ + { \ + .name = nm, .clock = c, \ + .hdisplay = (hact), .hsync_start = (hact) + (hfp), .hsync_end = (hact) + (hfp) + (hsw), .htotal = (hact) + (hfp) + (hsw) + (hbp), .hskew = 0, \ + .vdisplay = (vact), .vsync_start = (vact) + (vfp), .vsync_end = (vact) + (vfp) + (vsw), .vtotal = (vact) + (vfp) + (vsw) + (vbp), .vscan = 0, \ + .vrefresh = DIV_ROUND(c * 1000, ((hact) + (hfp) + (hsw) + (hbp)) * ((vact) + (vfp) + (vsw) + (vbp))) * (((f)&DRM_MODE_FLAG_INTERLACE) ? 2 : 1), \ + .flags = (f), .type = 0 \ + } + +const Videomode dmt_modes[] = { + // 0x1 - 640 x 350 @ 85Hz + DRM_MODE("640 x 350 @ 85Hz", 31500, 640, 32, 64, 96, 350, 32, 3, 60, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x2 - 640 x 400 @ 85Hz + DRM_MODE("640 x 400 @ 85Hz", 31500, 640, 32, 64, 96, 400, 1, 3, 41, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x3 - 720 x 400 @ 85Hz + DRM_MODE("720 x 400 @ 85Hz", 35500, 720, 36, 72, 108, 400, 1, 3, 42, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x4 - 640 x 480 @ 60Hz + DRM_MODE("640 x 480 @ 60Hz", 25175, 640, 8, 96, 40, 480, 2, 2, 25, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x5 - 640 x 480 @ 72Hz + DRM_MODE("640 x 480 @ 72Hz", 31500, 640, 16, 40, 120, 480, 1, 3, 20, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x6 - 640 x 480 @ 75Hz + DRM_MODE("640 x 480 @ 75Hz", 31500, 640, 16, 64, 120, 480, 1, 3, 16, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x7 - 640 x 480 @ 85Hz + DRM_MODE("640 x 480 @ 85Hz", 36000, 640, 56, 56, 80, 480, 1, 3, 25, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x8 - 800 x 600 @ 56Hz + DRM_MODE("800 x 600 @ 56Hz", 36000, 800, 24, 72, 128, 600, 1, 2, 22, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x9 - 800 x 600 @ 60Hz + DRM_MODE("800 x 600 @ 60Hz", 40000, 800, 40, 128, 88, 600, 1, 4, 23, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0xa - 800 x 600 @ 72Hz + DRM_MODE("800 x 600 @ 72Hz", 50000, 800, 56, 120, 64, 600, 37, 6, 23, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0xb - 800 x 600 @ 75Hz + DRM_MODE("800 x 600 @ 75Hz", 49500, 800, 16, 80, 160, 600, 1, 3, 21, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0xc - 800 x 600 @ 85Hz + DRM_MODE("800 x 600 @ 85Hz", 56250, 800, 32, 64, 152, 600, 1, 3, 27, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0xd - 800 x 600 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("800 x 600 @ 120Hz CVT (Reduced Blanking)", 73250, 800, 48, 32, 80, 600, 3, 4, 29, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0xe - 848 x 480 @ 60Hz + DRM_MODE("848 x 480 @ 60Hz", 33750, 848, 16, 112, 112, 480, 6, 8, 23, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0xf - 1024 x 768 @ 43Hz (Interlaced) + DRM_MODE("1024 x 768 @ 43Hz (Interlaced)", 44900, 1024, 8, 176, 56, 768, 0, 4, 20, DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x10 - 1024 x 768 @ 60Hz + DRM_MODE("1024 x 768 @ 60Hz", 65000, 1024, 24, 136, 160, 768, 3, 6, 29, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x11 - 1024 x 768 @ 70Hz + DRM_MODE("1024 x 768 @ 70Hz", 75000, 1024, 24, 136, 144, 768, 3, 6, 29, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x12 - 1024 x 768 @ 75Hz + DRM_MODE("1024 x 768 @ 75Hz", 78750, 1024, 16, 96, 176, 768, 1, 3, 28, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x13 - 1024 x 768 @ 85Hz + DRM_MODE("1024 x 768 @ 85Hz", 94500, 1024, 48, 96, 208, 768, 1, 3, 36, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x14 - 1024 x 768 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1024 x 768 @ 120Hz CVT (Reduced Blanking)", 115500, 1024, 48, 32, 80, 768, 3, 4, 38, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x15 - 1152 x 864 @ 75Hz + DRM_MODE("1152 x 864 @ 75Hz", 108000, 1152, 64, 128, 256, 864, 1, 3, 32, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x55 - 1280 x 720 @ 60Hz + DRM_MODE("1280 x 720 @ 60Hz", 74250, 1280, 110, 40, 220, 720, 5, 5, 20, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x16 - 1280 x 768 @ 60Hz CVT (Reduced Blanking) + DRM_MODE("1280 x 768 @ 60Hz CVT (Reduced Blanking)", 68250, 1280, 48, 32, 80, 768, 3, 7, 12, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x17 - 1280 x 768 @ 60Hz + DRM_MODE("1280 x 768 @ 60Hz", 79500, 1280, 64, 128, 192, 768, 3, 7, 20, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x18 - 1280 x 768 @ 75Hz + DRM_MODE("1280 x 768 @ 75Hz", 102250, 1280, 80, 128, 208, 768, 3, 7, 27, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x19 - 1280 x 768 @ 85Hz + DRM_MODE("1280 x 768 @ 85Hz", 117500, 1280, 80, 136, 216, 768, 3, 7, 31, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x1a - 1280 x 768 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1280 x 768 @ 120Hz CVT (Reduced Blanking)", 140250, 1280, 48, 32, 80, 768, 3, 7, 35, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x1b - 1280 x 800 @ 60Hz CVT (Reduced Blanking) + DRM_MODE("1280 x 800 @ 60Hz CVT (Reduced Blanking)", 71000, 1280, 48, 32, 80, 800, 3, 6, 14, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x1c - 1280 x 800 @ 60Hz + DRM_MODE("1280 x 800 @ 60Hz", 83500, 1280, 72, 128, 200, 800, 3, 6, 22, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x1d - 1280 x 800 @ 75Hz + DRM_MODE("1280 x 800 @ 75Hz", 106500, 1280, 80, 128, 208, 800, 3, 6, 29, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x1e - 1280 x 800 @ 85Hz + DRM_MODE("1280 x 800 @ 85Hz", 122500, 1280, 80, 136, 216, 800, 3, 6, 34, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x1f - 1280 x 800 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1280 x 800 @ 120Hz CVT (Reduced Blanking)", 146250, 1280, 48, 32, 80, 800, 3, 6, 38, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x20 - 1280 x 960 @ 60Hz + DRM_MODE("1280 x 960 @ 60Hz", 108000, 1280, 96, 112, 312, 960, 1, 3, 36, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x21 - 1280 x 960 @ 85Hz + DRM_MODE("1280 x 960 @ 85Hz", 148500, 1280, 64, 160, 224, 960, 1, 3, 47, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x22 - 1280 x 960 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1280 x 960 @ 120Hz CVT (Reduced Blanking)", 175500, 1280, 48, 32, 80, 960, 3, 4, 50, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x23 - 1280 x 1024 @ 60Hz + DRM_MODE("1280 x 1024 @ 60Hz", 108000, 1280, 48, 112, 248, 1024, 1, 3, 38, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x24 - 1280 x 1024 @ 75Hz + DRM_MODE("1280 x 1024 @ 75Hz", 135000, 1280, 16, 144, 248, 1024, 1, 3, 38, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x25 - 1280 x 1024 @ 85Hz + DRM_MODE("1280 x 1024 @ 85Hz", 157500, 1280, 64, 160, 224, 1024, 1, 3, 44, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x26 - 1280 x 1024 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1280 x 1024 @ 120Hz CVT (Reduced Blanking)", 187250, 1280, 48, 32, 80, 1024, 3, 7, 50, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x27 - 1360 x 768 @ 60Hz + DRM_MODE("1360 x 768 @ 60Hz", 85500, 1360, 64, 112, 256, 768, 3, 6, 18, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x28 - 1360 x 768 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1360 x 768 @ 120Hz CVT (Reduced Blanking)", 148250, 1360, 48, 32, 80, 768, 3, 5, 37, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x51 - 1366 x 768 @ 60Hz + DRM_MODE("1366 x 768 @ 60Hz", 85500, 1366, 70, 143, 213, 768, 3, 3, 24, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x56 - 1366 x 768 @ 60Hz + DRM_MODE("1366 x 768 @ 60Hz", 72000, 1366, 14, 56, 64, 768, 1, 3, 28, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x29 - 1400 x 1050 @ 60Hz CVT (Reduced Blanking) + DRM_MODE("1400 x 1050 @ 60Hz CVT (Reduced Blanking)", 101000, 1400, 48, 32, 80, 1050, 3, 4, 23, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x2a - 1400 x 1050 @ 60Hz + DRM_MODE("1400 x 1050 @ 60Hz", 121750, 1400, 88, 144, 232, 1050, 3, 4, 32, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x2b - 1400 x 1050 @ 75Hz + DRM_MODE("1400 x 1050 @ 75Hz", 156000, 1400, 104, 144, 248, 1050, 3, 4, 42, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x2c - 1400 x 1050 @ 85Hz + DRM_MODE("1400 x 1050 @ 85Hz", 179500, 1400, 104, 152, 256, 1050, 3, 4, 48, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x2d - 1400 x 1050 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1400 x 1050 @ 120Hz CVT (Reduced Blanking)", 208000, 1400, 48, 32, 80, 1050, 3, 4, 55, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x2e - 1440 x 900 @ 60Hz CVT (Reduced Blanking) + DRM_MODE("1440 x 900 @ 60Hz CVT (Reduced Blanking)", 88750, 1440, 48, 32, 80, 900, 3, 6, 17, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x2f - 1440 x 900 @ 60Hz + DRM_MODE("1440 x 900 @ 60Hz", 106500, 1440, 80, 152, 232, 900, 3, 6, 25, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x30 - 1440 x 900 @ 75Hz + DRM_MODE("1440 x 900 @ 75Hz", 136750, 1440, 96, 152, 248, 900, 3, 6, 33, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x31 - 1440 x 900 @ 85Hz + DRM_MODE("1440 x 900 @ 85Hz", 157000, 1440, 104, 152, 256, 900, 3, 6, 39, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x32 - 1440 x 900 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1440 x 900 @ 120Hz CVT (Reduced Blanking)", 182750, 1440, 48, 32, 80, 900, 3, 6, 44, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x53 - 1600 x 900 @ 60Hz + DRM_MODE("1600 x 900 @ 60Hz", 108000, 1600, 24, 80, 96, 900, 1, 3, 96, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x33 - 1600 x 1200 @ 60Hz + DRM_MODE("1600 x 1200 @ 60Hz", 162000, 1600, 64, 192, 304, 1200, 1, 3, 46, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x34 - 1600 x 1200 @ 65Hz + DRM_MODE("1600 x 1200 @ 65Hz", 175500, 1600, 64, 192, 304, 1200, 1, 3, 46, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x35 - 1600 x 1200 @ 70Hz + DRM_MODE("1600 x 1200 @ 70Hz", 189000, 1600, 64, 192, 304, 1200, 1, 3, 46, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x36 - 1600 x 1200 @ 75Hz + DRM_MODE("1600 x 1200 @ 75Hz", 202500, 1600, 64, 192, 304, 1200, 1, 3, 46, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x37 - 1600 x 1200 @ 85Hz + DRM_MODE("1600 x 1200 @ 85Hz", 229500, 1600, 64, 192, 304, 1200, 1, 3, 46, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x38 - 1600 x 1200 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1600 x 1200 @ 120Hz CVT (Reduced Blanking)", 268250, 1600, 48, 32, 80, 1200, 3, 4, 64, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x39 - 1680 x 1050 @ 60Hz CVT (Reduced Blanking) + DRM_MODE("1680 x 1050 @ 60Hz CVT (Reduced Blanking)", 119000, 1680, 48, 32, 80, 1050, 3, 6, 21, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x3a - 1680 x 1050 @ 60Hz + DRM_MODE("1680 x 1050 @ 60Hz", 146250, 1680, 104, 176, 280, 1050, 3, 6, 30, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x3b - 1680 x 1050 @ 75Hz + DRM_MODE("1680 x 1050 @ 75Hz", 187000, 1680, 120, 176, 296, 1050, 3, 6, 40, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x3c - 1680 x 1050 @ 85Hz + DRM_MODE("1680 x 1050 @ 85Hz", 214750, 1680, 128, 176, 304, 1050, 3, 6, 46, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x3d - 1680 x 1050 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1680 x 1050 @ 120Hz CVT (Reduced Blanking)", 245500, 1680, 48, 32, 80, 1050, 3, 6, 53, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x3e - 1792 x 1344 @ 60 Hz + DRM_MODE("1792 x 1344 @ 60 Hz", 204750, 1792, 128, 200, 328, 1344, 1, 3, 46, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x3f - 1792 x 1344 @ 75Hz + DRM_MODE("1792 x 1344 @ 75Hz", 261000, 1792, 96, 216, 352, 1344, 1, 3, 69, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x40 - 1792 x 1344 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1792 x 1344 @ 120Hz CVT (Reduced Blanking)", 333250, 1792, 48, 32, 80, 1344, 3, 4, 72, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x41 - 1856 x 1392 at 60Hz + DRM_MODE("1856 x 1392 at 60Hz", 218250, 1856, 96, 224, 352, 1392, 1, 3, 43, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x42 - 1856 x 1392 @ 75Hz + DRM_MODE("1856 x 1392 @ 75Hz", 288000, 1856, 128, 224, 352, 1392, 1, 3, 104, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x43 - 1856 x 1392 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1856 x 1392 @ 120Hz CVT (Reduced Blanking)", 356500, 1856, 48, 32, 80, 1392, 3, 4, 75, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x52 - 1920 x 1080 @ 60Hz + DRM_MODE("1920 x 1080 @ 60Hz", 148500, 1920, 88, 44, 148, 1080, 4, 5, 36, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x44 - 1920 x 1200 @ 60Hz CVT (Reduced Blanking) + DRM_MODE("1920 x 1200 @ 60Hz CVT (Reduced Blanking)", 154000, 1920, 48, 32, 80, 1200, 3, 6, 26, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x45 - 1920 x 1200 @ 60Hz + DRM_MODE("1920 x 1200 @ 60Hz", 193250, 1920, 136, 200, 336, 1200, 3, 6, 36, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x46 - 1920 x 1200 @ 75Hz + DRM_MODE("1920 x 1200 @ 75Hz", 245250, 1920, 136, 208, 344, 1200, 3, 6, 46, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x47 - 1920 x 1200 @ 85Hz + DRM_MODE("1920 x 1200 @ 85Hz", 281250, 1920, 144, 208, 352, 1200, 3, 6, 53, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x48 - 1920 x 1200 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1920 x 1200 @ 120Hz CVT (Reduced Blanking)", 317000, 1920, 48, 32, 80, 1200, 3, 6, 62, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x49 - 1920 x 1440 @ 60Hz + DRM_MODE("1920 x 1440 @ 60Hz", 234000, 1920, 128, 208, 344, 1440, 1, 3, 56, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x4a - 1920 x 1440 @ 75Hz + DRM_MODE("1920 x 1440 @ 75Hz", 297000, 1920, 144, 224, 352, 1440, 1, 3, 56, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x4b - 1920 x 1440 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("1920 x 1440 @ 120Hz CVT (Reduced Blanking)", 380500, 1920, 48, 32, 80, 1440, 3, 4, 78, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x54 - 2048 x 1152 @ 60Hz + DRM_MODE("2048 x 1152 @ 60Hz", 162000, 2048, 26, 80, 96, 1152, 1, 3, 44, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x4c - 2560 x 1600 @ 60Hz CVT (Reduced Blanking) + DRM_MODE("2560 x 1600 @ 60Hz CVT (Reduced Blanking)", 268500, 2560, 48, 32, 80, 1600, 3, 6, 37, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x4d - 2560 x 1600 @ 60Hz + DRM_MODE("2560 x 1600 @ 60Hz", 348500, 2560, 192, 280, 472, 1600, 3, 6, 49, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x4e - 2560 x 1600 @ 75Hz + DRM_MODE("2560 x 1600 @ 75Hz", 443250, 2560, 208, 280, 488, 1600, 3, 6, 63, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x4f - 2560 x 1600 @ 85Hz + DRM_MODE("2560 x 1600 @ 85Hz", 505250, 2560, 208, 280, 488, 1600, 3, 6, 73, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), + // 0x50 - 2560 x 1600 @ 120Hz CVT (Reduced Blanking) + DRM_MODE("2560 x 1600 @ 120Hz CVT (Reduced Blanking)", 552750, 2560, 48, 32, 80, 1600, 3, 6, 85, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x57 - 4096 x 2160 @ 60Hz CVT (Reduced Blanking v2) + DRM_MODE("4096 x 2160 @ 60Hz CVT (Reduced Blanking v2)", 556744, 4096, 8, 32, 40, 2160, 48, 8, 6, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + // 0x58 - 4096 x 2160 @ 59.94 Hz CVT (Reduced Blanking v2) + DRM_MODE("4096 x 2160 @ 59.94 Hz CVT (Reduced Blanking v2)", 556188, 4096, 8, 32, 40, 2160, 48, 8, 6, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), + /* TERMINATOR */ + {}, +}; + +} // namespace kms diff --git a/kms++/src/omap/omapcard.cpp b/kms++/src/omap/omapcard.cpp new file mode 100644 index 0000000..500bb72 --- /dev/null +++ b/kms++/src/omap/omapcard.cpp @@ -0,0 +1,23 @@ + +#include <kms++/omap/omapcard.h> + +extern "C" { +#include <omap_drmif.h> +} + +using namespace std; + +namespace kms +{ +OmapCard::OmapCard(const string& device) + : Card(device) +{ + m_omap_dev = omap_device_new(fd()); +} + +OmapCard::~OmapCard() +{ + omap_device_del(m_omap_dev); +} + +} // namespace kms diff --git a/kms++/src/omap/omapframebuffer.cpp b/kms++/src/omap/omapframebuffer.cpp new file mode 100644 index 0000000..4a94202 --- /dev/null +++ b/kms++/src/omap/omapframebuffer.cpp @@ -0,0 +1,193 @@ + +#include <cstring> +#include <stdexcept> +#include <sys/mman.h> +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <fcntl.h> +#include <unistd.h> +#include <drm_fourcc.h> +#include <drm.h> +#include <drm_mode.h> + +#include <kms++/kms++.h> +#include <kms++/omap/omapkms++.h> + +extern "C" { +#include <omap_drmif.h> +} + +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) +#define round_up(x, y) ((((x)-1) | __round_mask(x, y)) + 1) +#define PAGE_SIZE 4096 + +using namespace std; + +namespace kms +{ +OmapFramebuffer::OmapFramebuffer(OmapCard& card, uint32_t width, uint32_t height, const string& fourcc, Flags flags) + : OmapFramebuffer(card, width, height, fourcc_str_to_pixel_format(fourcc), flags) +{ +} + +OmapFramebuffer::OmapFramebuffer(OmapCard& card, uint32_t width, uint32_t height, PixelFormat format, Flags flags) + : Framebuffer(card, width, height), m_omap_card(card), m_format(format) +{ + Create(width, height, format, flags); +} + +OmapFramebuffer::~OmapFramebuffer() +{ + Destroy(); +} + +void OmapFramebuffer::Create(uint32_t width, uint32_t height, PixelFormat format, Flags buffer_flags) +{ + const PixelFormatInfo& format_info = get_pixel_format_info(format); + + m_num_planes = format_info.num_planes; + + for (int i = 0; i < format_info.num_planes; ++i) { + const PixelFormatPlaneInfo& pi = format_info.planes[i]; + FramebufferPlane& plane = m_planes[i]; + + uint32_t flags = OMAP_BO_SCANOUT | OMAP_BO_WC; + +#if defined(OMAP_BO_MEM_CONTIG) + if (buffer_flags & Flags::MemContig) + flags |= OMAP_BO_MEM_CONTIG; + if (buffer_flags & Flags::MemTiler) + flags |= OMAP_BO_MEM_TILER; + if (buffer_flags & Flags::MemPin) + flags |= OMAP_BO_MEM_PIN; +#endif + + struct omap_bo* bo; + + uint32_t stride; + + if (!(buffer_flags & Flags::Tiled)) { + stride = format_info.stride(width, i); + + uint32_t size = format_info.planesize(stride, height, i); + + bo = omap_bo_new(m_omap_card.dev(), size, flags); + if (!bo) + throw invalid_argument(string("omap_bo_new failed: ") + strerror(errno)); + } else { + unsigned bitspertiler; + + switch (format) { + case PixelFormat::NV12: + bitspertiler = i == 0 ? 8 : 16; + break; + case PixelFormat::YUYV: + case PixelFormat::UYVY: + bitspertiler = 32; + break; + case PixelFormat::ARGB8888: + case PixelFormat::XRGB8888: + bitspertiler = 32; + break; + case PixelFormat::RGB565: + bitspertiler = 16; + break; + default: + throw invalid_argument("unimplemented format"); + } + + switch (bitspertiler) { + case 8: + flags |= OMAP_BO_TILED_8; + break; + case 16: + flags |= OMAP_BO_TILED_16; + break; + case 32: + flags |= OMAP_BO_TILED_32; + break; + default: + throw invalid_argument("bad bitspertiler"); + } + + uint32_t width_tiler = format_info.stride(width, i) / pi.bytes_per_block; + + bo = omap_bo_new_tiled(m_omap_card.dev(), width_tiler, height, flags); + if (!bo) + throw invalid_argument(string("omap_bo_new_tiled failed: ") + strerror(errno)); + + stride = format_info.stride(width, i, PAGE_SIZE); + } + + plane.omap_bo = bo; + plane.handle = omap_bo_handle(bo); + plane.stride = stride; + plane.size = omap_bo_size(bo); + plane.offset = 0; + plane.map = 0; + plane.prime_fd = -1; + } + + /* create framebuffer object */ + 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] = { m_planes[0].offset, m_planes[1].offset }; + uint32_t id; + int r = drmModeAddFB2(card().fd(), width, height, pixel_format_to_fourcc(format), + bo_handles, pitches, offsets, &id, 0); + if (r) + throw invalid_argument(string("drmModeAddFB2 failed: ") + strerror(errno)); + + set_id(id); +} + +void OmapFramebuffer::Destroy() +{ + /* delete framebuffer */ + drmModeRmFB(card().fd(), id()); + + for (uint i = 0; i < m_num_planes; ++i) { + FramebufferPlane& plane = m_planes[i]; + + /* unmap buffer */ + if (plane.map) + munmap(plane.map, plane.size); + + omap_bo_del(plane.omap_bo); + + if (plane.prime_fd >= 0) + ::close(plane.prime_fd); + } +} + +uint8_t* OmapFramebuffer::map(unsigned plane) +{ + FramebufferPlane& p = m_planes[plane]; + + if (p.map) + return p.map; + + p.map = static_cast<uint8_t*>(omap_bo_map(p.omap_bo)); + if (p.map == MAP_FAILED) + throw invalid_argument(string("mmap failed: ") + strerror(errno)); + + return p.map; +} + +int OmapFramebuffer::prime_fd(unsigned int plane) +{ + FramebufferPlane& p = m_planes[plane]; + + if (p.prime_fd >= 0) + return p.prime_fd; + + int fd = omap_bo_dmabuf(p.omap_bo); + if (fd < 0) + throw std::runtime_error("omap_bo_dmabuf failed\n"); + + p.prime_fd = fd; + + return fd; +} + +} // namespace kms diff --git a/kms++/src/pixelformats.cpp b/kms++/src/pixelformats.cpp new file mode 100644 index 0000000..126a614 --- /dev/null +++ b/kms++/src/pixelformats.cpp @@ -0,0 +1,1186 @@ +#include <map> +#include <stdexcept> +#include <cassert> + +#include <kms++/pixelformats.h> + +using namespace std; + +namespace kms +{ +static map<PixelFormat, PixelFormatInfo> format_info_array = { + { + PixelFormat::R8, { + PixelFormatInfo { + "R8", + "R8 ", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 1, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGB332, { + PixelFormatInfo { + "RGB332", + "RGB8", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 1, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGB565, { + PixelFormatInfo { + "RGB565", + "RG16", + "RGBP", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::BGR565, { + PixelFormatInfo { + "BGR565", + "BG16", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XRGB1555, { + PixelFormatInfo { + "XRGB1555", + "XR15", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XBGR1555, { + PixelFormatInfo { + "XBGR1555", + "XB15", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGBX4444, { + PixelFormatInfo { + "RGBX4444", + "RX12", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XRGB4444, { + PixelFormatInfo { + "XRGB4444", + "XR12", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XBGR4444, { + PixelFormatInfo { + "XBGR4444", + "XB12", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ARGB1555, { + PixelFormatInfo { + "ARGB1555", + "AR15", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ABGR1555, { + PixelFormatInfo { + "ABGR1555", + "AB15", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGBA4444, { + PixelFormatInfo { + "RGBA4444", + "RA12", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ARGB4444, { + PixelFormatInfo { + "ARGB4444", + "AR12", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ABGR4444, { + PixelFormatInfo { + "ABGR4444", + "AB12", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGB888, { + PixelFormatInfo { + "RGB888", + "RG24", + "BGR3", + PixelColorType::RGB, + { 1, 1 }, + { { 3, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::BGR888, { + PixelFormatInfo { + "BGR888", + "BG24", + "RGB3", + PixelColorType::RGB, + { 1, 1 }, + { { 3, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XRGB8888, { + PixelFormatInfo { + "XRGB8888", + "XR24", + "XR24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XBGR8888, { + PixelFormatInfo { + "XBGR8888", + "XB24", + "XB24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGBX8888, { + PixelFormatInfo { + "RGBX8888", + "RX24", + "RX24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::BGRX8888, { + PixelFormatInfo { + "BGRX8888", + "BX24", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XBGR2101010, { + PixelFormatInfo { + "XBGR2101010", + "XB30", + "RX30", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XRGB2101010, { + PixelFormatInfo { + "XRGB2101010", + "XR30", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGBX1010102, { + PixelFormatInfo { + "RGBX1010102", + "RX30", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::BGRX1010102, { + PixelFormatInfo { + "BGRX1010102", + "BX30", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ARGB8888, { + PixelFormatInfo { + "ARGB8888", + "AR24", + "AR24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ABGR8888, { + PixelFormatInfo { + "ABGR8888", + "AB24", + "AB24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGBA8888, { + PixelFormatInfo { + "RGBA8888", + "RA24", + "RA24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::BGRA8888, { + PixelFormatInfo { + "BGRA8888", + "BA24", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ABGR2101010, { + PixelFormatInfo { + "ABGR2101010", + "AB30", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ARGB2101010, { + PixelFormatInfo { + "ARGB2101010", + "AR30", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGBA1010102, { + PixelFormatInfo { + "RGBA1010102", + "RA30", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::BGRA1010102, { + PixelFormatInfo { + "BGRA1010102", + "BA30", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::YUYV, { + PixelFormatInfo { + "YUYV", + "YUYV", + "YUYV", + PixelColorType::YUV, + { 2, 1 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::UYVY, { + PixelFormatInfo { + "UYVY", + "UYVY", + "UYVY", + PixelColorType::YUV, + { 2, 1 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::YVYU, { + PixelFormatInfo { + "YVYU", + "YVYU", + "YVYU", + PixelColorType::YUV, + { 2, 1 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::VYUY, { + PixelFormatInfo { + "VYUY", + "VYUY", + "VYUY", + PixelColorType::YUV, + { 2, 1 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::VUY888, { + PixelFormatInfo { + "VUY888", + "VU24", + "YUV3", + PixelColorType::YUV, + { 1, 1 }, + { { 3, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XVUY8888, { + PixelFormatInfo { + "XVUY8888", + "XVUY", + "YUVX", + PixelColorType::YUV, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::Y210, { + PixelFormatInfo { + "Y210", + "Y210", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 8, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::Y212, { + PixelFormatInfo { + "Y212", + "Y212", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 8, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::Y216, { + PixelFormatInfo { + "Y216", + "Y216", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 8, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::NV12, { + PixelFormatInfo { + "NV12", + "NV12", + "NM12", + PixelColorType::YUV, + { 2, 2 }, + { { 1, 1, 1, 1 }, { 2, 1, 2, 2 } }, + } + } + }, + { + PixelFormat::NV21, { + PixelFormatInfo { + "NV21", + "NV21", + "NM21", + PixelColorType::YUV, + { 2, 2 }, + { { 1, 1, 1, 1 }, { 2, 1, 2, 2 } }, + } + } + }, + { + PixelFormat::NV16, { + PixelFormatInfo { + "NV16", + "NV16", + "NM16", + PixelColorType::YUV, + { 2, 1 }, + { { 1, 1, 1, 1 }, { 2, 1, 2, 1 } }, + } + } + }, + { + PixelFormat::NV61, { + PixelFormatInfo { + "NV61", + "NV61", + "NM61", + PixelColorType::YUV, + { 2, 1 }, + { { 1, 1, 1, 1 }, { 2, 1, 2, 1 } }, + } + } + }, + { + PixelFormat::P030, { + PixelFormatInfo { + "P030", + "P030", + "", + PixelColorType::YUV, + { 6, 2 }, + { { 4, 3, 1, 1 }, { 8, 3, 2, 2 } }, + } + } + }, + { + PixelFormat::P230, { + PixelFormatInfo { + "P230", + "P230", + "", + PixelColorType::YUV, + { 6, 2 }, + { { 4, 3, 1, 1 }, { 8, 3, 2, 1 } }, + } + } + }, + { + PixelFormat::XVUY2101010, { + PixelFormatInfo { + "XVUY2101010", + "XY30", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::YUV420, { + PixelFormatInfo { + "YUV420", + "YU12", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 1, 1, 1, 1 }, { 1, 1, 2, 2 }, { 1, 1, 2, 2 } }, + } + } + }, + { + PixelFormat::YVU420, { + PixelFormatInfo { + "YVU420", + "YV12", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 1, 1, 1, 1 }, { 1, 1, 2, 2 }, { 1, 1, 2, 2 } }, + } + } + }, + { + PixelFormat::YUV422, { + PixelFormatInfo { + "YUV422", + "YU16", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 1, 1, 1, 1 }, { 1, 1, 2, 1 }, { 1, 1, 2, 1 } }, + } + } + }, + { + PixelFormat::YVU422, { + PixelFormatInfo { + "YVU422", + "YV16", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 1, 1, 1, 1 }, { 1, 1, 2, 1 }, { 1, 1, 2, 1 } }, + } + } + }, + { + PixelFormat::YUV444, { + PixelFormatInfo { + "YUV444", + "YU24", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::YVU444, { + PixelFormatInfo { + "YVU444", + "YV24", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::T430, { + PixelFormatInfo { + "T430", + "T430", + "", + PixelColorType::YUV, + { 1, 1 }, + { { 4, 1, 1, 1 }, { 4, 1, 1, 1 }, { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::Y8, { + PixelFormatInfo { + "Y8", + "GREY", + "GREY", + PixelColorType::YUV, + { 1, 1 }, + { { 1, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::Y10, { + PixelFormatInfo { + "Y10", + "", + "Y10 ", + PixelColorType::YUV, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::Y10P, { + PixelFormatInfo { + "Y10P", + "", + "Y10P", + PixelColorType::YUV, + { 4, 1 }, + { { 5, 4, 1, 1 } }, + } + } + }, + { + PixelFormat::Y12, { + PixelFormatInfo { + "Y12", + "", + "Y12 ", + PixelColorType::YUV, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::Y12P, { + PixelFormatInfo { + "Y12P", + "", + "Y12P", + PixelColorType::YUV, + { 2, 1 }, + { { 3, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::XYYY2101010, { + PixelFormatInfo { + "XYYY2101010", + "YPA4", + "", + PixelColorType::YUV, + { 3, 1 }, + { { 4, 3, 1, 1 } }, + } + } + }, + { + PixelFormat::SBGGR8, { + PixelFormatInfo { + "SBGGR8", + "", + "BA81", + PixelColorType::RAW, + { 2, 2 }, + { { 1, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::SGBRG8, { + PixelFormatInfo { + "SGBRG8", + "", + "GBRG", + PixelColorType::RAW, + { 2, 2 }, + { { 1, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::SGRBG8, { + PixelFormatInfo { + "SGRBG8", + "", + "GRBG", + PixelColorType::RAW, + { 2, 2 }, + { { 1, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::SRGGB8, { + PixelFormatInfo { + "SRGGB8", + "", + "RGGB", + PixelColorType::RAW, + { 2, 2 }, + { { 1, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::SBGGR10, { + PixelFormatInfo { + "SBGGR10", + "", + "BG10", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SGBRG10, { + PixelFormatInfo { + "SGBRG10", + "", + "GB10", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SGRBG10, { + PixelFormatInfo { + "SGRBG10", + "", + "BA10", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SRGGB10, { + PixelFormatInfo { + "SRGGB10", + "", + "RG10", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SBGGR10P, { + PixelFormatInfo { + "SBGGR10P", + "", + "pBAA", + PixelColorType::RAW, + { 4, 2 }, + { { 5, 4, 1, 1 } }, + } + } + }, + { + PixelFormat::SGBRG10P, { + PixelFormatInfo { + "SGBRG10P", + "", + "pGAA", + PixelColorType::RAW, + { 4, 2 }, + { { 5, 4, 1, 1 } }, + } + } + }, + { + PixelFormat::SGRBG10P, { + PixelFormatInfo { + "SGRBG10P", + "", + "pgAA", + PixelColorType::RAW, + { 4, 2 }, + { { 5, 4, 1, 1 } }, + } + } + }, + { + PixelFormat::SRGGB10P, { + PixelFormatInfo { + "SRGGB10P", + "", + "pRAA", + PixelColorType::RAW, + { 4, 2 }, + { { 5, 4, 1, 1 } }, + } + } + }, + { + PixelFormat::SBGGR12, { + PixelFormatInfo { + "SBGGR12", + "", + "BG12", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SGBRG12, { + PixelFormatInfo { + "SGBRG12", + "", + "GB12", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SGRBG12, { + PixelFormatInfo { + "SGRBG12", + "", + "BA12", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SRGGB12, { + PixelFormatInfo { + "SRGGB12", + "", + "RG12", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SBGGR12P, { + PixelFormatInfo { + "SBGGR12P", + "", + "pBCC", + PixelColorType::RAW, + { 2, 2 }, + { { 3, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SGBRG12P, { + PixelFormatInfo { + "SGBRG12P", + "", + "pGCC", + PixelColorType::RAW, + { 2, 2 }, + { { 3, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SGRBG12P, { + PixelFormatInfo { + "SGRBG12P", + "", + "pgCC", + PixelColorType::RAW, + { 2, 2 }, + { { 3, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SRGGB12P, { + PixelFormatInfo { + "SRGGB12P", + "", + "pRCC", + PixelColorType::RAW, + { 2, 2 }, + { { 3, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SBGGR16, { + PixelFormatInfo { + "SBGGR16", + "", + "BYR2", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SGBRG16, { + PixelFormatInfo { + "SGBRG16", + "", + "GB16", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SGRBG16, { + PixelFormatInfo { + "SGRBG16", + "", + "GR16", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::SRGGB16, { + PixelFormatInfo { + "SRGGB16", + "", + "RG16", + PixelColorType::RAW, + { 2, 2 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::MJPEG, { + PixelFormatInfo { + "MJPEG", + "MJPG", + "MJPG", + PixelColorType::Undefined, + { 1, 1 }, + { { 1, 1, 1, 1 } }, + } + } + }, +}; + +const struct PixelFormatInfo& get_pixel_format_info(PixelFormat format) +{ + if (!format_info_array.count(format)) + throw invalid_argument("get_pixel_format_info: Unsupported pixelformat"); + + return format_info_array.at(format); +} + +PixelFormat fourcc_to_pixel_format(uint32_t fourcc) +{ + for (const auto& [fmt, pfi] : format_info_array) { + if (pfi.drm_fourcc == fourcc) + return fmt; + } + + throw invalid_argument("FourCC not supported"); +} + +uint32_t pixel_format_to_fourcc(PixelFormat f) +{ + return format_info_array.at(f).drm_fourcc; +} + +PixelFormat fourcc_str_to_pixel_format(const std::string& fourcc) +{ + return fourcc_to_pixel_format(str_to_fourcc(fourcc)); +} + +std::string pixel_format_to_fourcc_str(PixelFormat f) +{ + return fourcc_to_str(format_info_array.at(f).drm_fourcc); +} + +PixelFormat find_pixel_format_by_name(const std::string& name) +{ + for (const auto& [fmt, pfi] : format_info_array) { + if (pfi.name == name) + return fmt; + } + + throw invalid_argument("Unsupported pixelformat"); +} + +static constexpr uint32_t _div_round_up(uint32_t a, uint32_t b) +{ + return (a + b - 1) / b; +} + +static constexpr uint32_t _align_up(uint32_t a, uint32_t b) +{ + return _div_round_up(a, b) * b; +} + +std::tuple<uint32_t, uint32_t> PixelFormatInfo::align_pixels(uint32_t width, + uint32_t height) const +{ + return { _align_up(width, std::get<0>(pixel_align)), + _align_up(height, std::get<1>(pixel_align)) }; +} + +uint32_t PixelFormatInfo::stride(uint32_t width, uint32_t plane, uint32_t align) const +{ + if (plane >= num_planes) + throw std::runtime_error("Invalid plane number"); + + assert(width % std::get<0>(pixel_align) == 0); + + const auto& pi = planes[plane]; + + assert(width % pi.pixels_per_block == 0); + uint32_t stride = width / pi.pixels_per_block * pi.bytes_per_block; + + assert(stride % pi.hsub == 0); + stride = stride / pi.hsub; + + stride = _align_up(stride, align); + + return stride; +} + +uint32_t PixelFormatInfo::planesize(uint32_t stride, uint32_t height, + uint32_t plane) const +{ + assert(height % std::get<1>(pixel_align) == 0); + + const auto& pi = planes[plane]; + + assert(height % pi.vsub == 0); + + return stride * (height / pi.vsub); +} + +uint32_t PixelFormatInfo::framesize(uint32_t width, uint32_t height, uint32_t align) const +{ + uint32_t size = 0; + + for (uint32_t i = 0; i < num_planes; ++i) { + uint32_t s = stride(width, i, align); + size += planesize(s, height, i); + } + + return size; +} + +std::tuple<uint32_t, uint32_t, uint32_t> PixelFormatInfo::dumb_size(uint32_t width, + uint32_t height, + uint32_t plane, + uint32_t align) const +{ + /* + Helper function mainly for DRM dumb framebuffer + Returns (width, height, bitspp) tuple which results in a suitable plane + size. + + DRM_IOCTL_MODE_CREATE_DUMB takes a 'bpp' (bits-per-pixel) argument, + which is then used with the width and height to allocate the buffer. + This doesn't work for pixel formats where the average bits-per-pixel + is not an integer (e.g. P030) + + So, we instead use the bytes_per_block (in bits) as + the 'bpp' argument, and adjust the width accordingly. + */ + + assert(height % std::get<1>(pixel_align) == 0); + + const auto& pi = planes[plane]; + + assert(height % pi.vsub == 0); + + uint32_t stride = PixelFormatInfo::stride(width, plane, align); + + assert(stride % pi.bytes_per_block == 0); + + width = stride / pi.bytes_per_block; + height = height / pi.vsub; + uint32_t bitspp = pi.bytes_per_block * 8; + + return { width, height, bitspp }; +} + +} // namespace kms diff --git a/kms++/src/plane.cpp b/kms++/src/plane.cpp new file mode 100644 index 0000000..3e17024 --- /dev/null +++ b/kms++/src/plane.cpp @@ -0,0 +1,154 @@ +#include <cstdio> +#include <iostream> +#include <unistd.h> +#include <fcntl.h> +#include <cassert> +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <algorithm> + +#include <kms++/kms++.h> + +using namespace std; + +namespace kms +{ +struct PlanePriv { + drmModePlanePtr drm_plane; +}; + +Plane::Plane(Card& card, uint32_t id, uint32_t idx) + : DrmPropObject(card, id, DRM_MODE_OBJECT_PLANE, idx) +{ + 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; +} + +bool Plane::supports_crtc(Crtc* crtc) const +{ + return m_priv->drm_plane->possible_crtcs & (1 << crtc->idx()); +} + +bool Plane::supports_format(PixelFormat fmt) const +{ + auto p = m_priv->drm_plane; + + uint32_t fcc = pixel_format_to_fourcc(fmt); + + for (unsigned i = 0; i < p->count_formats; ++i) + if (fcc == p->formats[i]) + return true; + + return false; +} + +PlaneType Plane::plane_type() const +{ + if (card().has_universal_planes()) { + switch (get_prop_value("type")) { + case DRM_PLANE_TYPE_OVERLAY: + return PlaneType::Overlay; + case DRM_PLANE_TYPE_PRIMARY: + return PlaneType::Primary; + case DRM_PLANE_TYPE_CURSOR: + return PlaneType::Cursor; + default: + throw invalid_argument("Bad plane type"); + } + } else { + return PlaneType::Overlay; + } +} + +vector<Crtc*> Plane::get_possible_crtcs() const +{ + unsigned idx = 0; + vector<Crtc*> v; + auto crtcs = card().get_crtcs(); + + for (uint32_t crtc_mask = m_priv->drm_plane->possible_crtcs; + crtc_mask; + idx++, crtc_mask >>= 1) { + if ((crtc_mask & 1) == 0) + continue; + + auto iter = find_if(crtcs.begin(), crtcs.end(), [idx](Crtc* crtc) { return crtc->idx() == idx; }); + + if (iter == crtcs.end()) + continue; + + v.push_back(*iter); + } + + return v; +} + +vector<uint32_t> Plane::get_fourccs() const +{ + auto p = m_priv->drm_plane; + vector<uint32_t> r; + + for (unsigned i = 0; i < p->count_formats; ++i) + r.push_back(p->formats[i]); + + return r; +} + +vector<PixelFormat> Plane::get_formats() const +{ + auto p = m_priv->drm_plane; + vector<PixelFormat> r; + + for (unsigned i = 0; i < p->count_formats; ++i) + try { + r.push_back(fourcc_to_pixel_format(p->formats[i])); + } catch (const std::invalid_argument&) { + // skip formats that are not supported + } + + return r; +} + +uint32_t Plane::crtc_id() const +{ + return m_priv->drm_plane->crtc_id; +} + +uint32_t Plane::fb_id() const +{ + return m_priv->drm_plane->fb_id; +} + +uint32_t Plane::crtc_x() const +{ + return m_priv->drm_plane->crtc_x; +} + +uint32_t Plane::crtc_y() const +{ + return m_priv->drm_plane->crtc_y; +} + +uint32_t Plane::x() const +{ + return m_priv->drm_plane->x; +} + +uint32_t Plane::y() const +{ + return m_priv->drm_plane->y; +} + +uint32_t Plane::gamma_size() const +{ + return m_priv->drm_plane->gamma_size; +} + +} // namespace kms diff --git a/kms++/src/property.cpp b/kms++/src/property.cpp new file mode 100644 index 0000000..0484f0e --- /dev/null +++ b/kms++/src/property.cpp @@ -0,0 +1,85 @@ +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <kms++/kms++.h> + +using namespace std; + +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); + m_name = m_priv->drm_prop->name; + + PropertyType t; + drmModePropertyPtr p = m_priv->drm_prop; + if (drm_property_type_is(p, DRM_MODE_PROP_BITMASK)) + t = PropertyType::Bitmask; + else if (drm_property_type_is(p, DRM_MODE_PROP_BLOB)) + t = PropertyType::Blob; + else if (drm_property_type_is(p, DRM_MODE_PROP_ENUM)) + t = PropertyType::Enum; + else if (drm_property_type_is(p, DRM_MODE_PROP_OBJECT)) + t = PropertyType::Object; + else if (drm_property_type_is(p, DRM_MODE_PROP_RANGE)) + t = PropertyType::Range; + else if (drm_property_type_is(p, DRM_MODE_PROP_SIGNED_RANGE)) + t = PropertyType::SignedRange; + else + throw invalid_argument("Invalid property type"); + + m_type = t; +} + +Property::~Property() +{ + drmModeFreeProperty(m_priv->drm_prop); + delete m_priv; +} + +const string& Property::name() const +{ + return m_name; +} + +bool Property::is_immutable() const +{ + return m_priv->drm_prop->flags & DRM_MODE_PROP_IMMUTABLE; +} + +bool Property::is_pending() const +{ + return m_priv->drm_prop->flags & DRM_MODE_PROP_PENDING; +} + +vector<uint64_t> Property::get_values() const +{ + drmModePropertyPtr p = m_priv->drm_prop; + return vector<uint64_t>(p->values, p->values + p->count_values); +} + +map<uint64_t, string> Property::get_enums() const +{ + drmModePropertyPtr p = m_priv->drm_prop; + + map<uint64_t, string> map; + + for (int i = 0; i < p->count_enums; ++i) + map[p->enums[i].value] = string(p->enums[i].name); + + return map; +} + +vector<uint32_t> Property::get_blob_ids() const +{ + drmModePropertyPtr p = m_priv->drm_prop; + return vector<uint32_t>(p->blob_ids, p->blob_ids + p->count_blobs); +} +} // namespace kms diff --git a/kms++/src/videomode.cpp b/kms++/src/videomode.cpp new file mode 100644 index 0000000..b7b1b28 --- /dev/null +++ b/kms++/src/videomode.cpp @@ -0,0 +1,272 @@ +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <cmath> +#include <sstream> +#include <kms++/format.h> + +#include <kms++/kms++.h> +#include "helpers.h" + +using namespace std; + +namespace kms +{ +bool Videomode::valid() const +{ + return !!clock; +} + +unique_ptr<Blob> Videomode::to_blob(Card& card) const +{ + drmModeModeInfo drm_mode = video_mode_to_drm_mode(*this); + + return unique_ptr<Blob>(new Blob(card, &drm_mode, sizeof(drm_mode))); +} + +float Videomode::calculated_vrefresh() const +{ + // XXX interlace should only halve visible vertical lines, not blanking + float refresh = (clock * 1000.0) / (htotal * vtotal) * (interlace() ? 2 : 1); + return roundf(refresh * 100.0) / 100.0; +} + +bool Videomode::interlace() const +{ + return flags & DRM_MODE_FLAG_INTERLACE; +} + +SyncPolarity Videomode::hsync() const +{ + if (flags & DRM_MODE_FLAG_PHSYNC) + return SyncPolarity::Positive; + if (flags & DRM_MODE_FLAG_NHSYNC) + return SyncPolarity::Negative; + return SyncPolarity::Undefined; +} + +SyncPolarity Videomode::vsync() const +{ + if (flags & DRM_MODE_FLAG_PVSYNC) + return SyncPolarity::Positive; + if (flags & DRM_MODE_FLAG_NVSYNC) + return SyncPolarity::Negative; + return SyncPolarity::Undefined; +} + +void Videomode::set_interlace(bool ilace) +{ + if (ilace) + flags |= DRM_MODE_FLAG_INTERLACE; + else + flags &= ~DRM_MODE_FLAG_INTERLACE; +} + +void Videomode::set_hsync(SyncPolarity pol) +{ + flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC); + + switch (pol) { + case SyncPolarity::Positive: + flags |= DRM_MODE_FLAG_PHSYNC; + break; + case SyncPolarity::Negative: + flags |= DRM_MODE_FLAG_NHSYNC; + break; + default: + break; + } +} + +void Videomode::set_vsync(SyncPolarity pol) +{ + flags &= ~(DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC); + + switch (pol) { + case SyncPolarity::Positive: + flags |= DRM_MODE_FLAG_PVSYNC; + break; + case SyncPolarity::Negative: + flags |= DRM_MODE_FLAG_NVSYNC; + break; + default: + break; + } +} + +string Videomode::to_string_short() const +{ + return fmt::format("{}x{}{}@{:.2f}", hdisplay, vdisplay, interlace() ? "i" : "", calculated_vrefresh()); +} + +static char sync_to_char(SyncPolarity pol) +{ + switch (pol) { + case SyncPolarity::Positive: + return '+'; + case SyncPolarity::Negative: + return '-'; + default: + return '?'; + } +} + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +template<typename T> +std::string join(const T& values, const std::string& delim) +{ + std::ostringstream ss; + for (const auto& v : values) { + if (&v != &values[0]) + ss << delim; + ss << v; + } + return ss.str(); +} + +static const map<int, string> mode_type_map = { + // the deprecated ones don't care about a short name + { DRM_MODE_TYPE_BUILTIN, "builtin" }, // deprecated + { DRM_MODE_TYPE_CLOCK_C, "clock_c" }, // deprecated + { DRM_MODE_TYPE_CRTC_C, "crtc_c" }, // deprecated + { DRM_MODE_TYPE_PREFERRED, "P" }, + { DRM_MODE_TYPE_DEFAULT, "default" }, // deprecated + { DRM_MODE_TYPE_USERDEF, "U" }, + { DRM_MODE_TYPE_DRIVER, "D" }, +}; + +static const map<int, string> mode_flag_map = { + // the first 5 flags are displayed elsewhere + { DRM_MODE_FLAG_PHSYNC, "" }, + { DRM_MODE_FLAG_NHSYNC, "" }, + { DRM_MODE_FLAG_PVSYNC, "" }, + { DRM_MODE_FLAG_NVSYNC, "" }, + { DRM_MODE_FLAG_INTERLACE, "" }, + { DRM_MODE_FLAG_DBLSCAN, "dblscan" }, + { DRM_MODE_FLAG_CSYNC, "csync" }, + { DRM_MODE_FLAG_PCSYNC, "pcsync" }, + { DRM_MODE_FLAG_NCSYNC, "ncsync" }, + { DRM_MODE_FLAG_HSKEW, "hskew" }, + { DRM_MODE_FLAG_BCAST, "bcast" }, // deprecated + { DRM_MODE_FLAG_PIXMUX, "pixmux" }, // deprecated + { DRM_MODE_FLAG_DBLCLK, "2x" }, + { DRM_MODE_FLAG_CLKDIV2, "clkdiv2" }, +}; + +static const map<int, string> mode_3d_map = { + { DRM_MODE_FLAG_3D_NONE, "" }, + { DRM_MODE_FLAG_3D_FRAME_PACKING, "3dfp" }, + { DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE, "3dfa" }, + { DRM_MODE_FLAG_3D_LINE_ALTERNATIVE, "3dla" }, + { DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL, "3dsbs" }, + { DRM_MODE_FLAG_3D_L_DEPTH, "3dldepth" }, + { DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH, "3dgfx" }, + { DRM_MODE_FLAG_3D_TOP_AND_BOTTOM, "3dtab" }, + { DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF, "3dsbs" }, +}; + +static const map<int, string> mode_aspect_map = { + { DRM_MODE_FLAG_PIC_AR_NONE, "" }, + { DRM_MODE_FLAG_PIC_AR_4_3, "4:3" }, + { DRM_MODE_FLAG_PIC_AR_16_9, "16:9" }, + { DRM_MODE_FLAG_PIC_AR_64_27, "64:27" }, + { DRM_MODE_FLAG_PIC_AR_256_135, "256:135" }, +}; + +static string mode_type_str(uint32_t val) +{ + vector<string> s; + for (const auto& [k, v] : mode_type_map) { + if (val & k) { + if (!v.empty()) + s.push_back(v); + val &= ~k; + } + } + // any unknown bits + if (val != 0) + s.push_back(fmt::format("{:#x}", val)); + return join(s, "|"); +} + +static string mode_flag_str(uint32_t val) +{ + vector<string> s; + for (const auto& [k, v] : mode_flag_map) { + if (val & k) { + if (!v.empty()) + s.push_back(v); + val &= ~k; + } + } + auto it = mode_3d_map.find(val & DRM_MODE_FLAG_3D_MASK); + if (it != mode_3d_map.end()) { + if (!it->second.empty()) + s.push_back(it->second); + val &= ~DRM_MODE_FLAG_3D_MASK; + } + it = mode_aspect_map.find(val & DRM_MODE_FLAG_PIC_AR_MASK); + if (it != mode_aspect_map.end()) { + if (!it->second.empty()) + s.push_back(it->second); + val &= ~DRM_MODE_FLAG_PIC_AR_MASK; + } + // any unknown bits + if (val != 0) + s.push_back(fmt::format("{:#x}", val)); + return join(s, "|"); +} + +string Videomode::to_string_long() const +{ + string h = fmt::format("{}/{}/{}/{}/{}", hdisplay, hfp(), hsw(), hbp(), sync_to_char(hsync())); + string v = fmt::format("{}/{}/{}/{}/{}", vdisplay, vfp(), vsw(), vbp(), sync_to_char(vsync())); + + string str = fmt::format("{} {:.3f} {} {} {} ({:.2f}) {} {}", + to_string_short(), + clock / 1000.0, + h, v, + vrefresh, calculated_vrefresh(), + mode_type_str(type), + mode_flag_str(flags)); + + return str; +} + +string Videomode::to_string_long_padded() const +{ + string h = fmt::format("{}/{}/{}/{}/{}", hdisplay, hfp(), hsw(), hbp(), sync_to_char(hsync())); + string v = fmt::format("{}/{}/{}/{}/{}", vdisplay, vfp(), vsw(), vbp(), sync_to_char(vsync())); + + string str = fmt::format("{:<16} {:7.3f} {:<18} {:<18} {:2} ({:.2f}) {:<5} {}", + to_string_short(), + clock / 1000.0, + h, v, + vrefresh, calculated_vrefresh(), + mode_type_str(type), + mode_flag_str(flags)); + + return str; +} + +Videomode videomode_from_timings(uint32_t clock_khz, + uint16_t hact, uint16_t hfp, uint16_t hsw, uint16_t hbp, + uint16_t vact, uint16_t vfp, uint16_t vsw, uint16_t vbp) +{ + Videomode m{}; + m.clock = clock_khz; + + m.hdisplay = hact; + m.hsync_start = hact + hfp; + m.hsync_end = hact + hfp + hsw; + m.htotal = hact + hfp + hsw + hbp; + + m.vdisplay = vact; + m.vsync_start = vact + vfp; + m.vsync_end = vact + vfp + vsw; + m.vtotal = vact + vfp + vsw + vbp; + + return m; +} + +} // namespace kms |
