AM68A: MIPI DSI Command Mode in Linux

Part Number: AM68A
Other Parts Discussed in Thread: AM62L

Tool/software:

Hello,

We're using the AM68A on a custom board with a MIPI DSI touchscreen. We're using TI Linux from sdk v10.00. We have a custom driver for the touchscreen and it has video working. Part of our driver is sending MIPI DSI commands during initialization and de-initialization. Initially we thought they were working because the video is working. However while implementing something that requires sending an additional MIPI DSI command, I'm seeing that the MIPI DSI commands don't seem to be working. The write functions return 0, which means zero bytes were transmitted. The read commands always return errno 5, which is Device IO Error.

So basically, initially I thought our MIPI DSI commands were working because we were observing video on the screen. Turns out it was never really working and the screen has been operating in a default mode.

Could someone help me in troubleshooting this? Does the controller support MIPI DSI commands?

Thanks!

  • Hi Amandio,

    The controller is in compliance with the MIPI DSI® v1.3.1 spec and therefore supports command mode.

    We will loop in the appropriate software expert to assist with your troubleshoot.

    Thanks and regards,
    Mark

  • Hi Mark,

    Thanks for confirming the support of command mode. Looking forward to hearing from the software expert.

  • Hi Amandio,

    Which Linux display driver is in-use?

    Regards,

    Takuma

  • The panel driver is a custom driver we've written for the Winstar WF101JSYAHMNB0. The current source is below:

    #include <drm/drm_mipi_dsi.h>
    #include <drm/drm_modes.h>
    #include <drm/drm_panel.h>
    #include <linux/media-bus-format.h>
    
    #include <linux/gpio/consumer.h>
    #include <linux/delay.h>
    #include <linux/module.h>
    #include <linux/mod_devicetable.h>
    #include <linux/regulator/consumer.h>
    
    #define WINSTAR_COMMAND_LENGTH  (2U)
    
    static const u8 winstar_init_commands[][WINSTAR_COMMAND_LENGTH] =   {
                                                                            { 0xB2, 0x30 }, //  Set panel to 4 lane
    	                                                                    { 0x80, 0x5B }, //  This and the rest set gamma values.
    	                                                                    { 0x81, 0x47 },
    	                                                                    { 0x82, 0x84 },
    	                                                                    { 0x83, 0x88 },
    	                                                                    { 0x84, 0x88 },
    	                                                                    { 0x85, 0x23 },
    	                                                                    { 0x86, 0xB6 },
                                                                        };
    
    struct winstar {
    	struct drm_panel	panel;
    	struct mipi_dsi_device	*dsi;
    	struct gpio_desc	*reset;
    };
    
    static inline struct winstar *panel_to_winstar(struct drm_panel *panel)
    {
    	return container_of(panel, struct winstar, panel);
    }
    
    static int winstar_prepare(struct drm_panel *panel)
    {
    	struct winstar *ctx = panel_to_winstar(panel);
    	struct mipi_dsi_device *dsi = ctx->dsi;
    	unsigned int i;
    	int ret;
    
    	gpiod_set_value(ctx->reset, 0);
    
    	msleep(20);
    
    	gpiod_set_value(ctx->reset, 1);
    
    	msleep(30);
    
    	gpiod_set_value(ctx->reset, 0);
    
    	msleep(55);
    
    	for (i = 0; i < ARRAY_SIZE(winstar_init_commands); i++) {
    
    		ret = mipi_dsi_dcs_write_buffer(dsi, winstar_init_commands[i],
                WINSTAR_COMMAND_LENGTH);
    
    		if (ret < 0)
            {
                dev_err(panel->dev, "Failed to send MIPI command bytes: %*ph. Error: %d.\n",
                    WINSTAR_COMMAND_LENGTH, winstar_init_commands[i], ret);
                break;
            }
    	}
    
    	return ret;
    }
    
    static int winstar_enable(struct drm_panel *panel)
    {
        int ret;
    
    	struct winstar *ctx = panel_to_winstar(panel);
    
    	ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
        if(ret < 0)
        {
            dev_err(panel->dev, "Could not exit sleep mode. Error: %d.\n", ret);
            return ret;
        }
    
    	msleep(200);
    
    	ret = mipi_dsi_dcs_set_display_on(ctx->dsi);
        if(ret < 0)
        {
            dev_err(panel->dev, "Could not set display on. Error: %d.\n", ret);
            return ret;
        }
    
    	msleep(200);
    
    	return 0;
    }
    
    static int winstar_disable(struct drm_panel *panel)
    {
        int ret;
    
    	struct winstar *ctx = panel_to_winstar(panel);
    
    	ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
        if(ret < 0)
        {
            dev_err(panel->dev, "Could not set display off. Error: %d.\n", ret);
        }
    
        return ret;
    }
    
    static int winstar_unprepare(struct drm_panel *panel)
    {
    	struct winstar *ctx = panel_to_winstar(panel);
    	int ret;
    
    	ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
    	if (ret < 0)
        {
            dev_err(panel->dev, "Failed to enter sleep mode: %d\n", ret);
            return ret;
        }		
    
    	msleep(200);
    
    	gpiod_set_value(ctx->reset, 1);
    
    	return 0;
    }
    
    #define PANEL_H_FRONT_PORCH		(160)
    #define PANEL_H_SYNC			(70)
    #define PANEL_H_BACK_PORCH		(90)
    #define PANEL_V_FRONT_PORCH		(17)
    #define PANEL_V_SYNC			(15)
    #define PANEL_V_BACK_PORCH		(3)
    
    static const struct drm_display_mode winstar_default_mode = {
    	.clock		= 51206,
    
    	.hdisplay	= 1024,
    	.hsync_start	= 1024 + PANEL_H_FRONT_PORCH,
    	.hsync_end	= 1024 + PANEL_H_FRONT_PORCH + PANEL_H_SYNC,
    	.htotal		= 1024 + PANEL_H_FRONT_PORCH + PANEL_H_SYNC + PANEL_H_BACK_PORCH,
    
    	.vdisplay	= 600,
    	.vsync_start	= 600 + PANEL_V_FRONT_PORCH,
    	.vsync_end	= 600 + PANEL_V_FRONT_PORCH + PANEL_V_SYNC,
    	.vtotal		= 600 + PANEL_V_FRONT_PORCH + PANEL_V_SYNC + PANEL_V_BACK_PORCH,
    
    	.width_mm	= 235,
    	.height_mm	= 143,
    
    	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
    	.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
    };
    
    static int winstar_get_modes(struct drm_panel *panel,
    			     struct drm_connector *connector)
    {
    	struct winstar *ctx = panel_to_winstar(panel);
    	struct drm_display_mode *mode;
    
    	mode = drm_mode_duplicate(connector->dev, &winstar_default_mode);
    
    	if (!mode) {
    		dev_err(&ctx->dsi->dev, "failed to add mode %ux%u@%u\n",
    			winstar_default_mode.hdisplay,
    			winstar_default_mode.vdisplay,
    			drm_mode_vrefresh(&winstar_default_mode));
    		return -ENOMEM;
    	}
    
    	drm_mode_set_name(mode);
    
    	drm_mode_probed_add(connector, mode);
    
    	return 1;
    }
    
    static const struct drm_panel_funcs winstar_funcs = {
    	.disable = winstar_disable,
    	.unprepare = winstar_unprepare,
    	.prepare = winstar_prepare,
    	.enable = winstar_enable,
    	.get_modes = winstar_get_modes,
    };
    
    static int winstar_dsi_probe(struct mipi_dsi_device *dsi)
    {
    	struct winstar *ctx;
    	int ret;
    
    	ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
    	if (!ctx)
    		return -ENOMEM;
    
    	mipi_dsi_set_drvdata(dsi, ctx);
    	ctx->dsi = dsi;
    
    	drm_panel_init(&ctx->panel, &dsi->dev, &winstar_funcs,
    		       DRM_MODE_CONNECTOR_DSI);
    
    	ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW);
    	if (IS_ERR(ctx->reset))
    		return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
    				     "Couldn't get our reset GPIO\n");
    
    	ret = drm_panel_of_backlight(&ctx->panel);
    
    	if (ret)
    	{
    		dev_err(&dsi->dev, "Couldn't get backlight. Error: %d.\n", ret);
    		return ret;
    	}
    
    	drm_panel_add(&ctx->panel);
    
    	dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
    	dsi->format = MIPI_DSI_FMT_RGB888;
    	dsi->lanes = 4;
    
    	ret = mipi_dsi_attach(dsi);
    	if (ret < 0) {
    		dev_err(&dsi->dev, "mipi_dsi_attach failure. Error: %d.\n", ret);
    		drm_panel_remove(&ctx->panel);
    		return ret;
    	}
    
    	return 0;
    }
    
    static void winstar_dsi_remove(struct mipi_dsi_device *dsi)
    {
    	struct winstar *ctx = mipi_dsi_get_drvdata(dsi);
    
    	mipi_dsi_detach(dsi);
    	drm_panel_remove(&ctx->panel);
    }
    
    static const struct of_device_id winstar_of_match[] = {
    	{ .compatible = "winstar,wf101jsyahmnb0", },
    	{ /* sentinel */ }
    };
    MODULE_DEVICE_TABLE(of, winstar_of_match);
    
    static struct mipi_dsi_driver winstar_driver = {
    	.probe = winstar_dsi_probe,
    	.remove = winstar_dsi_remove,
    	.driver = {
    		.name = "winstar-wf101jsyahmnb0",
    		.of_match_table = winstar_of_match,
    	},
    };
    module_mipi_dsi_driver(winstar_driver);

    I've tried sending the mipi commands during the enable and prepare functions, but I get the same issues.

  • Hi Amandio,

    Double checking with internal team, but so far, it looks like DSI command mode is not supported in software. DSS block is different, but DSI block is the same as AM62L, so response will most likely be the same as this thread:  RE: SK-AM62P-LP: MIPI command mode supported by TIDSS ?  

    Regards,

    Takuma

  • Thanks for the update. Looking forward to hear back on the confirmation. 

    If it turns out that there is no support in the software, can you tell us know if that support is planned and what the timeline is?

    Otherwise, could you guide us on how to enable it ourselves?

    Thanks!!

  • Hi Amandio,

    No plans or timeline that I can share. But, I have put in a request to our SDK team to see if they can plan for it.

    To set expectations, it will not be in the upcoming 11.1 SDK, and most likely not in 11.2 SDK as well due to backlog of other planned features (i.e., it will not be implemented this year).

    Regards,

    Takuma

  • Thanks for the input. Just to be crystal clear, you have confirmed that the current Linux drivers lack support for sending & receiving MIPI DSI commands?

    If that is indeed the case, can you guide us on what has to change to support it? Reading that other post you linked, the bridge driver was mentioned. Does that mean cdns-core-dsi would need to be updated? Maybe in the cdns_dsi_transfer function?

    Thanks!

  • Hi Amandio,

    Checking with our SDK team to confirm if there is no support. However, no feedback yet. 

    Personally, looking through the driver, the cdns_dsi_transfer function looks to be the function that gets called by mipi_dsi_dcs_write_buffer

    Does this function get called when your custom driver runs, and does it complete without any errors?

    Regards,

    Takuma

  • Yes, I confirmed via printks that cdns_dsi_transfer is gets called in my panel driver when trying to send the mipi commands.

    It's not returning a negative value when writing, but it always returns 0. According to the function documentation, if it isn't returning a negative value due to error, it should return the number of bytes written. So always returning 0 I suppose could be viewed as an error in this case if it's not sending data. But if I'm understanding this correctly, the code for cdns_dsi_transfer shows that it can only return 0 and a few other error codes, not the actual number bytes sent.

    On the other hand, reading always returns -EIO.

    Thanks!

  • Hi Amandio,

    So always returning 0 I suppose could be viewed as an error in this case if it's not sending data. But if I'm understanding this correctly, the code for cdns_dsi_transfer shows that it can only return 0 and a few other error codes, not the actual number bytes sent.

    Yes, I see the same as well. Let me gather some history behind this code. I will update this thread next week, as the engineer who was working on this is in India. So due to timezone differences, the responses will be delayed by a day.

    Regards,

    Takuma

  • Sounds good. Thanks for the help!

  • Hi Amandio,

    I have gotten confirmation that DSI mode is not supported. It looks to be both a lack of support on DSI and DSS block.

    Regards,

    Takuma

  • Thanks for your response. And to confirm, support is not on the roadmap at all?