This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM5708: Framebuffer rotation

Part Number: AM5708


Tool/software:

I am setting up a new hardware with a new display. I have extended the panel-simple.c driver and it works fine. However, the image is rotated 180º. I have tried to rotate by adding the rotation = <180>; property in the device-tree but it only showed a black screen instead.

Did I miss something? Should that property be enough for the framebuffer to be inverted by hardware?

I have tried other approaches like passing fbcon=rotate:2 to the kernel boot command line. This inverted the linux logo and the framebuffer console (fbcon), but when I execute an application (i.e.: /usr/share/examples/gui/analogclock/analogclock) it still shows wrong (as without the parameter). I can change my QT application to be rotated by code, but I'd rather have linux and/or the AM57xx display subsystem do it for me.

I am using Arago, as provided in SDK8 (ti-processor-sdk-linux-rt-am57xx-evm-08_02_01_00), and it doesn't use any X server, just plain frame buffer access (/dev/fb0).

This is my DTS:

lcd0: display {
    status = "okay";
    compatible = "himax,hx8238";
    label = "lcd";
    //rotation = <180>;
    power-supply = <&lp8732_ldo0_reg>;
    reg = <0>;
    enable-gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;

    port {
        lcd_in: endpoint {
        remote-endpoint = <&dpi_out>;
        data-lines = <24>;
    };
};
  • Hello Sebastian,

    Will inquire internally. 

    Best, 

    Josue

  • Hello, 

    This appears to be more of a display related issue than graphics. 

    Did I miss something? Should that property be enough for the framebuffer to be inverted by hardware?

    I am not sure rotation =<180>; is the correct way to set the value. We should be able to confirm by checking the device tree bindings.

  • Thanks for the replies! As I said, the display is a panel-simple extension: I have no way to configure it through commands to invert it. I was expecting a way for the display sub system to convert the output to the display lines somehow.

    There are some references to it in the manual, but it's not clear to me how to do it, or where those registers are modified in the driver/s:

    Page 3785 (https://www.ti.com/lit/ug/spruhz7k/spruhz7k.pdf?ts=1727765396976)

  • Hello Sebastian, 

    Are you using the default driver or your own custom driver for the panel-simple driver? I believe it could be an error in your custom driver.

    Thanks,
    Sarabesh S.

  • Hello Sarabesh,

    As I said, I've added my panel to simple-panel to define its timings, like this:

    --- a/ti-processor-sdk-linux-rt-am57xx-evm-08_02_01_00/board-support/linux-rt-5.10.100+gitAUTOINC+204ec708dc-g204ec708dc/drivers/gpu/drm/panel/panel-simple.c
    +++ b/ti-processor-sdk-linux-rt-am57xx-evm-08_02_01_00/board-support/linux-rt-5.10.100+gitAUTOINC+204ec708dc-g204ec708dc/drivers/gpu/drm/panel/panel-simple.c
    @@ -2961,6 +2961,38 @@ static const struct panel_desc nlt_nl6448ac18 = {
            .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE,
     };
     
    +/* Driver for 320x240 display */
    +static const struct drm_display_mode himax_hx8238_mode = {
    +       .width_mm = 70,
    +       .height_mm = 52,
    +       .clock = 6500,
    +       .hdisplay = 320,
    +       .hsync_start = 320 + 68,
    +       .hsync_end = 320 + 68 + 20,
    +       .htotal = 320 + 68 + 20 + 32,
    +       .vdisplay = 240,
    +       .vsync_start = 240 + 18,
    +       .vsync_end = 240 + 18 + 4,
    +       .vtotal = 240 + 18 + 4 + 4,
    +       .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
    +};
    +
    +static const struct panel_desc himax_hx8238 = {
    +       .modes = &himax_hx8238_mode,
    +       .num_modes = 1,
    +       .bpc = 8,
    +       .size = {
    +               .width = 70,
    +               .height = 52,
    +       },
    +       .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
    +       .connector_type = DRM_MODE_CONNECTOR_DSI,
    +       .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE,
    +};
    +
     static const struct drm_display_mode nvd_9128_mode = {
            .clock = 29500,
            .hdisplay = 800,
    @@ -4207,6 +4239,9 @@ static const struct of_device_id platform_of_match[] = {
            }, {
                    .compatible = "nlt,nl6448ac18",
                    .data = &nlt_nl6448ac18,
    +       }, {
    +               .compatible = "himax,hx8238",
    +               .data = &himax_hx8238,
            }, {
                    .compatible = "nvd,9128",
                    .data = &nvd_9128,

    However, I understand that it's the AM57x Display Subsystem the one that can be configured to rotate the screen and send the HSYNC/VSYNC/Color-data signals in a different order, or in other words, to apply a conversion, so it can be done regardless of the panel being used.

  • Hi Sebastian, 

    It looks like your simple-panel driver is not set with the correct values. I am not familiar with that compatible, but based on our driver, I don't see any of the regular panel descriptors using that connector_type, DRM_MODE_CONNECTOR_DSI. Only the MIPI_DSI_driver panels have that connector_type. I'm afraid there is not much assistance I can provide other than referring you to our panel driver since your error is with a custom driver extension.

    Thank you,
    Sarabesh S.

  • Thanks for your help, but the display is working well, the timings are the ones recommended by the manufacturer, and there is no way to rotate it by setting those paramaters. My question is about the AM57 DSS to transform the output, as the manual suggests it can do, but looks like it's not supported by the driver.

  • Hi Sebastian,

    Let me bring this up internally to our experts to see what their feedback is. I will update this thread with more information as it comes.

    Thanks,
    Sarabesh S.

  • Hi Sebastian,

    There is an older thread that refers to the same topic. You should be able to refer to this thread to help get started with rotating the display. This is an older legacy device so a lot of current support is limited.

    Thanks,
    Sarabesh S.

  • Thank you very much Sarabesh! Looks like as I was suspecting it's not supported by the linux driver and needs tweaking in the application. I'll take a look into that thread, perhaps there is something we can use.

    Best regards,

  • No problem and that sounds good. Please update this thread with any relevant findings, resolution, or further questions you may have. 

    Regards,
    Sarabesh S.

  • Hello again,

    We have found a workaround to our rotation problem, hope it helps others with similar issues.

    1. To rotate the splash in U-Boot, we just use a rotated image ;-)
    2. To rotate the "framebuffer console" or fbcon, we add to the kernel command line (in u-boot bootargs): fbcon=rotate:2 This also rotates the linux logo (splash)
    3. The problem we were trying to solve with this thread was the QT application rotation, which runs in framebuffer. A first approach was to use the solutions in this StackOverflow answer. Although it worked, for some reason button press events were received several times, likely because of our application architecture, but if no rotation was applied it worked well. The solution was to patch QT 5 libraries, following this post and doing some rework since the patch provided there is for an older version of QT 5.

    Since we are using Yocto, I had to add the patch in meta-qt5/recipes-qt/qt5/qtbase/0023-linuxfb_rotation.patch and modify meta-qt5/recipes-qt/qt5/qtbase/qtbase_git.bb to include it among the other patches.

    The patch id modified so QT applications are rotated 180 degrees by default, but you can rotate to other values as explained in borkedlabs.com post, with 

    export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0:rotation=90
    

    This is the patch content:

    diff --git a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp
    index a66c9fa..a5b5f13 100644
    --- a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp
    +++ b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp
    @@ -287,7 +287,7 @@ static void blankScreen(int fd, bool on)
    }
    QLinuxFbScreen::QLinuxFbScreen(const QStringList &args)
    - : mArgs(args), mFbFd(-1), mTtyFd(-1), mBlitter(0)
    + : mArgs(args), mFbFd(-1), mTtyFd(-1), mBlitter(0), mRotation(180)
    {
    mMmap.data = 0;
    }
    @@ -313,6 +313,7 @@ bool QLinuxFbScreen::initialize()
    QRegularExpression mmSizeRx(QLatin1String("mmsize=(\\d+)x(\\d+)"));
    QRegularExpression sizeRx(QLatin1String("size=(\\d+)x(\\d+)"));
    QRegularExpression offsetRx(QLatin1String("offset=(\\d+)x(\\d+)"));
    + QRegularExpression rotationRx(QLatin1String("rotation=(0|90|180|270)"));
    QString fbDevice, ttyDevice;
    QSize userMmSize;
    @@ -334,6 +335,8 @@ bool QLinuxFbScreen::initialize()
    ttyDevice = match.captured(1);
    else if (arg.contains(fbRx, &match))
    fbDevice = match.captured(1);
    + else if (arg.contains(rotationRx, &match))
    + mRotation = match.captured(1).toInt();
    }
    if (fbDevice.isEmpty()) {
    @@ -372,10 +375,17 @@ bool QLinuxFbScreen::initialize()
    mDepth = determineDepth(vinfo);
    mBytesPerLine = finfo.line_length;
    QRect geometry = determineGeometry(vinfo, userGeometry);
    + QRect originalGeometry = geometry;
    + if( mRotation == 90 || mRotation == 270 )
    + {
    + int tmp = geometry.width();
    + geometry.setWidth(geometry.height());
    + geometry.setHeight(tmp);
    + }
    +
    mGeometry = QRect(QPoint(0, 0), geometry.size());
    mFormat = determineFormat(vinfo, mDepth);
    - mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, geometry.size());
    -
    + mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, originalGeometry.size());
    // mmap the framebuffer
    mMmap.size = finfo.smem_len;
    uchar *data = (unsigned char *)mmap(0, mMmap.size, PROT_READ | PROT_WRITE, MAP_SHARED, mFbFd, 0);
    @@ -384,11 +394,11 @@ bool QLinuxFbScreen::initialize()
    return false;
    }
    - mMmap.offset = geometry.y() * mBytesPerLine + geometry.x() * mDepth / 8;
    + mMmap.offset = originalGeometry.y() * mBytesPerLine + originalGeometry.x() * mDepth / 8;
    mMmap.data = data + mMmap.offset;
    QFbScreen::initializeCompositor();
    - mFbScreenImage = QImage(mMmap.data, geometry.width(), geometry.height(), mBytesPerLine, mFormat);
    + mFbScreenImage = QImage(mMmap.data, originalGeometry.width(), originalGeometry.height(), mBytesPerLine, mFormat);
    mCursor = new QFbCursor(this);
    @@ -414,8 +424,27 @@ QRegion QLinuxFbScreen::doRedraw()
    mBlitter->setCompositionMode(QPainter::CompositionMode_Source);
    for (const QRect &rect : touched)
    + {
    + if( mRotation == 90 || mRotation == 270 )
    + {
    + mBlitter->translate(mGeometry.height()/2, mGeometry.width()/2);
    + }
    + else if( mRotation == 180 )
    + {
    + mBlitter->translate(mGeometry.width()/2, mGeometry.height()/2);
    + }
    +
    + if( mRotation != 0 )
    + {
    + mBlitter->rotate(mRotation);
    + mBlitter->translate(-mGeometry.width()/2, -mGeometry.height()/2);
    + }
    +
    mBlitter->drawImage(rect, mScreenImage, rect);
    + mBlitter->resetTransform();
    + }
    +
    return touched;
    }
    diff --git a/src/plugins/platforms/linuxfb/qlinuxfbscreen.h b/src/plugins/platforms/linuxfb/qlinuxfbscreen.h
    index 1997d46..a34414f 100644
    --- a/src/plugins/platforms/linuxfb/qlinuxfbscreen.h
    +++ b/src/plugins/platforms/linuxfb/qlinuxfbscreen.h
    @@ -57,6 +57,7 @@ private:
    QStringList mArgs;
    int mFbFd;
    int mTtyFd;
    + int mRotation;
    QImage mFbScreenImage;
    int mBytesPerLine;