cbeif
Posts: 1
Joined: Wed Nov 22, 2023 3:56 am

How to transmit video data to MIPI DSI port?

Tue Nov 28, 2023 1:36 am

Hello, I would like to transmit a frame to a custom LCD device (with no touchscreen),
I do not want to use this LCD as the main display, just as an additional display.
I am using a raspberry pi 4b
I am trying to code this with the DRM/KMS libraries.

I started talking to the DRM drivers using the following C code, but after power cycling the raspberry pi, the calls to the
drmModeGetResources() started reporting that "The Operation is not supported"

Any idea what I am doing wrong in the following code:
and why would a power cycle change how the code was running????

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> // Include this for uint32_t
#include <fcntl.h>
#include <unistd.h>
#include <libdrm/drm.h>
#include <libdrm/drm_mode.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

#define WIDTH 320
#define HEIGHT 200
#define BPP 24


int main() {
    int drm_fd = open("/dev/dri/card0", O_RDWR);
    printf("drm_fc: %d\n", drm_fd);
    if (drm_fd < 0) {
        perror("Failed to open DRM device");
        exit(EXIT_FAILURE);
    }

    drmModeRes *resources = drmModeGetResources(drm_fd);
    printf("resources: %p\n", (void *)resources);
    if (!resources) {
        perror("Failed to get DRM resources");
        close(drm_fd);
        exit(EXIT_FAILURE);
    }

    drmModeConnector *connector = drmModeGetConnector(drm_fd, resources->connectors[0]);
    printf("Connector Information:\n");
    printf("ID: %u\n", connector->connector_id);
    printf("Type: %u\n", connector->connector_type);
    printf("Type ID: %u\n", connector->connector_type_id);
    printf("Connection: %u\n", connector->connection);
    printf("Count Modes: %u\n", connector->count_modes);
     // Print information about each mode
    for (int i = 0; i < connector->count_modes; i++) {
        drmModeModeInfo *mode = &connector->modes[i];
        printf("Mode %d:\n", i + 1);
        printf("  Name: %s\n", mode->name);
        printf("  Clock: %u kHz\n", mode->clock);
        printf("  H Display: %u\n", mode->hdisplay);
        printf("  H Sync Start: %u\n", mode->hsync_start);
        printf("  H Sync End: %u\n", mode->hsync_end);
        printf("  H Total: %u\n", mode->htotal);
        printf("  H Skew: %u\n", mode->hskew);
        printf("  V Display: %u\n", mode->vdisplay);
        printf("  V Sync Start: %u\n", mode->vsync_start);
        printf("  V Sync End: %u\n", mode->vsync_end);
        printf("  V Total: %u\n", mode->vtotal);
        printf("  V Scan: %u\n", mode->vscan);
        printf("  Flags: %u\n", mode->flags);
        printf("  Type: %u\n", mode->type);
        printf("\n");
    }
    
    
    
    drmModeModeInfo *mode = &connector->modes[0];

    printf("Mode Information:\n");
    printf("Name: %s\n", mode->name);
    printf("Clock: %u kHz\n", mode->clock);
    printf("H Display: %u\n", mode->hdisplay);
    printf("H Sync Start: %u\n", mode->hsync_start);
    printf("H Sync End: %u\n", mode->hsync_end);
    printf("H Total: %u\n", mode->htotal);
    printf("H Skew: %u\n", mode->hskew);
    printf("V Display: %u\n", mode->vdisplay);
    printf("V Sync Start: %u\n", mode->vsync_start);
    printf("V Sync End: %u\n", mode->vsync_end);
    printf("V Total: %u\n", mode->vtotal);
    printf("V Scan: %u\n", mode->vscan);
    printf("Flags: %u\n", mode->flags);
    printf("Type: %u\n", mode->type);   
    printf("\n");

    drmModeEncoder *encoder = drmModeGetEncoder(drm_fd, connector->encoder_id);
    printf("Encoder Information:\n");
    printf("ID: %u\n", encoder->encoder_id);
    printf("Type: %u\n", encoder->encoder_type);
    printf("CRTC ID: %u\n", encoder->crtc_id);
    printf("Possible CRTCs: 0x%x\n", encoder->possible_crtcs);
    printf("Possible Clones: 0x%x\n", encoder->possible_clones);
    printf("\n");
    
    
    drmModeCrtc *crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id);
    printf("CRTC Information:\n");
    printf("ID: %u\n", crtc->crtc_id);
    printf("Buffer ID: %u\n", crtc->buffer_id);
    printf("X: %d\n", crtc->x);
    printf("Y: %d\n", crtc->y);
    printf("Width: %u\n", crtc->width);
    printf("Height: %u\n", crtc->height);
    printf("Mode Valid: %d\n", crtc->mode_valid);    
    // Print mode information if valid
    if (crtc->mode_valid) {
        printf("Mode:\n");
        printf("  Name: %s\n", crtc->mode.name);
        // Print other mode details as needed
    }
    printf("\n");
    
    // Create a buffer object (BO) for pixel data
    // You need to fill the buffer with your pixel data
    //int stride = 0;  // Replace with the actual stride value
    //uint32_t handle = 0;  // Replace with the actual handle value

    //uint32_t fb_id;
    //drmModeFB *fb = drmModeAddFB(drm_fd, mode->hdisplay, mode->vdisplay, 24, 32, stride, handle, &fb_id);

  // Set up framebuffer parameters
    uint32_t stride = WIDTH * (BPP / 8);
    uint32_t size = stride * HEIGHT;
    uint32_t handle;
    //uint32_t fb_id;

    uint32_t fb_id; // the identifier of the newly created framebuffer
    int ret = drmModeAddFB(drm_fd, WIDTH, HEIGHT, BPP, BPP, stride, handle, &fb_id);
    if (ret==0) {
        perror("Error adding framebuffer");
        fprintf(stderr, "Parameters:\n");
        fprintf(stderr, "  Width: %u\n", WIDTH);
        fprintf(stderr, "  Height: %u\n", HEIGHT);
        fprintf(stderr, "  BPP: %u\n", BPP);
        fprintf(stderr, "  Stride: %u\n", stride);
        fprintf(stderr, "  Handle: %u\n", 0);
        fprintf(stderr, "  FB ID Pointer: %p\n", (void*)&fb_id);

        drmModeFreeConnector(connector);
        drmModeFreeResources(resources);
        close(drm_fd);
        return 1;
    }


    drmModeSetCrtc(drm_fd, crtc->crtc_id, fb_id, 0, 0, &connector->connector_id, 1, mode);

    // Wait or perform other operations

    // Cleanup
    //drmModeRmFB(fb);
    drmModeRmFB(drm_fd, fb_id);

    drmModeFreeConnector(connector);
    drmModeFreeEncoder(encoder);
    drmModeFreeCrtc(crtc);
    drmModeFreeResources(resources);

    close(drm_fd);

    return 0;
}

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 15844
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: How to transmit video data to MIPI DSI port?

Tue Nov 28, 2023 4:32 pm

Have you configured the display in the kernel? Userspace can't configure a DSI panel.

At a guess you're seeing v3d and vc4 swapping /dev/dri/cardN references between boots. One is the 3D block on Pi4&5, and the other is the display output pipeline. You need to use the appropriate one, and that does mean enumerating each in turn until you find what you're looking for.
Software Engineer at Raspberry Pi Ltd. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

cbeef
Posts: 1
Joined: Wed Nov 22, 2023 4:11 pm

Re: How to transmit video data to MIPI DSI port?

Tue Nov 28, 2023 8:20 pm

How do you add code into the kernel?

Do you agree with this synopsis from Claude 2:

Here are the key steps to add code to the Raspberry Pi kernel to create a new panel driver for an LCD using the MIPI DSI port:

Get the Raspberry Pi kernel source code
Clone the latest Raspberry Pi kernel source code from the github repository (https://github.com/raspberrypi/linux)
Configure the kernel
Run make bcm2709_defconfig (for Pi 2/3) or make bcm2711_defconfig (for Pi 4)
Enable any additional kernel options you need
Add a new driver file
Create a new .c file under drivers/gpu/drm/panel/ (e.g. newpanel.c)
Implement panel specific functions like enable, disable, get_modes etc.
Register the new panel driver
Add an entry for the new panel to the panel_dpi_and_dsi() function in drivers/gpu/drm/drm_panel_dpi.c
This will register the new driver with the DRM subsystem
Build the kernel
Run make to build the kernel with the new driver code
Install kernel and modules
Copy kernel image, device tree blobs and modules to boot partition
Update config to boot new kernel image
Test the new panel driver
Boot Raspberry Pi with new kernel
Check if new panel is detected and displays output
The key steps are adding the driver in the right location, registering with DRM using the right APIs and booting the kernel to test. Read existing panel drivers for examples.

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 15844
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: How to transmit video data to MIPI DSI port?

Wed Nov 29, 2023 10:37 am

I don't know who Claude 2 is. If it's the competitor to ChatGPT, then don't trust AI to provide technical answers.

Have a look at https://github.com/raspberrypi/linux/pull/5433 which added a panel driver for Waveshare's DSI displays.

You need to either create a new panel driver OR modify drivers/gpu/drm/panel/panel-simple.c if it is a truly simple panel with no configuration required.
There is no file drivers/gpu/drm/drm_panel_dpi.c. Modules are built and advertise device tree compatible strings that they support. You then need a device tree overlay that asks the kernel to load your module by using that compatible string, and attach it to the appropriate DSI host controller.

Any PR should break it into 3 parts - the driver, the defconfig change, and the dtoverlay. They are independent subsystems within the Linux kernel tree, so changing all 3 at once makes maintenance a pain.
Software Engineer at Raspberry Pi Ltd. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

Return to “Graphics programming”