54#include <xf86drmMode.h>
55#include <drm_fourcc.h>
75#define MOD_NAME "[drm] "
79 using frame_uniq = std::unique_ptr<video_frame, frame_deleter>;
81 struct drm_connector_deleter{
void operator()(drmModeConnectorPtr c) { drmModeFreeConnector(c); } };
82 using Drm_connector_uniq = std::unique_ptr<drmModeConnector, drm_connector_deleter>;
84 struct drm_resources_deleter{
void operator()(drmModeResPtr r) { drmModeFreeResources(r); } };
85 using Drm_res_uniq = std::unique_ptr<drmModeRes, drm_resources_deleter>;
87 struct drm_encoder_deleter{
void operator()(drmModeEncoderPtr e) { drmModeFreeEncoder(e); } };
88 using Drm_encoder_uniq = std::unique_ptr<drmModeEncoder, drm_encoder_deleter>;
90 struct drm_crtc_deleter{
void operator()(drmModeCrtcPtr c) { drmModeFreeCrtc(c); } };
91 using Drm_crtc_uniq = std::unique_ptr<drmModeCrtc, drm_crtc_deleter>;
93 struct drm_plane_res_deleter{
void operator()(drmModePlaneResPtr r) { drmModeFreePlaneResources(r); } };
94 using Drm_plane_res_uniq = std::unique_ptr<drmModePlaneRes, drm_plane_res_deleter>;
96 struct drm_plane_deleter{
void operator()(drmModePlanePtr p) { drmModeFreePlane(p); } };
97 using Drm_plane_uniq = std::unique_ptr<drmModePlane, drm_plane_deleter>;
99 struct drm_obj_props_deleter{
void operator()(drmModeObjectPropertiesPtr p) { drmModeFreeObjectProperties(p); } };
100 using Drm_object_properties_uniq = std::unique_ptr<drmModeObjectProperties, drm_obj_props_deleter>;
102 struct drm_prop_deleter{
void operator()(drmModePropertyPtr p) { drmModeFreeProperty(p); } };
103 using Drm_property_uniq = std::unique_ptr<drmModePropertyRes, drm_prop_deleter>;
108 Fd_uniq(
int fd) : fd(fd) { }
109 Fd_uniq(
const Fd_uniq&) =
delete;
110 Fd_uniq(Fd_uniq&& o){
118 Fd_uniq& operator=(
const Fd_uniq&) =
delete;
119 Fd_uniq& operator=(Fd_uniq&& o){
124 operator bool()
const {
return fd > 0; }
126 int get() {
return fd; }
143 MemoryMapping() =
default;
148 munmap(ptr, mapped_size);
151 MemoryMapping(
const MemoryMapping&) =
delete;
152 MemoryMapping& operator=(
const MemoryMapping&) =
delete;
154 MemoryMapping(MemoryMapping&& o)
noexcept{
155 std::swap(ptr, o.ptr);
156 std::swap(mapped_size, o.mapped_size);
159 MemoryMapping& operator=(MemoryMapping&& o)
noexcept{
160 std::swap(ptr, o.ptr);
161 std::swap(mapped_size, o.mapped_size);
166 static MemoryMapping create(
void *addr,
size_t len,
int prot,
int flags,
int fildes, off_t off){
168 res.ptr = mmap(addr,
len, prot,
flags, fildes, off);
169 if(res.ptr != MAP_FAILED)
170 res.mapped_size =
len;
175 bool valid()
const {
return ptr != MAP_FAILED; }
176 void *get()
const {
return ptr; }
177 size_t size()
const {
return mapped_size; }
179 void *ptr = MAP_FAILED;
180 size_t mapped_size = 0;
183 template<
typename T,
class Deleter>
186 Uniq_wrapper() =
default;
192 Uniq_wrapper(
const Uniq_wrapper&) =
delete;
193 Uniq_wrapper& operator=(
const Uniq_wrapper&) =
delete;
195 Uniq_wrapper(Uniq_wrapper&& o){
196 std::swap(
val, o.val);
199 Uniq_wrapper& operator=(Uniq_wrapper&& o){
200 std::swap(
val, o.val);
204 T& get() {
return val; }
215 struct Fb_handle_deleter {
216 void operator()(Fb_handle h){
220 drm_mode_destroy_dumb destroy_info = {};
221 destroy_info.handle = h.handle;
222 drmIoctl(h.dri_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_info);
225 using Fb_handle_uniq = Uniq_wrapper<Fb_handle, Fb_handle_deleter>;
232 struct Fb_id_deleter {
233 void operator()(Fb_id i){
237 drmModeRmFB(i.dri_fd, i.id);
240 using Fb_id_uniq = Uniq_wrapper<Fb_id, Fb_id_deleter>;
242 class Gem_handle_manager{
249 void add_ref(uint32_t handle){
250 handle_map[handle]++;
253 void unref(uint32_t handle){
254 int refs = handle_map[handle]--;
255 assert(refs >= 0 &&
"Unref called on invalid handle");
258 drmIoctl(dri_fd, DRM_IOCTL_GEM_CLOSE, &handle);
259 handle_map.erase(handle);
263 std::map<uint32_t, int> handle_map;
266 Gem_handle_manager(
int dri_fd): dri_fd(dri_fd) { }
276 Handle(
const Handle&) =
delete;
277 Handle& operator=(
const Handle&) =
delete;
279 std::swap(handle, o.handle);
280 std::swap(ctx, o.ctx);
282 Handle& operator=(Handle&& o){
283 std::swap(handle, o.handle);
284 std::swap(ctx, o.ctx);
288 uint32_t get()
const {
292 friend Gem_handle_manager;
293 Handle(uint32_t handle, Gem_handle_manager *ctx): handle(handle), ctx(ctx){
294 ctx->add_ref(handle);
297 Gem_handle_manager *ctx =
nullptr;
300 Handle get_handle(
int fd){
302 int res = drmPrimeFDToHandle(dri_fd, fd, &handle);
307 return Handle(handle,
this);
378 case DRM_MODE_CONNECTOR_VGA: res =
"VGA";
break;
379 case DRM_MODE_CONNECTOR_HDMIA: res =
"HDMI-A";
break;
380 case DRM_MODE_CONNECTOR_HDMIB: res =
"HDMI-B";
break;
381 case DRM_MODE_CONNECTOR_DisplayPort: res =
"DP";
break;
382 case DRM_MODE_CONNECTOR_eDP: res =
"eDP";
break;
383 case DRM_MODE_CONNECTOR_DVII: res =
"DVI-I";
break;
384 case DRM_MODE_CONNECTOR_DVID: res =
"DVI-D";
break;
385 case DRM_MODE_CONNECTOR_DVIA: res =
"DVI-A";
break;
386#ifdef DRM_MODE_CONNECTOR_USB
387 case DRM_MODE_CONNECTOR_USB: res =
"USB";
break;
390 res = std::to_string(
type);
394 res += std::to_string(
id);
400 drmVersionPtr version = drmGetVersion(s->
drm.
dri_fd.get());
407 version->version_major,
408 version->version_minor,
409 version->version_patchlevel,
412 drmFreeVersion(version);
417 auto do_open = [](
const char *path) -> Fd_uniq {
418 int fd = open(path, O_RDWR);
427 Drm_res_uniq resources(drmModeGetResources(fd));
433 uint64_t dumb_support =
false;
435 res = drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &dumb_support);
436 if(res < 0 || !dumb_support){
454 const int max_index = 32;
455 for(
int i = 0; i < max_index; i++){
456 snprintf(buf,
sizeof(buf), DRM_DEV_NAME, DRM_DIR_NAME, i);
457 auto ret = do_open(buf);
465static int64_t
get_property(
int dri, drmModeObjectPropertiesPtr props, std::string_view
name){
466 for(
unsigned i = 0; i < props->count_props; i++){
467 Drm_property_uniq prop(drmModeGetProperty(dri, props->props[i]));
471 if(prop->name ==
name)
472 return props->prop_values[i];
479 Drm_plane_res_uniq plane_res(drmModeGetPlaneResources(dri));
485 Drm_plane_uniq primary_plane;
486 for(
unsigned i = 0; i < plane_res->count_planes; i++){
487 Drm_plane_uniq plane(drmModeGetPlane(dri, plane_res->planes[i]));
491 Drm_object_properties_uniq props(drmModeObjectGetProperties(dri, plane_res->planes[i], DRM_MODE_OBJECT_PLANE));
496 if(
get_property(dri, props.get(),
"type") == DRM_PLANE_TYPE_PRIMARY){
497 primary_plane = std::move(plane);
507 for(
unsigned i = 0; i < primary_plane->count_formats; i++){
515 std::vector<Drm_connector_uniq> res;
517 for(
int i = 0; i < s->
drm.
res->count_connectors; i++){
518 Drm_connector_uniq conn(drmModeGetConnector(s->
drm.
dri_fd.get(), s->
drm.
res->connectors[i]));
522 res.push_back(std::move(conn));
539 res = drmSetClientCap(dri, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
547 s->
drm.
res.reset(drmModeGetResources(dri));
553 uint64_t prime_support =
false;
554 res = drmGetCap(dri, DRM_CAP_PRIME, &prime_support);
555 if(res < 0 || !prime_support){
565 case DRM_MODE_CONNECTED:
566 return " (Connected)";
567 case DRM_MODE_DISCONNECTED:
568 return " (Not connected)";
569 case DRM_MODE_UNKNOWNCONNECTION:
579 for(
auto& connector : connectors){
583 for(
int i = 0; i < connector->count_modes; i++){
584 auto mode_info = &connector->modes[i];
585 color_printf(
"\t\t%dx%d@%d (%s)%s\n", mode_info->hdisplay, mode_info->vdisplay, mode_info->vrefresh, mode_info->name,
586 mode_info->type & DRM_MODE_TYPE_PREFERRED ?
" preferred" :
"");
594 for(
auto& connector : connectors){
604 if(connector->count_modes > 0 && connector->connection == DRM_MODE_CONNECTED){
628 if(find_preferred_mode && !(
mode->type & DRM_MODE_TYPE_PREFERRED)){
643 std::vector<int> encoder_list;
647 for(
int i = 0; i < s->
drm.
connector->count_encoders; i++){
651 for(
int encoder_id : encoder_list){
652 s->
drm.
encoder.reset(drmModeGetEncoder(dri, encoder_id));
656 std::vector<unsigned> crtc_list;
660 for(
int i = 0; i < s->
drm.
res->count_crtcs; i++){
661 if((1 << i) & s->
drm.
encoder->possible_crtcs){
662 crtc_list.push_back(s->
drm.
res->crtcs[i]);
666 for(
unsigned crtc_id : crtc_list){
667 s->
drm.
crtc.reset(drmModeGetCrtc(dri, crtc_id));
669 for(
int i = 0; i < s->
drm.
res->count_crtcs; i++){
670 if(s->
drm.
res->crtcs[i] == crtc_id){
694 res = drmSetMaster(dri);
712 drm_mode_create_dumb create_info = {};
713 create_info.width = width;
714 create_info.height = height;
715 create_info.bpp = 32;
716 res = drmIoctl(dri, DRM_IOCTL_MODE_CREATE_DUMB, &create_info);
721 handle.handle = create_info.handle;
724 buf.
pitch = create_info.pitch;
725 buf.
size = create_info.size;
727 buf.
handle = Fb_handle_uniq(handle);
731 uint32_t handles[] = {buf.
handle.get().handle, 0, 0, 0};
732 uint32_t pitches[] = {buf.
pitch, 0, 0, 0};
733 uint32_t offsets[] = {0, 0, 0, 0};
735 res = drmModeAddFB2(dri, width, height,
736 pix_fmt, handles, pitches, offsets, &fb_id.id, 0);
744 buf.
id = Fb_id_uniq(fb_id);
750 struct drm_mode_map_dumb map_info = {};
751 map_info.handle = buf.
handle.get().handle;
753 res = drmIoctl(dri, DRM_IOCTL_MODE_MAP_DUMB, &map_info);
759 buf.
map = MemoryMapping::create(0, buf.
size, PROT_READ | PROT_WRITE, MAP_SHARED, dri, map_info.offset);
760 if(!buf.
map.valid()){
789 auto dst_p =
static_cast<char *
>(dst->
map.get());
790 auto src_p =
static_cast<char *
>(src->
tiles[0].
data);
799 if(src_w < dst->width)
800 x = (dst->
width - src_w) / 2;
801 if(src_h < dst->height)
802 y = (dst->
height - src_h) / 2;
805 int width = std::min(src_w, dst->
width - x);
806 int height = std::min(src_h, dst->
height - y);
811 dst_p += dst->
pitch * y;
814 for(
int y = 0; y < height; y++){
815 memcpy(dst_p, src_p, linesize);
830 pix_fmt = DRM_FORMAT_XBGR8888;
834 pix_fmt = DRM_FORMAT_XRGB8888;
846 auto s = std::make_unique<drm_display_state>();
851 bool help_requested =
false;
852 std::string_view sv_cfg(s->cfg);
853 while(!sv_cfg.empty()){
859 help_requested =
true;
860 }
else if(
key ==
"dev"){
861 s->device_path =
val;
862 }
else if(
key ==
"connector"){
863 s->req_connector =
val;
864 }
else if(
key ==
"mode"){
886 color_printf(
TBOLD(
"\tpath")
" - Path to the DRI device. If not specified /dev/video[0..32] are tried.\n");
887 color_printf(
TBOLD(
"\tconnector")
" - The physical connector the display device is plugged into.\n");
888 color_printf(
TBOLD(
"\tmode")
" - Video mode to use. If not specified, the preferred mode is used. \n");
898 s->splashscreen =
get_splash_fb(s.get(), s->drm.mode_info->hdisplay, s->drm.mode_info->vdisplay);
910 res = drmModeSetCrtc(s->drm.dri_fd.get(), s->drm.crtc->crtc_id, s->drm.crtc->buffer_id,
911 s->drm.crtc->x , s->drm.crtc->y, &s->drm.connector->connector_id, 1, &s->drm.crtc->mode);
921 while(!s->free_frames.empty()){
922 auto frame = std::move(s->free_frames.back());
923 s->free_frames.pop_back();
926 return frame.release();
945 for(
int i = 0; i < drm_frame->fd_count; i++){
949 uint32_t handles[4] = {};
950 for(
int i = 0; i < drm_frame->planes; i++){
951 handles[i] = fb.
gem_objects[drm_frame->fd_indices[i]].get();
956 res = drmModeAddFB2WithModifiers(s->
drm.
dri_fd.get(), fb.
frame->tiles[0].width, fb.
frame->tiles[0].height, drm_frame->drm_format,
957 handles, drm_frame->pitches, drm_frame->offsets, drm_frame->modifiers, &fb_id.id, DRM_MODE_FB_MODIFIERS);
962 fb.
id = Fb_id_uniq(fb_id);
1003 s->drm_prime_fb = std::move(fb);
1015 std::vector<codec_t> out;
1021 out.push_back(
UYVY);
1023 out.push_back(
RGBA);
1025 size_t length =
sizeof(
codec_t) * out.size();
1031 memcpy(
val, out.data(), *
len);
1041 int rgb_shift[] = {0, 8, 16};
1043 std::swap(rgb_shift[0], rgb_shift[2]);
1050 if(
sizeof(rgb_shift) > *
len) {
1053 memcpy(
val, rgb_shift,
sizeof(rgb_shift));
1054 *
len =
sizeof(rgb_shift);
1074 pix_fmt =
drm_format_supported(s, DRM_FORMAT_XBGR8888) ? DRM_FORMAT_XBGR8888 : DRM_FORMAT_XRGB8888;
1077 pix_fmt = DRM_FORMAT_UYVY;
1083 if(!s->drm.mode_info || s->drm.mode_info->hdisplay >
desc.
width || s->drm.mode_info->vdisplay >
desc.
height){
1085 s->drm.mode_info =
nullptr;
1086 for(
int i = 0; i < s->drm.connector->count_modes; i++){
1087 auto mode = &s->drm.connector->modes[i];
1095 s->drm.mode_info =
mode;
1098 if(!s->drm.mode_info){
1112 s->front_buffer =
create_dumb_fb(s->drm.dri_fd.get(), s->drm.mode_info->hdisplay, s->drm.mode_info->vdisplay, pix_fmt);
1113 s->back_buffer =
create_dumb_fb(s->drm.dri_fd.get(), s->drm.mode_info->hdisplay, s->drm.mode_info->vdisplay, pix_fmt);
1121 *available_cards = NULL;
struct video_frame * frame
const char unsigned int flags
int void(** deleter)(void *))
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
int vc_get_linesize(unsigned int width, codec_t codec)
struct video_frame * vf_alloc_desc_data(struct video_desc desc)
int color_printf(const char *format,...)
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_WARNING
less severe errors
static Drm_prime_fb drm_fb_from_frame(drm_display_state *s, frame_uniq frame)
static void * display_drm_init(struct module *parent, const char *cfg, unsigned int flags)
static std::string get_connector_str(int type, uint32_t id)
static const struct video_display_info display_drm_info
static bool drm_format_supported(drm_display_state *s, uint32_t fmt)
static bool display_drm_reconfigure(void *state, struct video_desc desc)
static int64_t get_property(int dri, drmModeObjectPropertiesPtr props, std::string_view name)
static void print_connectors(drm_display_state *s)
static void display_drm_done(void *state)
static void recycle_frame(drm_display_state *s, frame_uniq &frame)
static bool probe_drm_formats(drm_display_state *s)
static Framebuffer create_dumb_fb(int dri, int width, int height, uint32_t pix_fmt)
static Fd_uniq open_dri(drm_display_state *s)
static Framebuffer get_splash_fb(drm_display_state *s, int width, int height)
static struct video_frame * display_drm_getf(void *state)
static void print_drm_driver_info(drm_display_state *s)
static std::vector< Drm_connector_uniq > get_connectors(drm_display_state *s)
static void recycle_prime_frame(drm_display_state *s, Drm_prime_fb &fb)
static bool display_drm_get_property(void *state, int property, void *val, size_t *len)
static const char * get_connector_status_str(drmModeConnection c)
static bool setup_crtc(drm_display_state *s)
static void draw_splash(drm_display_state *s)
static bool display_drm_putf(void *state, struct video_frame *f, long long flags)
static bool set_framebuffer(drm_display_state *s, uint32_t fb_id)
static bool swap_buffers(drm_display_state *s)
static void draw_frame(Framebuffer *dst, video_frame *src, bool center=true)
static void display_drm_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
static bool init_drm_state(drm_display_state *s)
static bool get_codecs(drm_display_state *s, void *val, size_t *len)
#define VIDEO_DISPLAY_ABI_VERSION
struct video_frame * get_splashscreen()
@ DISPLAY_PROPERTY_RGB_SHIFT
red,green,blue shift - int[3] (bits)
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
@ LIBRARY_CLASS_VIDEO_DISPLAY
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
std::string_view tokenize(std::string_view &str, char delim, char quot)
Tokenizer for string_view.
bool parse_num(std::string_view sv, T &res, int base=10)
Gem_handle_manager::Handle gem_objects[4]
drmModeModeInfoPtr mode_info
Drm_connector_uniq connector
std::unique_ptr< Gem_handle_manager > gem_manager
std::set< uint32_t > supported_drm_formats
std::string req_connector
Drm_prime_fb drm_prime_fb
std::vector< frame_uniq > free_frames
void operator()(video_frame *f)
char * data
Raw tile data. Pointer must be at least 4B aligned.
unsigned int width
width of every tile (!)
unsigned int height
height of every tile (!)
Struct video_frame represents a video frame and contains video description.
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
@ DRM_PRIME
DRM Prime buffer, data contains struct drm_prime_frame.
@ RGBA
RGBA 8-bit, big-endian.
This is an umbrella header for video functions.
int vc_get_size(unsigned int width, codec_t codec)
void vf_recycle(struct video_frame *buf)
This function should be called before returning a frame back to a frame pool. Currently this is used ...
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.