Zach Reizner | d847a7e | 2016-04-28 16:48:32 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2015 The Chromium OS Authors. All rights reserved. |
| 3 | * Use of this source code is governed by a BSD-style license that can be |
| 4 | * found in the LICENSE file. |
| 5 | */ |
| 6 | |
| 7 | #include <ctype.h> |
| 8 | |
| 9 | #include "bs_drm.h" |
| 10 | |
| 11 | #define MAX_PLANES 4 |
| 12 | #define MAX_COMPONENTS 4 |
| 13 | |
| 14 | static drmModeModeInfoPtr find_best_mode(int mode_count, drmModeModeInfoPtr modes) |
| 15 | { |
| 16 | if (mode_count <= 0 || modes == NULL) |
| 17 | return NULL; |
| 18 | |
| 19 | for (int m = 0; m < mode_count; m++) |
| 20 | if (modes[m].type & DRM_MODE_TYPE_PREFERRED) |
| 21 | return &modes[m]; |
| 22 | |
| 23 | return &modes[0]; |
| 24 | } |
| 25 | |
| 26 | static bool is_format_supported(uint32_t format_count, uint32_t *formats, uint32_t format) |
| 27 | { |
| 28 | for (uint32_t i = 0; i < format_count; i++) |
| 29 | if (formats[i] == format) |
| 30 | return true; |
| 31 | return false; |
| 32 | } |
| 33 | |
| 34 | static uint32_t find_overlay_plane(int fd, uint32_t crtc_id, uint32_t format) |
| 35 | { |
| 36 | drmModeRes *res = drmModeGetResources(fd); |
| 37 | if (res == NULL) { |
| 38 | bs_debug_error("failed to get drm resources"); |
| 39 | return 0; |
| 40 | } |
| 41 | |
| 42 | uint32_t crtc_mask = 0; |
| 43 | for (int crtc_index = 0; crtc_index < res->count_crtcs; crtc_index++) { |
| 44 | if (res->crtcs[crtc_index] == crtc_id) { |
| 45 | crtc_mask = (1 << crtc_index); |
| 46 | break; |
| 47 | } |
| 48 | } |
| 49 | if (crtc_mask == 0) { |
| 50 | bs_debug_error("invalid crtc id %u", crtc_id); |
| 51 | drmModeFreeResources(res); |
| 52 | return 0; |
| 53 | } |
| 54 | |
| 55 | drmModePlaneRes *plane_res = drmModeGetPlaneResources(fd); |
| 56 | if (plane_res == NULL) { |
| 57 | bs_debug_error("failed to get plane resources"); |
| 58 | drmModeFreeResources(res); |
| 59 | return 0; |
| 60 | } |
| 61 | |
| 62 | uint32_t plane_id = 0; |
| 63 | bool use_plane = false; |
| 64 | for (uint32_t plane_index = 0; !use_plane && plane_index < plane_res->count_planes; |
| 65 | plane_index++) { |
| 66 | drmModePlane *plane = drmModeGetPlane(fd, plane_res->planes[plane_index]); |
| 67 | if (plane == NULL) { |
| 68 | bs_debug_error("failed to get plane id %u", plane_res->planes[plane_index]); |
| 69 | continue; |
| 70 | } |
| 71 | |
| 72 | if ((plane->possible_crtcs & crtc_mask) == 0 || |
| 73 | !is_format_supported(plane->count_formats, plane->formats, format)) { |
| 74 | drmModeFreePlane(plane); |
| 75 | continue; |
| 76 | } |
| 77 | |
| 78 | drmModeObjectPropertiesPtr props = |
| 79 | drmModeObjectGetProperties(fd, plane->plane_id, DRM_MODE_OBJECT_PLANE); |
| 80 | for (uint32_t prop_index = 0; prop_index < props->count_props; ++prop_index) { |
| 81 | drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[prop_index]); |
| 82 | if (strcmp(prop->name, "type")) { |
| 83 | drmModeFreeProperty(prop); |
| 84 | continue; |
| 85 | } |
| 86 | |
| 87 | uint64_t desired_value = 0; |
| 88 | bool has_value = false; |
| 89 | for (int enum_index = 0; enum_index < prop->count_enums; enum_index++) { |
| 90 | struct drm_mode_property_enum *penum = &prop->enums[enum_index]; |
| 91 | if (!strcmp(penum->name, "Overlay")) { |
| 92 | desired_value = penum->value; |
| 93 | has_value = true; |
| 94 | break; |
| 95 | } |
| 96 | } |
| 97 | drmModeFreeProperty(prop); |
| 98 | |
| 99 | if (has_value && desired_value == props->prop_values[prop_index]) |
| 100 | use_plane = true; |
| 101 | |
| 102 | break; |
| 103 | } |
| 104 | drmModeFreeObjectProperties(props); |
| 105 | |
| 106 | if (use_plane) |
| 107 | plane_id = plane->plane_id; |
| 108 | |
| 109 | drmModeFreePlane(plane); |
| 110 | } |
| 111 | |
| 112 | drmModeFreePlaneResources(plane_res); |
| 113 | drmModeFreeResources(res); |
| 114 | |
| 115 | return plane_id; |
| 116 | } |
| 117 | |
| 118 | struct draw_format_component { |
| 119 | float rgb_coeffs[3]; |
| 120 | float value_offset; |
| 121 | uint32_t horizontal_subsample_rate; |
| 122 | uint32_t vertical_subsample_rate; |
| 123 | uint32_t pixel_skip; |
| 124 | uint32_t plane_index; |
| 125 | uint32_t plane_offset; |
| 126 | }; |
| 127 | |
| 128 | struct draw_format { |
| 129 | uint32_t pixel_format; |
| 130 | const char *name; |
| 131 | size_t component_count; |
| 132 | struct draw_format_component components[MAX_COMPONENTS]; |
| 133 | }; |
| 134 | |
| 135 | #define PIXEL_FORMAT_AND_NAME(x) GBM_FORMAT_##x, #x |
| 136 | |
| 137 | static const struct draw_format g_draw_formats[] = { |
| 138 | { |
| 139 | PIXEL_FORMAT_AND_NAME(NV12), |
| 140 | 3, |
| 141 | { |
| 142 | { { 0.2567890625f, 0.50412890625f, 0.09790625f }, 16.0f, 1, 1, 1, 0, 0 }, |
| 143 | { { -0.14822265625f, -0.2909921875f, 0.43921484375f }, 128.0f, 2, 2, 1, 1, 0 }, |
| 144 | { { 0.43921484375f, -0.3677890625f, -0.07142578125f }, 128.0f, 2, 2, 1, 1, 1 }, |
| 145 | }, |
| 146 | }, |
| 147 | { |
| 148 | PIXEL_FORMAT_AND_NAME(XRGB8888), |
| 149 | 3, |
| 150 | { |
| 151 | { { 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 4, 0, 0 }, |
| 152 | { { 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 1 }, |
| 153 | { { 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 2 }, |
| 154 | }, |
| 155 | }, |
| 156 | { |
| 157 | PIXEL_FORMAT_AND_NAME(ARGB8888), |
| 158 | 4, |
| 159 | { |
| 160 | { { 0.0f, 0.0f, 1.0f }, 0.0f, 1, 1, 4, 0, 0 }, |
| 161 | { { 0.0f, 1.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 1 }, |
| 162 | { { 1.0f, 0.0f, 0.0f }, 0.0f, 1, 1, 4, 0, 2 }, |
| 163 | { { 0.0f, 0.0f, 0.0f }, 255.0f, 1, 1, 4, 0, 3 }, |
| 164 | }, |
| 165 | }, |
| 166 | }; |
| 167 | |
| 168 | static uint8_t clampbyte(float f) |
| 169 | { |
| 170 | if (f >= 255.0f) |
| 171 | return 255; |
| 172 | if (f <= 0.0f) |
| 173 | return 0; |
| 174 | return (uint8_t)f; |
| 175 | } |
| 176 | |
| 177 | static uint8_t convert_color(const struct draw_format_component *comp, uint8_t r, uint8_t g, |
| 178 | uint8_t b) |
| 179 | { |
| 180 | return clampbyte(comp->value_offset + r * comp->rgb_coeffs[0] + g * comp->rgb_coeffs[1] + |
| 181 | b * comp->rgb_coeffs[2]); |
| 182 | } |
| 183 | |
| 184 | static const struct draw_format *get_draw_format(uint32_t pixel_format) |
| 185 | { |
| 186 | for (size_t format_index = 0; |
| 187 | format_index < sizeof(g_draw_formats) / sizeof(g_draw_formats[0]); format_index++) { |
| 188 | const struct draw_format *format = &g_draw_formats[format_index]; |
| 189 | if (format->pixel_format == pixel_format) |
| 190 | return format; |
| 191 | } |
| 192 | |
| 193 | return NULL; |
| 194 | } |
| 195 | |
| 196 | struct draw_plane { |
| 197 | uint32_t row_stride; |
| 198 | uint8_t *ptr; |
| 199 | }; |
| 200 | |
| 201 | static void unmmap_planes(struct gbm_bo *bo, size_t num_planes, struct draw_plane *planes) |
| 202 | { |
| 203 | for (uint32_t plane_index = 0; plane_index < num_planes; plane_index++) |
| 204 | bs_dma_buf_unmmap_plane(bo, plane_index, planes[plane_index].ptr); |
| 205 | } |
| 206 | |
| 207 | static size_t mmap_planes(struct gbm_bo *bo, struct draw_plane planes[MAX_PLANES]) |
| 208 | { |
| 209 | size_t num_planes = gbm_bo_get_num_planes(bo); |
| 210 | if (num_planes > MAX_PLANES) { |
| 211 | bs_debug_error("buffer object has unexpected number of planes %zu", num_planes); |
| 212 | return 0; |
| 213 | } |
| 214 | |
| 215 | for (size_t plane_index = 0; plane_index < num_planes; plane_index++) { |
| 216 | struct draw_plane *plane = &planes[plane_index]; |
| 217 | plane->row_stride = gbm_bo_get_plane_stride(bo, plane_index); |
| 218 | plane->ptr = bs_dma_buf_mmap_plane(bo, plane_index); |
| 219 | if (!plane->ptr) { |
| 220 | bs_debug_error("failed to mmap plane %zu of buffer object", plane_index); |
| 221 | unmmap_planes(bo, plane_index, planes); |
| 222 | return 0; |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | return true; |
| 227 | } |
| 228 | |
| 229 | static bool draw_pattern(struct gbm_bo *bo, const struct draw_format *format) |
| 230 | { |
| 231 | const uint32_t width = gbm_bo_get_width(bo); |
| 232 | const uint32_t height = gbm_bo_get_height(bo); |
| 233 | const uint32_t striph = height / 4; |
| 234 | |
| 235 | struct draw_plane planes[MAX_PLANES]; |
| 236 | size_t num_planes = mmap_planes(bo, planes); |
| 237 | if (num_planes == 0) { |
| 238 | bs_debug_error("failed to prepare to draw pattern to buffer object"); |
| 239 | return false; |
| 240 | } |
| 241 | |
| 242 | for (uint32_t s = 0; s < 4; s++) { |
| 243 | uint8_t r = 0, g = 0, b = 0; |
| 244 | switch (s) { |
| 245 | case 0: |
| 246 | r = g = b = 1; |
| 247 | break; |
| 248 | case 1: |
| 249 | r = 1; |
| 250 | break; |
| 251 | case 2: |
| 252 | g = 1; |
| 253 | break; |
| 254 | case 3: |
| 255 | b = 1; |
| 256 | break; |
| 257 | default: |
| 258 | r = g = b = 0; |
| 259 | break; |
| 260 | } |
| 261 | for (uint32_t y = s * striph; y < (s + 1) * striph; y++) { |
| 262 | uint8_t *rows[MAX_COMPONENTS] = { 0 }; |
| 263 | for (size_t comp_index = 0; comp_index < format->component_count; |
| 264 | comp_index++) { |
| 265 | const struct draw_format_component *comp = |
| 266 | &format->components[comp_index]; |
| 267 | struct draw_plane *plane = &planes[comp->plane_index]; |
| 268 | rows[comp_index] = |
| 269 | plane->ptr + comp->plane_offset + |
| 270 | plane->row_stride * (y / comp->vertical_subsample_rate); |
| 271 | } |
| 272 | for (uint32_t x = 0; x < width; x++) { |
| 273 | const float i = (float)x / (float)width * 256.0f; |
| 274 | for (size_t comp_index = 0; comp_index < format->component_count; |
| 275 | comp_index++) { |
| 276 | const struct draw_format_component *comp = |
| 277 | &format->components[comp_index]; |
| 278 | if ((y % comp->vertical_subsample_rate) == 0 && |
| 279 | (x % comp->horizontal_subsample_rate) == 0) |
| 280 | *(rows[comp_index] + x * comp->pixel_skip) = |
| 281 | convert_color(comp, r * i, g * i, b * i); |
| 282 | } |
| 283 | } |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | unmmap_planes(bo, num_planes, planes); |
| 288 | return true; |
| 289 | } |
| 290 | |
| 291 | int main(int argc, char **argv) |
| 292 | { |
| 293 | assert(sizeof(g_draw_formats) / sizeof(g_draw_formats[0]) > 0); |
| 294 | const struct draw_format *plane_format = &g_draw_formats[0]; |
| 295 | if (argc == 2) { |
| 296 | char *format_str = argv[1]; |
| 297 | if (strlen(format_str) == 4) { |
| 298 | plane_format = get_draw_format(*(uint32_t *)format_str); |
| 299 | } |
| 300 | else { |
| 301 | plane_format = 0; |
| 302 | for (size_t format_index = 0; |
| 303 | format_index < sizeof(g_draw_formats) / sizeof(g_draw_formats[0]); |
| 304 | format_index++) { |
| 305 | const struct draw_format *format = &g_draw_formats[format_index]; |
| 306 | if (!strcmp(format_str, format->name)) { |
| 307 | plane_format = format; |
| 308 | break; |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | if (plane_format == NULL) { |
| 314 | printf("plane format %s is not recognized\n", format_str); |
| 315 | return false; |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | printf("Using plane format %s (%4.4s)\n", plane_format->name, |
| 320 | (char *)&plane_format->pixel_format); |
| 321 | |
| 322 | drmModeConnector *connector; |
| 323 | struct bs_drm_pipe pipe = { 0 }; |
| 324 | struct bs_drm_pipe_plumber *plumber = bs_drm_pipe_plumber_new(); |
| 325 | bs_drm_pipe_plumber_connector_ranks(plumber, bs_drm_connectors_internal_rank); |
| 326 | bs_drm_pipe_plumber_connector_ptr(plumber, &connector); |
| 327 | if (!bs_drm_pipe_plumber_make(plumber, &pipe)) { |
| 328 | bs_debug_error("failed to make pipe"); |
| 329 | return 1; |
| 330 | } |
| 331 | bs_drm_pipe_plumber_destroy(&plumber); |
| 332 | |
| 333 | drmModeModeInfo *mode_ptr = find_best_mode(connector->count_modes, connector->modes); |
| 334 | if (!mode_ptr) { |
| 335 | bs_debug_error("failed to find preferred mode"); |
| 336 | return 1; |
| 337 | } |
| 338 | drmModeModeInfo mode = *mode_ptr; |
| 339 | drmModeFreeConnector(connector); |
| 340 | printf("Using mode %s\n", mode.name); |
| 341 | |
| 342 | uint32_t plane_id = find_overlay_plane(pipe.fd, pipe.crtc_id, plane_format->pixel_format); |
| 343 | if (plane_id == 0) { |
| 344 | bs_debug_error("failed to find overlay plane"); |
| 345 | return 1; |
| 346 | } |
| 347 | |
| 348 | printf("Using CRTC:%u ENCODER:%u CONNECTOR:%u PLANE:%u\n", pipe.crtc_id, pipe.encoder_id, |
| 349 | pipe.connector_id, plane_id); |
| 350 | |
| 351 | struct gbm_device *gbm = gbm_create_device(pipe.fd); |
| 352 | if (!gbm) { |
| 353 | bs_debug_error("failed to create gbm"); |
| 354 | return 1; |
| 355 | } |
| 356 | |
| 357 | struct gbm_bo *bg_bo = gbm_bo_create(gbm, mode.hdisplay, mode.vdisplay, GBM_FORMAT_XRGB8888, |
| 358 | GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR); |
| 359 | if (!bg_bo) { |
| 360 | bs_debug_error("failed to create background buffer ojbect"); |
| 361 | return 1; |
| 362 | } |
| 363 | |
| 364 | void *bo_ptr = bs_dma_buf_mmap_plane(bg_bo, 0); |
| 365 | if (bo_ptr == NULL) { |
| 366 | bs_debug_error("failed to mmap background buffer object"); |
| 367 | return 1; |
| 368 | } |
| 369 | memset(bo_ptr, 0xff, gbm_bo_get_height(bg_bo) * gbm_bo_get_stride(bg_bo)); |
| 370 | bs_dma_buf_unmmap_plane(bg_bo, 0, bo_ptr); |
| 371 | |
| 372 | printf("Creating buffer %ux%u\n", mode.hdisplay, mode.vdisplay); |
| 373 | struct gbm_bo *bo = |
| 374 | gbm_bo_create(gbm, mode.hdisplay, mode.vdisplay, plane_format->pixel_format, |
| 375 | GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR); |
| 376 | if (!bo) { |
| 377 | bs_debug_error("failed to create buffer object"); |
| 378 | return 1; |
| 379 | } |
| 380 | |
| 381 | uint32_t crtc_fb_id = bs_drm_fb_create_gbm(bg_bo); |
| 382 | if (!crtc_fb_id) { |
| 383 | bs_debug_error("failed to create frame buffer for buffer object"); |
| 384 | return 1; |
| 385 | } |
| 386 | |
| 387 | uint32_t plane_fb_id = bs_drm_fb_create_gbm(bo); |
| 388 | if (!plane_fb_id) { |
| 389 | bs_debug_error("failed to create plane frame buffer for buffer object"); |
| 390 | return 1; |
| 391 | } |
| 392 | |
| 393 | if (!draw_pattern(bo, plane_format)) { |
| 394 | bs_debug_error("failed to draw pattern to buffer object"); |
| 395 | return 1; |
| 396 | } |
| 397 | |
| 398 | int ret = |
| 399 | drmModeSetCrtc(pipe.fd, pipe.crtc_id, crtc_fb_id, 0, 0, &pipe.connector_id, 1, &mode); |
| 400 | if (ret < 0) { |
| 401 | bs_debug_error("Could not set mode on CRTC %d %s", pipe.crtc_id, strerror(errno)); |
| 402 | return 1; |
| 403 | } |
| 404 | |
| 405 | ret = drmModeSetPlane(pipe.fd, plane_id, pipe.crtc_id, plane_fb_id, 0, 0, 0, mode.hdisplay, |
| 406 | mode.vdisplay, 0, 0, mode.hdisplay << 16, mode.vdisplay << 16); |
| 407 | |
| 408 | if (ret) { |
| 409 | bs_debug_error("failed to set plane %d", ret); |
| 410 | return 1; |
| 411 | } |
| 412 | |
| 413 | sleep(5); |
| 414 | |
| 415 | ret = drmModeSetCrtc(pipe.fd, pipe.crtc_id, 0, 0, 0, NULL, 0, NULL); |
| 416 | if (ret < 0) { |
| 417 | bs_debug_error("Could not disable CRTC %d %s", pipe.crtc_id, strerror(errno)); |
| 418 | return 1; |
| 419 | } |
| 420 | |
| 421 | drmModeRmFB(pipe.fd, plane_fb_id); |
| 422 | drmModeRmFB(pipe.fd, crtc_fb_id); |
| 423 | gbm_bo_destroy(bo); |
| 424 | gbm_bo_destroy(bg_bo); |
| 425 | gbm_device_destroy(gbm); |
| 426 | close(pipe.fd); |
| 427 | |
| 428 | return 0; |
| 429 | } |