Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4 lanes DSI panel on cm3+ enabling error with VC4 #4323

Open
aromanro opened this issue May 5, 2021 · 45 comments
Open

4 lanes DSI panel on cm3+ enabling error with VC4 #4323

aromanro opened this issue May 5, 2021 · 45 comments

Comments

@aromanro
Copy link

aromanro commented May 5, 2021

Description

We have a custom device using cm3+ that has a 4 lanes DSI panel. The panel is disabled when it enters in screen saving mode, and sometimes when it's enabled back (e.g. when touched) the log contains this:

[   55.052332] Enabling!
[   56.074629] vc4_dsi 3f700000.dsi: transfer interrupt wait timeout
[   56.074654] vc4_dsi 3f700000.dsi: instat: 0x00000000
[   56.074812] [drm:vc4_dsi_host_transfer [vc4]] *ERROR* DSI transfer failed, resetting: -110
[   56.144764] Enabled!

Initially the screen remained blank but I added a workaround attempting to call to mipi_dsi_dcs_set_display_on several times (with a small delay between the calls) before giving up, hopefully this solves the issue for now.

System

  • Which model of Raspberry Pi?

cm3+

  • Which OS and version (cat /etc/rpi-issue)?

Raspberry Pi reference 2021-01-11
Generated using pi-gen, https://github.com/RPi-Distro/pi-gen, 21090519d85bdaa1615d5d5057d37b09368ea5d2, stage4

  • Which firmware version (vcgencmd version)?

Feb 25 2021 12:12:09
Copyright (c) 2012 Broadcom
version 564e5f9b852b23a330b1764bcf0b2d022a20afd0 (clean) (release) (start)

  • Which kernel version (uname -a)?

Linux raspberrypi 5.10.17-v7+ #1403 SMP Mon Feb 22 11:29:51 GMT 2021 armv7l GNU/Linux

@aromanro aromanro changed the title 4 lanes DSI panel on cm3+ enabling error 4 lanes DSI panel on cm3+ enabling error with VC4 May 5, 2021
@6by9
Copy link
Contributor

6by9 commented May 5, 2021

Check the state of the DSI data lanes (particularly lane 0) when your display is repowered. If they aren't in LP-11 then the DSI block will timeout waiting for them to enter that state before sending the command.

Are you sending the commands in LP or HS mode? (MIPI_DSI_MODE_LPM for the panel's mode_flags). LP is generally easier to monitor on a scope.

@aromanro
Copy link
Author

aromanro commented May 5, 2021

HS mode for now. I'll change it to LP to see if it makes a difference.

@aromanro
Copy link
Author

aromanro commented May 5, 2021

Unfortunately setting that flag does not seem to help much:

[ 1105.008805] Enabling!
[ 1106.085326] vc4_dsi 3f700000.dsi: transfer interrupt wait timeout
[ 1106.085347] vc4_dsi 3f700000.dsi: instat: 0x00000000
[ 1106.085525] [drm:vc4_dsi_host_transfer [vc4]] *ERROR* DSI transfer failed, resetting: -110
[ 1106.155480] Enabled!

@6by9
Copy link
Contributor

6by9 commented May 5, 2021

Does your panel have an enable or reset GPIO that you have connected? If so then have you verified that you are complying with the startup timings between asserting/clearing the GPIO and sending your first command?

If the default state for the GPIO happens to match that for "panel active" then on boot your panel will be active far sooner than you think it is, and sending commands works. On power down the power actually gets killed, and then power up to first command time may be too short.

Beyond that I can't offer much guidance without seeing code.

@aromanro
Copy link
Author

aromanro commented May 5, 2021

It does have a reset pin (no enable pin). As far as I know I respected the timings.
I'll check again.

I'll see if I can post the code here.

@aromanro
Copy link
Author

aromanro commented May 5, 2021

Here is the code (I eliminated the long list of initialization commands).
As it's work in progress (we're trying to fix the DSI timeout issue), it has some commented code in there and other stuff (as error flag in /proc file to detect it and recover by other means):

#include <linux/version.h>
#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
#else
#include <drm/drmP.h>
#endif
#if KERNEL_VERSION(5, 1, 0) <= LINUX_VERSION_CODE
#include <drm/drm_probe_helper.h>
#endif

#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/fb.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>

#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>

#include <video/mipi_display.h>

#include <linux/gpio.h>

#include <linux/proc_fs.h>
#include <linux/seq_file.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
#define HAVE_PROC_OPS
#endif

#define DEFAULT_GPIO_RESET_PIN 13
#define DEFAULT_GPIO_BACKLIGHT_PIN 28

#define CMD_RETRIES 3
#define RETRY_DELAY 50


static atomic_t errorFlag = ATOMIC_INIT(0);
struct proc_dir_entry *procFile;


struct hgltp08_touchscreen
{
    struct drm_panel base;
    struct mipi_dsi_device *dsi;

    int resetPin;
    int gpioResetD;

    int backlightPin;
    int gpioBacklightD;

    bool prepared;
    bool enabled;
};


static struct drm_display_mode default_mode =
{
    .hdisplay	= 800,
    .vdisplay	= 1280,

    .clock = 56535,

    #if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
    #else
    .vrefresh   = 50,
    #endif // KERNEL_VERSION


#define FRONT_PORCH 18
#define SYNC_LEN 18
#define BACK_PORCH 18

    .hsync_start= 800 + FRONT_PORCH,
    .hsync_end	= 800 + FRONT_PORCH + SYNC_LEN,
    .htotal		= 800 + FRONT_PORCH + SYNC_LEN + BACK_PORCH,

#define VFRONT_PORCH 30
#define VSYNC_LEN 4
#define VBACK_PORCH 10

    .vsync_start= 1280 + VFRONT_PORCH,
    .vsync_end	= 1280 + VFRONT_PORCH + VSYNC_LEN,
    .vtotal		= 1280 + VFRONT_PORCH + VSYNC_LEN + VBACK_PORCH,

    .width_mm = 170,
    .height_mm = 106,

    .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
};


static struct hgltp08_touchscreen *panel_to_ts(struct drm_panel *panel)
{
    return container_of(panel, struct hgltp08_touchscreen, base);
}

struct panel_command
{
    struct cmd
    {
        u8	cmd;
        u8	data;
    } cmd;
    u8 delay;
};

#define SWITCH_PAGE_CMD(_page)  \
	{					        \
		.cmd= {			        \
            .cmd = 0xFF,        \
			.data = (_page),	\
		},				        \
		.delay = 0,             \
	}


#define CMD_DELAY(_cmd, _data, _delay)\
	{						    \
        .cmd = {    			\
            .cmd = (_cmd),      \
            .data = (_data),    \
        },  				    \
		.delay = (_delay),      \
	}

#define COMMAND_CMD(_cmd, _data) CMD_DELAY(_cmd, _data, 0)


static const struct panel_command panel_cmds_init[] =
{
// long list of initialization commands removed
};


static ssize_t procfile_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{
  int ef;
  char buf[1];

  if (ubuf == NULL || count <= 0)
    return 0;

  if (ppos == NULL || *ppos > 0)
    return 0;//return -EINVAL;

  ef = atomic_read(&errorFlag);
  buf[0] = ef ? '1' : '0';

  if(copy_to_user(ubuf, buf, 1))
    return -EFAULT;

  *ppos = 1;

  return 1;
}


#ifdef HAVE_PROC_OPS
static const struct proc_ops proc_operations = {
      .proc_read  = procfile_read,
#else
static const struct file_operations proc_operations = {
      .owner = THIS_MODULE,
      .read  = procfile_read,
#endif
};


static int send_cmd_data(struct hgltp08_touchscreen *ctx, u8 cmd, u8 data)
{
    u8 buf[2] = { cmd, data };
    int ret;

    ret = mipi_dsi_dcs_write_buffer(ctx->dsi, buf, sizeof(buf));
    if (ret < 0)
    {
        printk(KERN_ALERT "MIPI DSI DCS write failed: %d\n", ret);

        return ret;
    }

    return 0;
}


static int switch_page(struct hgltp08_touchscreen *ctx, u8 page)
{
    u8 buf[4] = { 0xFF, 0x98, 0x81, page };
    int ret;

    ret = mipi_dsi_dcs_write_buffer(ctx->dsi, buf, sizeof(buf));
    if (ret < 0)
    {
        printk(KERN_ALERT "MIPI DSI DCS write failed for switching page: %d\n", ret);

        return ret;
    }

    return 0;
}

static int hgltp08_init_sequence(struct hgltp08_touchscreen *ctx)
{
    int i, ret, cmdcnt;

    ret = 0;

    for (i = 0; i < ARRAY_SIZE(panel_cmds_init); ++i)
    {
        const struct panel_command *cmd = &panel_cmds_init[i];

        cmdcnt = 0;
        do
        {
            if (cmd->cmd.cmd == 0xFF)
                ret = switch_page(ctx, cmd->cmd.data);
            else
                ret = send_cmd_data(ctx, cmd->cmd.cmd, cmd->cmd.data);

            if (ret) msleep(RETRY_DELAY);

            ++cmdcnt;
        }
        while (ret && cmdcnt <= CMD_RETRIES);

        if (ret)
        {
            printk(KERN_ALERT "Couldn't send initialization command!\n");

            atomic_set(&errorFlag, 1);

            return ret;
        }

        if (cmd->delay)
            msleep(cmd->delay);
    }

    return 0;
}

static int hgltp08_prepare(struct drm_panel *panel)
{
    struct hgltp08_touchscreen *ctx = panel_to_ts(panel);
    struct mipi_dsi_device *dsi = ctx->dsi;
    int ret;
    int cmdcnt;
    bool slow_mode;

    if (ctx->prepared)
        return 0;

    printk(KERN_ALERT "Preparing!\n");


    procFile = proc_create("hgltp08", 0444, NULL, &proc_operations);


    if (!dsi)
        printk(KERN_ALERT "No DSI device!\n");


    ctx->gpioBacklightD = gpio_request(ctx->backlightPin, "gpioBacklightD");
    if (ctx->gpioBacklightD < 0)
    {
        ctx->gpioBacklightD = 0;
        printk(KERN_ALERT "Couldn't grab the gpio BacklightD pin\n");
    }
    else
        ctx->gpioBacklightD = 1;

    if (ctx->gpioBacklightD)
        gpio_direction_output(ctx->backlightPin, 1);


    ctx->gpioResetD = gpio_request(ctx->resetPin, "gpioResetD");
    if (ctx->gpioResetD < 0)
    {
        ctx->gpioResetD = 0;
        printk(KERN_ALERT "Couldn't grab the gpio ResetD pin\n");
    }
    else
        ctx->gpioResetD = 1;

    if (ctx->gpioResetD)
        gpio_direction_output(ctx->resetPin, 1);

    msleep(125);

    if (ctx->gpioResetD)
        gpio_set_value_cansleep(ctx->resetPin, 0);
    msleep(20);
    if (ctx->gpioResetD)
        gpio_set_value_cansleep(ctx->resetPin, 1);

    msleep(125);

    slow_mode = dsi->mode_flags & MIPI_DSI_MODE_LPM;

    if (!slow_mode)
        dsi->mode_flags |= MIPI_DSI_MODE_LPM;

    cmdcnt = 0;
    do
    {
        ret = hgltp08_init_sequence(ctx);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret)
    {
        printk(KERN_ALERT "Couldn't send initialization commands!\n");

        atomic_set(&errorFlag, 1);

        return ret;
    }

    cmdcnt = 0;
    do
    {
        ret = switch_page(ctx, 0);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret)
    {
        printk(KERN_ALERT "Couldn't switch page!\n");

        atomic_set(&errorFlag, 1);

        return ret;
    }

    cmdcnt = 0;
    do
    {
        ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret < 0)
    {
        printk(KERN_ALERT "Couldn't set tear on!\n");

        atomic_set(&errorFlag, 1);
    }

    msleep(125);

    cmdcnt = 0;
    do
    {
        ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret)
    {
        printk(KERN_ALERT "Couldn't exit sleep mode!\n");

        atomic_set(&errorFlag, 1);
    }

    msleep(125);

    cmdcnt = 0;
    do
    {
        ret = mipi_dsi_dcs_set_display_on(dsi);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret)
    {
        printk(KERN_ALERT "Couldn't set display on!\n");

        atomic_set(&errorFlag, 1);
    }

    if (!slow_mode)
        dsi->mode_flags &= ~(MIPI_DSI_MODE_LPM);

    msleep(20);

    ctx->prepared = true;

    printk(KERN_ALERT "Prepared!\n");

    return 0;
}


static int hgltp08_unprepare(struct drm_panel *panel)
{
    struct hgltp08_touchscreen *ctx = panel_to_ts(panel);
    struct mipi_dsi_device *dsi = ctx->dsi;
    int ret, cmdcnt;
    bool slow_mode;

    if (!ctx->prepared)
        return 0;

    printk(KERN_ALERT "Unprepare!\n");

    proc_remove(procFile);

    slow_mode = dsi->mode_flags & MIPI_DSI_MODE_LPM;

    if (!slow_mode)
        dsi->mode_flags |= MIPI_DSI_MODE_LPM;

    cmdcnt = 0;
    do
    {
        ret = mipi_dsi_dcs_set_display_off(dsi);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret)
    {
        printk(KERN_WARNING "failed to set display off: %d\n", ret);

        atomic_set(&errorFlag, 1);
    }

    cmdcnt = 0;
    do
    {
        ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret)
    {
        printk(KERN_WARNING "failed to enter sleep mode: %d\n", ret);
        atomic_set(&errorFlag, 1);
    }

    if (!slow_mode)
        dsi->mode_flags &= ~(MIPI_DSI_MODE_LPM);

    msleep(120);

    if (ctx->gpioBacklightD)
        gpio_set_value_cansleep(ctx->backlightPin, 0);

    if (ctx->gpioResetD)
    {
        gpio_free(ctx->resetPin);
        ctx->gpioResetD = 0;
    }

    if (ctx->gpioBacklightD)
    {
        gpio_free(ctx->backlightPin);
        ctx->gpioBacklightD = 0;
    }

    ctx->prepared = false;

    return 0;
}


static int hgltp08_enable(struct drm_panel *panel)
{
    struct hgltp08_touchscreen *ctx = panel_to_ts(panel);
    int ret;
    int cmdcnt;
    struct mipi_dsi_device *dsi = ctx->dsi;
    bool slow_mode;

    if (ctx->enabled)
        return 0;

    printk(KERN_ALERT "Enabling!\n");

    cmdcnt = 0;
    do
    {
        ret = drm_panel_prepare(panel);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret < 0)
    {
        printk(KERN_ALERT "Couldn't prepare the panel!\n");

        atomic_set(&errorFlag, 1);
    }

    slow_mode = dsi->mode_flags & MIPI_DSI_MODE_LPM;
    if (!slow_mode)
        dsi->mode_flags |= MIPI_DSI_MODE_LPM;

    /*

    cmdcnt = 0;
    do
    {
        ret = mipi_dsi_dcs_set_tear_on(ctx->dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret < 0)
    {
        printk(KERN_ALERT "Couldn't set tear on!\n");

        atomic_set(&errorFlag, 1);
    }

    */

    if (ctx->gpioBacklightD)
        gpio_set_value_cansleep(ctx->backlightPin, 1);

    cmdcnt = 0;
    do
    {
        ret = mipi_dsi_dcs_set_display_on(ctx->dsi);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret)
    {
        printk(KERN_ALERT "Couldn't set display on!\n");

        atomic_set(&errorFlag, 1);
    }

    //if (!slow_mode)
    // clear the LPM mode no matter what
    dsi->mode_flags &= ~(MIPI_DSI_MODE_LPM);

    ctx->enabled = true;

    printk(KERN_ALERT "Enabled!\n");

    return 0;
}

static int hgltp08_disable(struct drm_panel *panel)
{
    int cmdcnt;
    int ret;
    struct hgltp08_touchscreen *ctx = panel_to_ts(panel);
    struct mipi_dsi_device *dsi = ctx->dsi;
    bool slow_mode;

    if (!ctx->enabled)
        return 0;

    slow_mode = dsi->mode_flags & MIPI_DSI_MODE_LPM;
    if (!slow_mode)
        dsi->mode_flags |= MIPI_DSI_MODE_LPM;

    cmdcnt = 0;
    do
    {
        ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
        if (ret) msleep(RETRY_DELAY);
        ++cmdcnt;
    }
    while (ret && cmdcnt <= CMD_RETRIES);

    if (ret)
    {
        printk(KERN_ALERT "Couldn't set display off!\n");

        atomic_set(&errorFlag, 1);
    }

    if (ctx->gpioBacklightD)
        gpio_set_value_cansleep(ctx->backlightPin, 0);

    // leave it in slow mode until it wakes up
    //if (!slow_mode)
    //    dsi->mode_flags &= ~(MIPI_DSI_MODE_LPM);

    ctx->enabled = false;

    printk(KERN_ALERT "Disabled!\n");

    return 0;
}

static int hgltp08_get_modes(struct drm_panel *panel
#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
                            , struct drm_connector *connector)
{
    struct drm_display_mode *mode = drm_mode_duplicate(connector->dev, &default_mode);
#else
                            )
{
    static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
    struct drm_display_mode *mode = drm_mode_duplicate(panel->drm, &default_mode);
#endif // KERNEL_VERSION

    if (!mode)
    {
        printk(KERN_ALERT "failed to add mode %ux%ux\n", default_mode.hdisplay, default_mode.vdisplay);
        return -ENOMEM;
    }

    drm_mode_set_name(mode);

    mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;

#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
	drm_mode_probed_add(connector, mode);

	connector->display_info.width_mm = mode->width_mm;
	connector->display_info.height_mm = mode->height_mm;
#else
    drm_mode_probed_add(panel->connector, mode);

    panel->connector->display_info.width_mm = mode->width_mm;
    panel->connector->display_info.height_mm = mode->height_mm;

    panel->connector->display_info.bpc = 8;

    drm_display_info_set_bus_formats(&panel->connector->display_info, &bus_format, 1);
#endif // KERNEL_VERSION

    return 1;
}

static const struct drm_panel_funcs hgltp08_drm_funcs =
{
    .disable = hgltp08_disable,
    .unprepare = hgltp08_unprepare,
    .prepare = hgltp08_prepare,
    .enable = hgltp08_enable,
    .get_modes = hgltp08_get_modes,
};

static int hgltp08_probe(struct mipi_dsi_device *dsi)
{
    int ret;
    const __be32 *prop;

    struct device *dev = &dsi->dev;
    struct hgltp08_touchscreen *ctx;

    printk(KERN_ALERT "Probing!\n");

    // read overwrites of panel parameters from device tree

    prop = of_get_property(dsi->dev.of_node, "clock", NULL);
    if (prop)
    {
        default_mode.clock = be32_to_cpup(prop);
        printk(KERN_ALERT "clock set to %d\n", default_mode.clock);
    }
    else
        printk(KERN_ALERT "clock not set, using default\n");

    prop = of_get_property(dsi->dev.of_node, "hsync_start", NULL);
    if (prop)
    {
        default_mode.hsync_start = be32_to_cpup(prop);
        printk(KERN_ALERT "hsync_start set to %d\n", default_mode.hsync_start);
    }
    else
        printk(KERN_ALERT "hsync_start not set, using default\n");

    prop = of_get_property(dsi->dev.of_node, "hsync_end", NULL);
    if (prop)
    {
        default_mode.hsync_end = be32_to_cpup(prop);
        printk(KERN_ALERT "hsync_end set to %d\n", default_mode.hsync_end);
    }
    else
        printk(KERN_ALERT "hsync_end not set, using default\n");

    prop = of_get_property(dsi->dev.of_node, "htotal", NULL);
    if (prop)
    {
        default_mode.htotal = be32_to_cpup(prop);
        printk(KERN_ALERT "htotal set to %d\n", default_mode.htotal);
    }
    else
        printk(KERN_ALERT "htotal not set, using default\n");

    prop = of_get_property(dsi->dev.of_node, "vsync_start", NULL);
    if (prop)
    {
        default_mode.vsync_start = be32_to_cpup(prop);
        printk(KERN_ALERT "vsync_start set to %d\n", default_mode.vsync_start);
    }
    else
        printk(KERN_ALERT "vsync_start not set, using default\n");

    prop = of_get_property(dsi->dev.of_node, "vsync_end", NULL);
    if (prop)
    {
        default_mode.vsync_end = be32_to_cpup(prop);
        printk(KERN_ALERT "vsync_end set to %d\n", default_mode.vsync_end);
    }
    else
        printk(KERN_ALERT "vsync_end not set, using default\n");

    prop = of_get_property(dsi->dev.of_node, "vtotal", NULL);
    if (prop)
    {
        default_mode.vtotal = be32_to_cpup(prop);
        printk(KERN_ALERT "vtotal set to %d\n", default_mode.vtotal);
    }
    else
        printk(KERN_ALERT "vtotal not set, using default\n");

    // end panel

    ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
    if (!ctx)
        return -ENOMEM;
    memset(ctx, 0, sizeof(*ctx));

    ctx->backlightPin = DEFAULT_GPIO_BACKLIGHT_PIN;

    prop = of_get_property(dsi->dev.of_node, "backlight", NULL);
    if (prop)
    {
        ctx->backlightPin = be32_to_cpup(prop);
        printk(KERN_ALERT "Backlight pin set to %d\n", ctx->backlightPin);
    }
    else
        printk(KERN_ALERT "Backlight pin not set, using default\n");


    ctx->resetPin = DEFAULT_GPIO_RESET_PIN;

    prop = of_get_property(dsi->dev.of_node, "reset", NULL);
    if (prop)
    {
        ctx->resetPin = be32_to_cpup(prop);
        printk(KERN_ALERT "Reset pin set to %d\n", ctx->resetPin);
    }
    else
        printk(KERN_ALERT "Reset pin not set, using default\n");

    mipi_dsi_set_drvdata(dsi, ctx);
    ctx->dsi = dsi;

    dsi->lanes = 4;
    dsi->format = MIPI_DSI_FMT_RGB888;

    dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE /* | MIPI_DSI_MODE_LPM*/;

    printk(KERN_ALERT "DSI Device init for %s!\n", dsi->name);


#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
	drm_panel_init(&ctx->base, dev, &hgltp08_drm_funcs,
		       DRM_MODE_CONNECTOR_DSI);
#else
    drm_panel_init(&ctx->base);
    ctx->base.dev = dev;
    ctx->base.funcs = &hgltp08_drm_funcs;
#endif // KERNEL_VERSION

    drm_panel_add(&ctx->base);

    ret = mipi_dsi_attach(dsi);
    if (ret < 0)
    {
        dev_err(dev, "mipi_dsi_attach() failed: %d\n", ret);

        drm_panel_remove(&ctx->base);
        kfree(ctx);
        return ret;
    }

    printk(KERN_ALERT "Probed!\n");

    return 0;
}

static int hgltp08_remove(struct mipi_dsi_device *dsi)
{
    struct hgltp08_touchscreen *ctx = mipi_dsi_get_drvdata(dsi);

    mipi_dsi_detach(dsi);
    drm_panel_remove(&ctx->base);

    kfree(ctx);

    printk(KERN_ALERT "Removed!\n");

    return 0;
}


static const struct of_device_id hgltp08_touchscreen_of_match[] =
{
    { .compatible = "hgltp08" },
    { }
};
MODULE_DEVICE_TABLE(of, hgltp08_touchscreen_of_match);



static struct mipi_dsi_driver panel_hgltp08_dsi_driver =
{
    .driver = {
        .name = "panel_hgltp08",
        .of_match_table = hgltp08_touchscreen_of_match,
    },
    .probe = hgltp08_probe,
    .remove = hgltp08_remove,
};
module_mipi_dsi_driver(panel_hgltp08_dsi_driver);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Homegear GmbH <contact@homegear.email>");
MODULE_DESCRIPTION("Homegear LTP08 Multitouch 8\" Display; black; WXGA 1280x800; Linux");
MODULE_VERSION("1.0.7");

@mingzhangqun
Copy link

I happened to have the same problem. This problem is easy to reproduce: 'Start Menu' > Preferences > Screen Configuration > 'Right click DSI-1' > Orientation, repeatedly switch between Normal and Right many times, and it will appear.

@mingzhangqun
Copy link

@aromanro Hi, How's it going now? I've tried the same method as you, and it doesn't solve the problem

@aromanro
Copy link
Author

aromanro commented May 6, 2021

It's not solved, indeed.
If it matters, in our case also the screen is rotated 90 degrees.

@mingzhangqun
Copy link

I'm just talking about the way to reproduce. It's not about 90 degrees.
The root cause is in vc4_dsi_host_transfer() function, when rotating the panel, vc4_dsi_host_transfer() will be called and sometimes there will be problems.
I tried to analyze it, but I didn't have the datasheet of DSI.

@aromanro
Copy link
Author

aromanro commented May 7, 2021

I think that call happens when you send DSI commands.

@aromanro
Copy link
Author

aromanro commented May 7, 2021

I cannot find a pattern in the behavior. Most of the times it works ok, but sometimes it fails:

[  757.818342] Enabling!
[  757.820866] Enabled!
[  787.845756] Disabled!
[  789.781974] Enabling!
[  789.784508] Enabled!
[  819.841600] Disabled!
[  943.466889] Enabling!
[  943.469375] Enabled!
[  973.494493] Disabled!
[  974.741391] Enabling!
[  974.743925] Enabled!
[ 1004.803160] Disabled!
[ 1012.044319] Enabling!
[ 1012.046915] Enabled!
[ 1042.092056] Disabled!
[ 1044.647519] Enabling!
[ 1044.650056] Enabled!
[ 1074.714638] Disabled!
[ 1075.834126] Enabling!
[ 1075.836631] Enabled!
[ 1105.881609] Disabled!
[ 1106.934023] Enabling!
[ 1106.936555] Enabled!
[ 1137.001271] Disabled!
[ 1139.148740] Enabling!
[ 1139.151318] Enabled!
[ 1169.176270] Disabled!
[ 1196.670476] Enabling!
[ 1196.672929] Enabled!
[ 1226.717871] Disabled!
[ 1228.586086] Enabling!
[ 1228.588671] Enabled!
[ 1258.653633] Disabled!
[ 1281.943716] Enabling!
[ 1281.946289] Enabled!
[ 1312.011086] Disabled!
[ 1314.912275] Enabling!
[ 1314.914814] Enabled!
[ 1344.959233] Disabled!
[ 1372.156161] Enabling!
[ 1372.158803] Enabled!
[ 1402.203254] Disabled!
[ 1403.565231] Enabling!
[ 1403.567820] Enabled!
[ 1433.632476] Disabled!
[ 1436.867347] Enabling!
[ 1436.869837] Enabled!
[ 1466.914351] Disabled!
[ 1468.304355] Enabling!
[ 1469.357328] vc4_dsi 3f700000.dsi: transfer interrupt wait timeout
[ 1469.357351] vc4_dsi 3f700000.dsi: instat: 0x00000000
[ 1469.357503] [drm:vc4_dsi_host_transfer [vc4]] *ERROR* DSI transfer failed, resetting: -110
[ 1469.477520] Enabled!
[ 1499.718445] Disabled!
[ 1598.313889] Enabling!
[ 1598.316426] Enabled!
[ 1628.340988] Disabled!
[ 1629.647784] Enabling!
[ 1629.650381] Enabled!
[ 1659.715030] Disabled!
[ 1662.916990] Enabling!
[ 1663.997622] vc4_dsi 3f700000.dsi: transfer interrupt wait timeout
[ 1663.997647] vc4_dsi 3f700000.dsi: instat: 0x00000000
[ 1663.997806] [drm:vc4_dsi_host_transfer [vc4]] *ERROR* DSI transfer failed, resetting: -110
[ 1664.117871] Enabled!

I've set a short time interval until the screen goes blank to test it.
In this case the loop recovers it, but I've got a report where the timeout occurred several times in a row, having the error flag set as a consequence (so the /proc file gets a 1).

@6by9
Copy link
Contributor

6by9 commented May 7, 2021

We have a couple of threads going on about DSI, and there isn't a simple answer.

From https://www.raspberrypi.org/forums/viewtopic.php?f=44&t=305690 I have a hunch that the DSI lanes are not being left in LP-11 state correctly during the _enable call to the bridge/panel. Depending on timing and how fussy the device is, that seems to be causing grief on the SN65DSI83/4.

Could you try with commit 6by9@b939eaf from https://github.com/6by9/linux/tree/rpi-5.10.y-sn65dsi8x-marek to see whether that is the cause of your issues too.

@aromanro
Copy link
Author

aromanro commented May 7, 2021

Thank you! I'll try it today, if possible.

@mingzhangqun
Copy link

4,1841,201200358,-;[DSI]panel_bridge_pre_enable
4,1842,201200371,-;[DSI]drm_panel_prepare
6,1843,201200385,-;[DSI]panel_prepare:
6,1844,201273735,-;[DSI]ili9881d_prepare:
3,1845,202313725,-;vc4_dsi fe700000.dsi: transfer interrupt wait timeout
SUBSYSTEM=platform
DEVICE=+platform:fe700000.dsi
3,1846,202313748,-;vc4_dsi fe700000.dsi: instat: 0x00000000
SUBSYSTEM=platform
DEVICE=+platform:fe700000.dsi
3,1847,202313851,-;[drm:vc4_dsi_host_transfer [vc4]] ERROR DSI transfer failed, resetting: -110

Hi, @6by9
I've tried this 6by9@b939eaf, the issue is still.
@aromanro How is yours?

@aromanro
Copy link
Author

aromanro commented May 7, 2021

It seems that I won't know until Monday because I managed to make the device unbootable.

Anyway, we exposed the sources on GitHub for the panel driver, now they are available here: https://github.com/Homegear/Homegear-Linux-Touch-Panel-08-DSI-Display-Driver

@aromanro
Copy link
Author

aromanro commented May 7, 2021

Ok, unfortunately it does the same:

[   40.694822] Unprepare!
[   41.754402] vc4_dsi 3f700000.dsi: transfer interrupt wait timeout
[   41.754433] vc4_dsi 3f700000.dsi: instat: 0x00000000
[   41.754601] [drm:vc4_dsi_host_transfer [vc4]] *ERROR* DSI transfer failed, resetting: -110
[   42.038442] Preparing!

Now even when blanking is activated. I don't think I've seen such case until now, usually it's when preparing that's happening.

@6by9
Copy link
Contributor

6by9 commented May 7, 2021

What's trying to send a DSI message when the panel/bridge is powered down? That sounds like a bug in the driver somewhere as the DSI bus and the panel/bridge is likely powered down.

@aromanro
Copy link
Author

aromanro commented May 7, 2021

My guess is that's originating either from

ret = mipi_dsi_dcs_set_display_off(dsi);

or

ret = mipi_dsi_dcs_enter_sleep_mode(dsi);

in hgltp08_unprepare.

@mingzhangqun
Copy link

6,632,930340753,-;[DSI]panel_disable:
4,633,930562926,-;vc4_dsi fe700000.dsi: Timeout waiting for DSI STOP entry: STAT 0x00020803

I get these 'Timeout' occasionally.

@aromanro
Copy link
Author

aromanro commented May 13, 2021

I did more tests and changed the panel driver to adjust it to the patched video driver: https://github.com/Homegear/Homegear-Linux-Touch-Panel-08-DSI-Display-Driver/blob/main/lcd_driver.c

My guess is that the patched driver after disabling does immediately an unpreparing, too, when the screen is blanked, the goal being that when it's woken up the prepare will reset the panel.

I modified the panel driver to reset each time there is a DSI failure in initialization (I also played a lot with the timings for reset) but still the errors can happen a lot of times in a row and that renders the panel unusable.
Looks as a problem originating elsewhere, not in the panel (I don't see how it can persist after resetting the panel, but it doesn't persist when you reset the cm3 device).

If after such failure one touches it after a while (after the blanking is active again) it can come back to life. Quite odd.

@mingzhangqun
Copy link

mingzhangqun commented May 14, 2021

If after such failure one touches it after a while (after the blanking is active again) it can come back to life. Quite odd.

This is because after the blanking is active again, the panel will be initialized again and the display will return to normal.

@aromanro
Copy link
Author

aromanro commented May 14, 2021

Maybe, but why it doesn't in the retry loop?
https://github.com/Homegear/Homegear-Linux-Touch-Panel-08-DSI-Display-Driver/blob/c07b6098f68060dbdaa5cd9a4ee34d8c6399a82e/lcd_driver.c#L484

The failure there is always in the first initialization command, the 'switch page' one.

@mingzhangqun
Copy link

The failure there is always in the first initialization command, the 'switch page' one.

I guess because DSI host is not ready.

@aromanro
Copy link
Author

aromanro commented May 14, 2021

I tried various solutions, including changing the delays. Even if I put a huge delay after reset, the error can still occur.

Even if that is the case, that does not explain how it's ready very often after such reset, but then when that DSI error is happening, it's not ready 3 times or more in a row (I tried even 6 times).

@aromanro
Copy link
Author

We 'solved' the problems we had - for now - by eliminating mipi_dsi_dcs_set_display_off / mipi_dsi_dcs_set_display_on calls from disable / enable, also the mipi_dsi_dcs_enter_sleep_mode from unprepare. With the official kernel it works, no more DSI command errors that lock the screen.

I've still noticed once a Timeout waiting for DSI STOP entry but it doesn't seem to affect the functionality, so for now we're having a workaround. Not ideal, but better than the alternative.

@aromanro
Copy link
Author

aromanro commented May 18, 2021

Unfortunately I was too eager to say that the fix from yesterday works... it doesn't always work. I touched the screen once and it didn't came back to life, it remained blank. 'Enable' was called but not having any DSI call anymore, there was no error in there. That means that the flag remained zero. The issue is definitively originating elsewhere. Now with no DSI error in the log, too.

After entering screen blanking again, it came back to life. I need to find out what's really happening when blanking/unblanking...

My guess is that something / somewhere puts some circuitry in low power mode and leave it that way sometimes when unblanking. The DSI errors are a consequence of that.

Also a vc4_dsi 3f700000.dsi: Timeout waiting for DSI STOP entry: STAT 0x00020803 occurred, I don't know if when the screen remained blanked or during something else, but it was quite close to that time.

LE: It is related, it appears a little bit later than the issue, but seems connected.

@mingzhangqun
Copy link

Also a vc4_dsi 3f700000.dsi: Timeout waiting for DSI STOP entry: STAT 0x00020803 occurred, I don't know if when the screen remained blanked or during something else, but it was quite close to that time.

This is no matter. It just needs to be modified:
ret = wait_for((DSI_PORT_READ(STAT) & stat_stop) == stat_stop, /*200*/500);

@aromanro
Copy link
Author

aromanro commented May 20, 2021

I've got rid of all the errors by changing the VC4 driver.

I'll add a description today after I'll do some more tests with the DSI commands back in the enable/disable functions.

@mingzhangqun
Copy link

mingzhangqun commented May 20, 2021

I've got rid of all the errors by changing the VC4 driver.

I'll add a description today after I'll do some more tests with the DSI commands back in the enable/disable functions.

Can you show me what you have changed?

@aromanro
Copy link
Author

aromanro commented May 20, 2021

Yes. Btw, with those changes I still get those timeouts from above, if I turn on the dsi commands in enable/disable and so on in the panel driver, but it recovers in the loop. As it is right now https://github.com/Homegear/Homegear-Linux-Touch-Panel-08-DSI-Display-Driver/blob/main/lcd_driver.c there are no errors anymore, and I tested turning on/off the screen thousands of times (with a script). Before, typically in 100 attempts it happened and it wasn't recoverable at panel reset (unless blanking activated again).

Here are the changes:

Here, in vc4_dsi_encoder_disable: https://github.com/6by9/linux/blob/rpi-5.10.y-sn65dsi8x-marek/drivers/gpu/drm/vc4/vc4_dsi.c#L818
Comment out the code:

clk_disable_unprepare(dsi-&gt;pll_phy_clock);	
clk_disable_unprepare(dsi-&gt;escape_clock);	
clk_disable_unprepare(dsi-&gt;pixel_clock);

pm_runtime_put(dev);

In vc4_dsi_encoder_enable, here: https://github.com/6by9/linux/blob/rpi-5.10.y-sn65dsi8x-marek/drivers/gpu/drm/vc4/vc4_dsi.c#L895

Comment out the code:

ret = pm_runtime_get_sync(dev);	
if (ret) {		
      DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi-&gt;variant-&gt;port);		
      return;	
}

I'm currently trying to understand what difference does it make to disable only clocks disabling/unpreparing.

With the committed code, I needed to comment out both the clocks code and the pm code, but it might work ok if I turn back on the on/off/sleep dsi commands in the panel driver, with only one of them commented out. Still testing...

@aromanro
Copy link
Author

I also get

[  311.757362] vc4_dsi 3f700000.dsi: transfer interrupt wait timeout
[  311.757393] vc4_dsi 3f700000.dsi: instat: 0x00000000
[  311.757562] [drm:vc4_dsi_host_transfer [vc4]] *ERROR* DSI transfer failed, resetting: -110

But they recover. Strange.
They seem to occur only in disabling/unpreparing functions, if I turn back on the dsi commands.

@6by9
Copy link
Contributor

6by9 commented May 20, 2021

pll_phy_clock, escape_clock, and pixel_clock are all internal clocks to the SoC. They shouldn't take any significant time to come up, but that's the only thing I can think of in vc4_dsi_encoder_enable between enabling the clocks and calls into the pre_enable functions.

pm_runtime_[get|put] should power the blocks up/down, however there isn't fully discrete blocks power gating to the blocks, so I suspect that they actually result in very little different (AIUI they should request the power domains as registered at https://github.com/raspberrypi/linux/blob/rpi-5.10.y/arch/arm/boot/dts/bcm2835-rpi.dtsi#L85)

Having looked into the way DRM wants DSI to work, the current driver doesn't fully do the job, and I'm looking to rework it.

There seems to be an expectation that the transfer function can be called at any time, so what state should the DSI lines be left in afterwards? Dropping to ULPS or LP-00 (off) actually means leaving the PHY active.

The pre_enable functions of all devices are called starting from the panel, and coming back towards the DSI encoder. Many drivers send their DSI initialisation sequences from pre_enable, but haven't used pm_runtime to power up the chain, and the encoder hasn't had any prior notification that it is being enabled, Yet if you read the datasheets for most of these devices they expect the DSI lines to be in LP-11 (enabled) state before the chip is powered on. I don't see how this is achieved on other platforms.

This is the reason why vc4_dsi deliberately breaks the chain of encoders/bridges/displays so that the framework sees it as the end of the chain. It therefore gets called before the bridges/panels so can power up, and it then calls all the pre_enables/enables/disables/post_disables once it has got the DSI block into a sensible state. However this also then breaks the the calling of all the mode_fixups, mode_set, and mode_valid calls from the framework, and there is no way to replicate those within vc4_dsi as it doesn't have all the state.

The only solution I can see is to power up DSI from bind, and power it down in unbind. Anything else is leaving things in odd states or unable to honour all the requests that a bridge or panel driver can make.

@mingzhangqun
Copy link

@aromanro Hi, have you solved the problem?

@aromanro
Copy link
Author

Completely, no. I just reduced its frequency by one order of magnitude with the already mentioned changes. I'm still looking into it.

@aromanro
Copy link
Author

aromanro commented Jun 3, 2021

Ok, I give up for now.
The most stable VC4 dsi for us is here: https://github.com/Homegear/homegear-vc4-driver

Besides the changes I described above, I also tweaked some flags, here:
https://github.com/Homegear/homegear-vc4-driver/blob/43d897910e108c2c07895a42a4b433f21c10bdf1/vc4_dsi.c#L1124

Unfortunately the DSI errors did not go away, but they are now fewer and can recover (usually?).

I am testing with this simple script https://github.com/Homegear/Homegear-Linux-Touch-Panel-08-DSI-Display-Driver/blob/master/test/test.sh and in 3000 blanking/unblankings I see a little over 100 DSI errors, but they go away when retrying (maybe they need two retries).

Sometimes they cannot be fixed by retrying, in which case BIG PROBLEM! is displayed because it is signaled by the panel driver in the proc file. This happens once each 6000 blanking/unblankings or so.

I think the patch from @6by9 made things worse for us, for some reason the errors couldn't be avoided by retrying to send the command anymore (so lots of BIG PROBLEMs).

As an odd thing, the DSI errors occur mostly when disabling the panel but the commands work after a retry or two. If they occur when preparing the panel (typically in the initialization sequence after reset) they fail no matter how many times they are resent (even if the panel is reset again before retrying).

@6by9
Copy link
Contributor

6by9 commented Jun 3, 2021

I'm still looking into the DSI driver.
https://github.com/6by9/linux/commits/rpi-5.10.y-sn65dsi8x-marek/drivers/gpu/drm/vc4/vc4_dsi.c is the current state that I'm at, and it reworks the driver to use pm_runtime and remove the hacking around of the bridge chain.

Reports are that it is working with SN65DSI83 (the old driver wasn't), and it certainly works with the LT070ME05000 (1200x1920 Nexus 7) panel. I need to try it with the Pi 7" DSI panel (TC358743 + 800x480 panel). It'd be interesting to know if it works for your panel.

@aromanro
Copy link
Author

aromanro commented Jun 3, 2021

Nice, that looks like quite a change, I'll check it out. Thank you!

@aromanro
Copy link
Author

aromanro commented Jun 4, 2021

@6by9 Unfortunately I cannot say it works better for us. I started testing it and I got already three "BIG PROBLEM!" (that is, DSI errors at initialization that couldn't be recovered by resetting again and retrying sending the initialization sequence - several times). And probably will issue several more until the end of the 3000 tries loop.

With the variant I changed, we get at most one of those more serious issues each 3000 blanking/unblanking tests.

@aromanro
Copy link
Author

aromanro commented Jun 4, 2021

Please discard that, I think I cloned the wrong branch. I'll try again.

@aromanro
Copy link
Author

aromanro commented Jun 4, 2021

Unfortunately, the correct branch didn't work out as well.
I'm getting new errors, too:

[  176.820111] Unprepared!
[  177.907567] Preparing!
[  178.043987] [drm:vc4_dsi_runtime_suspend [vc4]] *ERROR* vc4_dsi_runtime_suspend: 
[  178.144029] [drm:vc4_dsi_runtime_resume [vc4]] *ERROR* vc4_dsi_runtime_resume: 
[  178.145817] [drm:vc4_dsi_runtime_resume [vc4]] *ERROR* vc4_dsi_runtime_resume: All good
[  178.423819] Prepared!
[  178.426337] Enabling!
[  178.428636] Enabled!
[  179.592424] Disabling!
[  180.633856] vc4_dsi 3f700000.dsi: transfer interrupt wait timeout
[  180.633886] vc4_dsi 3f700000.dsi: instat: 0x00000000
[  180.634062] [drm:vc4_dsi_host_transfer [vc4]] *ERROR* DSI transfer failed, resetting: -110
[  181.833826] vc4_dsi 3f700000.dsi: transfer interrupt wait timeout
[  181.833845] vc4_dsi 3f700000.dsi: instat: 0x00000000
[  181.833943] [drm:vc4_dsi_host_transfer [vc4]] *ERROR* DSI transfer failed, resetting: -110
[  182.073995] Disabled!
[  182.077915] Unpreparing!
[  182.080473] Unprepared!
[  183.115047] [drm:vc4_dsi_runtime_suspend [vc4]] *ERROR* vc4_dsi_runtime_suspend: 
[  183.176968] Preparing!
[  183.394103] [drm:vc4_dsi_runtime_resume [vc4]] *ERROR* vc4_dsi_runtime_resume: 
[  183.395897] [drm:vc4_dsi_runtime_resume [vc4]] *ERROR* vc4_dsi_runtime_resume: All good
[  183.673972] Prepared!
[  183.676465] Enabling!

The old DSI transfer errors are still there including the ones that cannot be recovered by retries.

Maybe this is relevant, but each time it's unrecoverable the sequence of retries is preceded by:

[  257.748296] vc4_dsi 3f700000.dsi: Timeout waiting for DSI ULPS entry: STAT 0x00020803

@aromanro
Copy link
Author

aromanro commented Jun 4, 2021

I commented out the vc4_dsi_ulps calls to enable and disable ulps, but still the problem (reset/reinitialization does not work, no matter how many retries) remains, so probably the above issue is just an effect, not a cause.

@6by9
Copy link
Contributor

6by9 commented Jun 4, 2021

OK, thanks for trying. DSI is an absolute pain to debug at the best of times.

STAT of 0x00020803 is
bit 0 DSI1_STAT_TXPKT1_END - txpkt1 scheduled
bit 1 DSI1_STAT_TXPKT1_DONE - txpkt1 commands complete
bit 11 DSI1_STAT_ERR_CONT_LP1 - LP1 contention error detected
bit 17 DSI1_STAT_PHY_CLOCK_HS - clock lane entered HS mode

The bits are latched though (reset by writing 1 to them), and only reset in a successful pass through vc4_dsi_ulps or vc4_dsi_runtime_resume.

Your code in https://github.com/Homegear/Homegear-Linux-Touch-Panel-08-DSI-Display-Driver/blob/master/lcd_driver.c with regard SLOW_MODE looks confusing if nothing else.
MIPI_DSI_MODE_LPM means commands are sent in low power mode. Video is always sent in high speed mode. You can just set it in dsi->mode_flags during probe and leave it alone.

Looking at it in more detail, it looks very much like an ILI9881 initialisation sequence with the bank switching. The driver for that panel already includes an initialisation table for each variant, so it seems a little odd duplicating all that information. I have an ILI9881C panel running from the Pi, although on 2 lanes. The init tables do seem to be magic runes though.
https://github.com/raspberrypi/linux/blob/rpi-5.10.y/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c

@aromanro
Copy link
Author

aromanro commented Jun 4, 2021

I'm sure it looks confusing, it went in that state while trying to workaround the problems. Initially it used slow mode only for the initialization sequence. I'll change the flag to have it set permanently on Monday.

@aromanro
Copy link
Author

aromanro commented Jul 1, 2021

We got rid of a lot of DSI errors by increasing the repeat count for short packets to 5 (currently 3 in what's committed: https://github.com/Homegear/homegear-vc4-driver/blob/e8e3608184141c379f4f11d93727081d4207a8d2/vc4_dsi.c#L1253 ).

So a combination of avoiding pm issues and this change basically solve the issues for us (very seldom they can still appear, but then the repeat loop in the panel driver solves it).

There is another issue that's maybe left, the case (out of many thousands) when after a reset initialization commands don't go through. I still have to reproduce that with the current changes.

LE: A repeat count of 3 seems close to optimal in our case, more than this does not seem to help much (could even make things worse). DSI errors still occur but their frequency drops about one order of magnitude compared with the previous version.

bigbearishappy added a commit to Seeed-Studio/seeed-linux-dtoverlays that referenced this issue Jun 28, 2023
This issue happened from the raspberrypi kernel issue
in [here](raspberrypi/linux#4323)
When it happened, we find a way to fix it with help of user code:
`DISPLAY=:0 xset s activate`
just run this command twice.the lcd will back to work nomally.
To achieve this,we crate a file system node dsi_state for user
 app to get the state of it.
PotatoMania added a commit to PotatoMania/uconsole-cm3 that referenced this issue Jan 31, 2024
Due to a potential bug(raspberrypi/linux#4323) in VC4's driver, the DSI
panel cannot be reliably initialized.

The updated patch set fix the poweroff sequence of the DSI display, but
also expose the bug to post-power-up environment.

When testing the DSI bus by turning the panel on and off, there's a 10
percent chance that error occurs when initializing the panel.

I believe Clockworkpi has recognized this issue before the first batch
of uConsole coming out, as there are big trunks of code disabled by
preprocessor directives. Their solution is to only initialize the
display once at boot.

The panel driver is updated to properly deinitialize the panel, but that
makes the bug having a chance to occur when the screen is
unblanked(recover from idle black screen).

In order to workaround it, the DSI bus error state is exposed through a
sysfs node. On RPi3 series, it's `/sys/devices/platform/soc/3f700000.dsi/3f700000.dsi.0/dsi_state`. That is the `dsi_state` file in the panel node's folder. Read it and `ok\n` means no error and `error\n` means error occurred. Userspace programs can read this file and blank/unblank the screen when needed.

There are different methods to blank/unblank the display under different
circumstances. CLI/X11/Wayland all have different ways to do that, yet
not interchangable.

I really doubt if I should push this commit to the main branch though
it's tested for months.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants