===== Test Application with DRM or Simple FB ===== Souce Code는 다음과 같다. #include #include #include /* for memset */ #include /* for open/close .. */ #include /* for O_RDWR */ #include /* for ioctl */ #include /* for mmap */ #include /* for fb_var_screeninfo, FBIOGET_VSCREENINFO */ #include "drm.h" /* /home/stephanos/VI/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/include/drm -> check Makefile */ #include "drm_mode.h" #define DEFAULT_FRAME_BUFFER_FILE "/dev/fb0" #define DEV_LCD_DRM "/dev/dri/card0" #define MAXIMUM_NUMBER_OF_CONNECTORS 10 #define MAXIMUM_NUMBER_OF_CONNECTOR_RESOURCE_BUFFER 20 typedef struct { void * pBuffer; uint32_t ByteSizeOfBuffer; uint32_t ScreenWidth; uint32_t ScreenHeight; } FrameBuffer_t; typedef struct { uint64_t pFB_ID[MAXIMUM_NUMBER_OF_CONNECTORS]; uint64_t pCRTC_ID[MAXIMUM_NUMBER_OF_CONNECTORS]; uint64_t pCONNECTOR_ID[MAXIMUM_NUMBER_OF_CONNECTORS]; uint64_t pENCODER_ID[MAXIMUM_NUMBER_OF_CONNECTORS]; } DRMResourceBuffer_t; typedef struct { struct drm_mode_modeinfo pMode[MAXIMUM_NUMBER_OF_CONNECTOR_RESOURCE_BUFFER]; uint64_t pProps[MAXIMUM_NUMBER_OF_CONNECTOR_RESOURCE_BUFFER]; uint64_t PPropValue[MAXIMUM_NUMBER_OF_CONNECTOR_RESOURCE_BUFFER]; uint64_t pEncoder[MAXIMUM_NUMBER_OF_CONNECTOR_RESOURCE_BUFFER]; } DRMConnectorResourceBuffer_t; typedef struct { struct drm_mode_create_dumb Create; struct drm_mode_map_dumb Map; struct drm_mode_fb_cmd Cmd; } DRMdumbBuffer_t; typedef struct { int Device; void * pFrameBuffer; struct fb_var_screeninfo var; struct fb_fix_screeninfo fix; } FrameBufferContext_t; void drm_draw_pixel( FrameBuffer_t *pFrameBuffer, uint32_t X, uint32_t Y, uint32_t Color/*(b << 16) | (g << 8) | (r << 0)*/) { if ( pFrameBuffer != (FrameBuffer_t *)0 ) { if ((pFrameBuffer->ScreenWidth > X) && (pFrameBuffer->ScreenHeight > Y)) { *(((uint32_t*)pFrameBuffer->pBuffer ) + Y * pFrameBuffer->ScreenWidth + X) = Color; } } } void drm_draw_test_pattern(FrameBuffer_t *pFrameBuffer) { uint32_t x; uint32_t y; uint32_t h; uint32_t w; h = pFrameBuffer->ScreenHeight; w = pFrameBuffer->ScreenWidth; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (x < 20 && y < 20) { drm_draw_pixel(pFrameBuffer, x, y, 0xffffff); } else if (x < 20 && (y > 20 && y < h - 20)) { drm_draw_pixel(pFrameBuffer, x, y, 0xff); } else if (y < 20 && (x > 20 && x < w - 20)) { drm_draw_pixel(pFrameBuffer, x, y, 0xff00); } else if (x > w - 20 && (y > 20 && y < h - 20)) { drm_draw_pixel(pFrameBuffer, x, y, 0xff0000); } else if (y > h - 20 && (x > 20 && x < w - 20)) { drm_draw_pixel(pFrameBuffer, x, y, 0xffff00); } else if (x == 20 || x == w - 20 || y == 20 || y == h - 20) { drm_draw_pixel(pFrameBuffer, x, y, 0xffffff); } else if (x == y || w - x == h - y) { drm_draw_pixel(pFrameBuffer, x, y, 0xff00ff); } else if (w - x == y || x == h - y) { drm_draw_pixel(pFrameBuffer, x, y, 0x00ffff); } else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) { int t = x * 3 / w; unsigned r = 0, g = 0, b = 0; unsigned c; if (t == 0) { b = (y % 256); } else if (t == 1) { g = (y % 256); } else if (t == 2) { r = (y % 256); } c = (b << 16) | (g << 8) | (r << 0); drm_draw_pixel(pFrameBuffer, x, y, c); } else { drm_draw_pixel(pFrameBuffer, x, y, 0); } } } } void drm_fill_screen(FrameBuffer_t *pFrameBuffer, uint32_t Color/*(b << 16) | (g << 8) | (r << 0)*/) { uint32_t X; uint32_t Y; if ( pFrameBuffer != (FrameBuffer_t *)0 ) { for (Y = 0; Y < pFrameBuffer->ScreenHeight; Y++) { for (X = 0; X < pFrameBuffer->ScreenWidth; X++) { *(((uint32_t*)pFrameBuffer->pBuffer ) + Y * pFrameBuffer->ScreenWidth + X) = Color; } } } } void fb_draw_pixel( FrameBufferContext_t *pFrameBufferContext, uint32_t X, uint32_t Y, uint32_t Color) { void * pFrameBuffer; pFrameBuffer = pFrameBufferContext->pFrameBuffer; if (pFrameBufferContext->var.bits_per_pixel == 8) { unsigned char * p; pFrameBuffer += pFrameBufferContext->fix.line_length * Y; p = pFrameBuffer; p += X; *p = (unsigned char)Color; } else if (pFrameBufferContext->var.bits_per_pixel == 16) { unsigned short * p; pFrameBuffer += pFrameBufferContext->fix.line_length * Y; p = pFrameBuffer; p += X; *p = (unsigned short)(((((Color >> 16) & 0xff) * 32 / 256) << 11) | ((((Color >> 8) & 0xff) * 64 / 256) << 5) | ((((Color >> 0) & 0xff) * 32 / 256) << 0)); } else if (pFrameBufferContext->var.bits_per_pixel == 24) { unsigned char * p; p = (unsigned char *)pFrameBuffer + pFrameBufferContext->fix.line_length * Y + 3 * X; *p++ = (unsigned char)Color; *p++ = (unsigned char)(Color >> 8); *p = (unsigned char)(Color >> 16); } else { uint32_t * p; pFrameBuffer += pFrameBufferContext->fix.line_length * Y; p = pFrameBuffer; p += X; *p = Color; } } void fill_screen(FrameBufferContext_t *pFrameBufferContext) { uint32_t x; uint32_t y; uint32_t h; uint32_t w; h = pFrameBufferContext->var.yres; w = pFrameBufferContext->var.xres; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { if (x < 20 && y < 20) { fb_draw_pixel(pFrameBufferContext, x, y, 0xffffff); } else if (x < 20 && (y > 20 && y < h - 20)) { fb_draw_pixel(pFrameBufferContext, x, y, 0xff); } else if (y < 20 && (x > 20 && x < w - 20)) { fb_draw_pixel(pFrameBufferContext, x, y, 0xff00); } else if (x > w - 20 && (y > 20 && y < h - 20)) { fb_draw_pixel(pFrameBufferContext, x, y, 0xff0000); } else if (y > h - 20 && (x > 20 && x < w - 20)) { fb_draw_pixel(pFrameBufferContext, x, y, 0xffff00); } else if (x == 20 || x == w - 20 || y == 20 || y == h - 20) { fb_draw_pixel(pFrameBufferContext, x, y, 0xffffff); } else if (x == y || w - x == h - y) { fb_draw_pixel(pFrameBufferContext, x, y, 0xff00ff); } else if (w - x == y || x == h - y) { fb_draw_pixel(pFrameBufferContext, x, y, 0x00ffff); } else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) { int t = x * 3 / w; unsigned r = 0, g = 0, b = 0; unsigned c; if (pFrameBufferContext->var.bits_per_pixel == 16) { if (t == 0) { b = (y % 32) * 256 / 32; } else if (t == 1) { g = (y % 64) * 256 / 64; } else if (t == 2) { r = (y % 32) * 256 / 32; } } else { if (t == 0) { b = (y % 256); } else if (t == 1) { g = (y % 256); } else if (t == 2) { r = (y % 256); } } c = (b << 16) | (g << 8) | (r << 0); fb_draw_pixel(pFrameBufferContext, x, y, c); } else { fb_draw_pixel(pFrameBufferContext, x, y, 0); } } } } void clear_area( FrameBufferContext_t *pFrameBufferContext, uint32_t X, uint32_t Y, uint32_t W, uint32_t H) { uint32_t AddressOffsetForH; uint32_t CountForH; unsigned char * pFrameBuffer; pFrameBuffer = (unsigned char *)pFrameBufferContext->pFrameBuffer; for (CountForH = 0; CountForH < H; CountForH++) { AddressOffsetForH = (X + pFrameBufferContext->var.xoffset) * (pFrameBufferContext->var.bits_per_pixel / 8) + (Y + CountForH + pFrameBufferContext->var.yoffset) * pFrameBufferContext->fix.line_length; memset(pFrameBuffer + AddressOffsetForH, 0, W * pFrameBufferContext->var.bits_per_pixel / 8); } } int device_open_mode(FrameBufferContext_t *pFrameBufferContext, int BitPerPixel) { pFrameBufferContext->Device = open(DEFAULT_FRAME_BUFFER_FILE, O_RDWR); if(pFrameBufferContext->Device < 0) { printf("Faile to open : %s \n", DEFAULT_FRAME_BUFFER_FILE); return -1; } if(ioctl(pFrameBufferContext->Device, FBIOGET_VSCREENINFO, &pFrameBufferContext->var) < 0) { printf("fbdev ioctl(VSCREENINFO)\n"); close(pFrameBufferContext->Device); return -1; } if(ioctl(pFrameBufferContext->Device, FBIOGET_FSCREENINFO, &pFrameBufferContext->fix) < 0) { printf("fbdev ioctl(FSCREENINFO)\n"); close(pFrameBufferContext->Device); return -1; } if(pFrameBufferContext->var.bits_per_pixel != BitPerPixel) { pFrameBufferContext->var.bits_per_pixel = BitPerPixel; if(ioctl(pFrameBufferContext->Device, FBIOPUT_VSCREENINFO, &pFrameBufferContext->var) < 0) { printf("fbdev ioctl(PUT)\n"); close(pFrameBufferContext->Device); return -1; } } return 0; } int device_open(FrameBufferContext_t *pFrameBufferContext) { if(device_open_mode(pFrameBufferContext, 8) != 0) { printf("Faile to open with 8bit: %s \n", DEFAULT_FRAME_BUFFER_FILE); return -1; } close(pFrameBufferContext->Device); if(device_open_mode(pFrameBufferContext, 24) != 0) { printf("Faile to open with 24bit: %s \n", DEFAULT_FRAME_BUFFER_FILE); return -1; } printf("x-resolution : %d\n", pFrameBufferContext->var.xres); printf("y-resolution : %d\n", pFrameBufferContext->var.yres); printf("x-resolution(virtual) : %d\n", pFrameBufferContext->var.xres_virtual); printf("y-resolution(virtual) : %d\n", pFrameBufferContext->var.yres_virtual); printf("bpp : %d\n", pFrameBufferContext->var.bits_per_pixel); printf("length of frame buffer memory : %d\n", pFrameBufferContext->fix.smem_len); return 0; } static int test_simple_fb_style(void) { FrameBufferContext_t FrameBufferContext; int Error; printf("Opening frame buffer device `%s'\n", DEFAULT_FRAME_BUFFER_FILE); if(device_open(&FrameBufferContext) != 0) { printf("Fail to Device Open\n"); return 0; } FrameBufferContext.pFrameBuffer = mmap(0, FrameBufferContext.var.yres_virtual * FrameBufferContext.fix.line_length, PROT_WRITE | PROT_READ, MAP_SHARED, FrameBufferContext.Device, 0); if(FrameBufferContext.pFrameBuffer == MAP_FAILED) { printf("Frame buffer MAP fail !\n"); close(FrameBufferContext.Device); return 0; } /* Display pattern to screen */ fill_screen(&FrameBufferContext); /* End of... */ munmap( FrameBufferContext.pFrameBuffer, FrameBufferContext.var.yres_virtual * FrameBufferContext.fix.line_length); close(FrameBufferContext.Device); return 0; } static int drm_open(void) { int Device; int Flags; uint64_t HasDumbBuffer; Device = -1; if ( access(DEV_LCD_DRM, F_OK) ) { printf("no device - %s\n", DEV_LCD_DRM); return 0; } if ( (Device = open(DEV_LCD_DRM, O_RDWR)) <= 0 ) { printf("Faile to open : %s \n", DEV_LCD_DRM); return 0; } // set FD_CLOEXEC flag Flags = fcntl(Device, F_GETFD); if (Flags < 0) { printf("Faile to read Flags \n"); close(Device); return 0; } if ( fcntl(Device, F_SETFD, Flags | FD_CLOEXEC) < 0 ) { printf("Faile to set Falsgs FD_CLOEXEC \n"); close(Device); return 0; } return Device; } static int drm_get_frame_buffer(FrameBuffer_t *pFrameBuffer, int Device, uint64_t * pConnectorID) { struct drm_mode_get_connector ConnectorResource = {0}; DRMConnectorResourceBuffer_t DRMConnectorResourceBuffer = {0}; DRMdumbBuffer_t DRMdumbBuffer = {0}; struct drm_mode_get_encoder Encoder = {0}; struct drm_mode_crtc CRTC = {0}; if ((pFrameBuffer != (FrameBuffer_t *)0) && (pConnectorID != (uint64_t * )0) ) { // get connector resource counts ConnectorResource.connector_id = *pConnectorID; ioctl(Device, DRM_IOCTL_MODE_GETCONNECTOR, &ConnectorResource); // get connector resources ConnectorResource.modes_ptr = (uint64_t)DRMConnectorResourceBuffer.pMode; ConnectorResource.props_ptr = (uint64_t)DRMConnectorResourceBuffer.pProps; ConnectorResource.prop_values_ptr = (uint64_t)DRMConnectorResourceBuffer.PPropValue; ConnectorResource.encoders_ptr = (uint64_t)DRMConnectorResourceBuffer.pEncoder; ioctl(Device, DRM_IOCTL_MODE_GETCONNECTOR, &ConnectorResource); // Check if the connector is OK to use (connected to something) if ((ConnectorResource.count_encoders > 0) && (ConnectorResource.count_modes > 0) && (ConnectorResource.encoder_id != 0) && (ConnectorResource.connection != 0) ) { // Creating a dumb buffer // If we create the buffer later, we can get the size of the screen first. // This must be a valid mode, so it's probably best to do this after we find a valid crtc with modes. DRMdumbBuffer.Create.width = DRMConnectorResourceBuffer.pMode[0].hdisplay; DRMdumbBuffer.Create.height = DRMConnectorResourceBuffer.pMode[0].vdisplay; DRMdumbBuffer.Create.bpp = 32; DRMdumbBuffer.Create.flags = 0; /* must be zero */ DRMdumbBuffer.Create.pitch = 0; DRMdumbBuffer.Create.size = 0; DRMdumbBuffer.Create.handle = 0; if(ioctl(Device, DRM_IOCTL_MODE_CREATE_DUMB, &DRMdumbBuffer.Create) < 0) { printf("Fail to create dumb buffer\n"); }; DRMdumbBuffer.Cmd.width=DRMdumbBuffer.Create.width; DRMdumbBuffer.Cmd.height=DRMdumbBuffer.Create.height; DRMdumbBuffer.Cmd.bpp=DRMdumbBuffer.Create.bpp; DRMdumbBuffer.Cmd.pitch=DRMdumbBuffer.Create.pitch; DRMdumbBuffer.Cmd.depth=24; DRMdumbBuffer.Cmd.handle=DRMdumbBuffer.Create.handle; ioctl(Device,DRM_IOCTL_MODE_ADDFB,&DRMdumbBuffer.Cmd); DRMdumbBuffer.Map.handle=DRMdumbBuffer.Create.handle; ioctl(Device,DRM_IOCTL_MODE_MAP_DUMB,&DRMdumbBuffer.Map); pFrameBuffer->pBuffer = mmap(0, DRMdumbBuffer.Create.size, PROT_READ | PROT_WRITE, MAP_SHARED, Device, DRMdumbBuffer.Map.offset); pFrameBuffer->ByteSizeOfBuffer = DRMdumbBuffer.Create.size; pFrameBuffer->ScreenWidth = DRMdumbBuffer.Create.width; pFrameBuffer->ScreenHeight = DRMdumbBuffer.Create.height; // drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); // Kernel Mode Setting (KMS) printf("Connection %d : mode: %d, prop: %d, enc: %d\n", ConnectorResource.connection, ConnectorResource.count_modes, ConnectorResource.count_props, ConnectorResource.count_encoders); printf("modes: %dx%d Frame Buffer Address: 0x%X Frame Buffer Byte Size: 0x%X(%d) \n", DRMConnectorResourceBuffer.pMode[0].hdisplay, DRMConnectorResourceBuffer.pMode[0].vdisplay, pFrameBuffer->pBuffer, pFrameBuffer->ByteSizeOfBuffer, pFrameBuffer->ByteSizeOfBuffer); // get encoder Encoder.encoder_id = ConnectorResource.encoder_id; ioctl(Device, DRM_IOCTL_MODE_GETENCODER, &Encoder); CRTC.crtc_id = Encoder.crtc_id; ioctl(Device, DRM_IOCTL_MODE_GETCRTC, &CRTC); CRTC.fb_id = DRMdumbBuffer.Cmd.fb_id; CRTC.set_connectors_ptr = (uint64_t)pConnectorID; CRTC.count_connectors = 1; CRTC.mode = DRMConnectorResourceBuffer.pMode[0]; CRTC.mode_valid = 1; ioctl(Device, DRM_IOCTL_MODE_SETCRTC, &CRTC); return 0; } else { printf("Not connected\n"); } } return -1; } static int drm_free_frame_buffer(FrameBuffer_t *pFrameBuffer) { if (pFrameBuffer != (FrameBuffer_t *)0) { if (pFrameBuffer->ByteSizeOfBuffer != 0) { munmap( pFrameBuffer->pBuffer, pFrameBuffer->ByteSizeOfBuffer ); printf("Free Frame Buffer\n"); return 0; } } return -1; } static int test_drm_style(void) { int Device; uint32_t ConnectorIndex; uint32_t Color; struct drm_mode_card_res Resource = {0}; DRMResourceBuffer_t DRMResourceBuffer = {0}; Device = drm_open(); if ( Device != 0 ) { FrameBuffer_t pFrameBuffer[MAXIMUM_NUMBER_OF_CONNECTORS]; printf("DRM opened !\n"); // Kernel Mode Setting (KMS) // Become the "master" of the DRI device ioctl(Device, DRM_IOCTL_SET_MASTER, 0); printf("DRM DRM_IOCTL_SET_MASTER\n"); // Get resource counts ioctl(Device, DRM_IOCTL_MODE_GETRESOURCES, &Resource); Resource.fb_id_ptr = (uint64_t)DRMResourceBuffer.pFB_ID; Resource.crtc_id_ptr = (uint64_t)DRMResourceBuffer.pCRTC_ID; Resource.connector_id_ptr = (uint64_t)DRMResourceBuffer.pCONNECTOR_ID; Resource.encoder_id_ptr = (uint64_t)DRMResourceBuffer.pENCODER_ID; // Get resource IDs ioctl(Device, DRM_IOCTL_MODE_GETRESOURCES, &Resource); printf("Get Resource ID fb: %d, crtc: %d, conn: %d, enc: %d\n", Resource.count_fbs, Resource.count_crtcs, Resource.count_connectors, Resource.count_encoders); // Loop though all available connectors for (ConnectorIndex = 0; ConnectorIndex < Resource.count_connectors; ConnectorIndex++) { drm_get_frame_buffer(&pFrameBuffer[ConnectorIndex], Device, &DRMResourceBuffer.pCONNECTOR_ID[ConnectorIndex]); } // Stop being the "master" of the DRI device ioctl(Device, DRM_IOCTL_DROP_MASTER, 0); printf("DRM DRM_IOCTL_DROP_MASTER\n"); int j; for (j = 0; j < 100; j++) { for (ConnectorIndex = 0; ConnectorIndex < Resource.count_connectors; ConnectorIndex++) { drm_fill_screen(&pFrameBuffer[ConnectorIndex], 0x00000000); usleep(100000); Color = (rand() % 0x00ffffff) & 0x00ff00ff; drm_fill_screen(&pFrameBuffer[ConnectorIndex], Color); usleep(100000); for (uint32_t k = 0; k <255; k++) { drm_fill_screen(&pFrameBuffer[ConnectorIndex], (Color | (k << 24))); } drm_draw_test_pattern(&pFrameBuffer[ConnectorIndex]); usleep(100000); } } //DRM_IOCTL_MODE_DESTROY_DUMB for (ConnectorIndex = 0; ConnectorIndex < Resource.count_connectors; ConnectorIndex++) { drm_free_frame_buffer(&pFrameBuffer[ConnectorIndex]); } close(Device); printf("DRM closed !\n"); } } int main() { printf("Test Symple FB Style\n"); test_simple_fb_style(); printf("Test DRM Style\n"); test_drm_style(); return 0; } {{wiki:atmel_sama5d42:test_app:test_app_002.zip}} 아래와 같이 컴파일 한다. stephanos@stephanos-VirtualBox:~/VI/App$ /home/stephanos/VI/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -I$/home/stephanos/VI/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/include/drm -o test test.c {{wiki:atmel_sama5d42:test_app:test_app_000.jpg}} NFS에 위에서 만들어진 test를 복사한다. Target Board를 NFS로 부팅한다. 로그인 후 아래와 같이 실행한다. {{wiki:atmel_sama5d42:test_app:test_app_001.jpg}}\\ {{wiki:atmel_sama5d42:test_app:test_app_003.jpg}}