Kernel 4.14.67: tilcdc doesn't recognize my LCD at boot

Hello,

I designed a custom board based off of the OSD335x-SM. So, from the kernel perspective, the board looks a lot like the pocket beaglebone. In order to get the board to boot I did the following:

  • I reprogrammed the EEPROM so that U-Boot would boot

  • Installed the latest Beaglebone image on an SDCard

  • Configured the DTS to behave like a pocket beaglebone

  • Modified uEnv.txt to not load capes dynamically and use my DTS file

The board booted fine.

I used Kernel build repo developed by Robert C Nelson to get a build-able version of the kernel. I can now create a custom version of the kernel DTS.

Now I want to get the LCD screen working

I have an 5 Inch New Haven LCD screen attached to the board and I’ve connected only 16 of the 24-bit color in an RGB565 format.

I’ve modified my DTS file to include support for the LCD Panel following the instructions in:

The relevant portions of my DTS file are here:

`

/ {

lcd_backlight: backlight {
status = “okay”;
compatible = “gpio-backlight”;
//brightness-levels = <0 51 53 56 62 75 101 152 255>;
//default-brightness-level = <8>;
//default-brightness-level = <0>;
pinctrl-names = “default”;
pinctrl-0 = <&backlight_pin>;
gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
//default-on;
//default-off;
};

lcd0:panel {
compatible = “ti,tilcdc,panel”;
pinctrl-names = “default”;

pinctrl-0 = <&lcd_pins>;
enable-gpios = <&gpio1 20 GPIO_ACTIVE_HIGH>;
backlight = <&lcd_backlight>;
status = “okay”;

panel-info {
ac-bias = <255>;
ac-bias-intrpt = <0>;
dma-burst-sz = <16>;
bpp = <16>;
fdd = <0x80>;
sync-edge = <0>;
sync-ctrl = <1>;
raster-order = <0>;
fifo-th = <0>;
//invert-pxl-clk;
};

display-timings {
native-mode=<&timing0>;
timing0: 800x480 {
clock-frequency = <45000000>;
hactive = <800>;
vactive = <480>;

hfront-porch = <40>;
hback-porch = <40>;
hsync-len = <48>;

vfront-porch = <13>;
vback-porch = <29>;
vsync-len = <3>;

hsync-active = <0>;
vsync-active = <0>;
//de-active = <1>;
//pixelclk-active = <1>;
};
};
};
};

&lcdc {
status = “okay”;
blue-and-red-wiring = “straight”;
};

`

Results:

  • I do not see anything on the LCD screen

  • I do not see a fb0 in /dev directory

  • I have verified that the devicetree has been read correctly by looking into /proc/devicetree/ocp/lcdc@4830e000 as well as /proc/devicetree/panel/ and everything looks correct.
    Here is a dump of, what I think, is the most relevant parts of the kernel boot dmesg output (I’ve added some extra messages within /driver/gpu/tilcdc_drv.c to help me debug this:

`


[ 2.056859] [drm] Initialized vgem 1.0.0 20120112 for virtual device on minor 0
[ 2.064449] usbcore: registered new interface driver udl
[ 2.071783] OF: graph: no port node found in /ocp/lcdc@4830e000
[ 2.077961] OF: graph: no port node found in /ocp/lcdc@4830e000
[ 2.084831] tilcdc 4830e000.lcdc: Check if componentized
[ 2.090258] tilcdc 4830e000.lcdc: Not componentized
[ 2.095191] tilcdc 4830e000.lcdc: Look for remote node
[ 2.100384] OF: graph: no port node found in /ocp/lcdc@4830e000
[ 2.106356] tilcdc 4830e000.lcdc: Return 0!
[ 2.110583] tilcdc 4830e000.lcdc: no encoders/connectors found, failed to initialize

[ 2.713767] panel panel: found backlight


[ 2.745671] panel panel: found enable GPIO

`

(I can attach the full dmesg if requested)

Analysis:

It looks like the tilcdc which is the driver that, I think, should create the /dev/fb0 fails because it finds no ‘encoder/connectors’ I’m not sure what this means and I can’t find documentation on this.

Within the /Documentation/devicetree/bindings/display/tilcdc/tilcdc.txt it makes a mention of the remote port but all the examples I’ve seen only make reference to HDMI as a remote endpoint, so I do not think I need to add a remote endpoint to the &lcdc entry. I’ve tried to add a remote port to my panel but it just causes a kernel panic. It seems like the tilcdc should automatically detect the panel and create an /dev/fb0.

Is there something I’m doing wrong?
Should the tilcdc be initialized after the panel. Perhaps using something like a deferred probe?

Any help would be greatly appreciated.

Dave

In my experience, the LCD driver runs great w/ a panel attached or not- you can probe the signals. tilcdc driver should be loading and you should see an /dev/fb0.

Not sure what
`

blue-and-red-wiring = “straight”;

`is but that shouldn’t be a big deal.

I’ve checked your DT against mine, it’s basically the same. I vaguely remember that error, but I can’t remember if I got rid of it or what.

That being said, can I see your lcd_pins node (“pinctrl-0”) to make sure it’s the same as mine? Mine’ s called “bb_lcd_lcd_pins” but it’s this:

`
bb_lcd_lcd_pins: pinmux_bb_lcd_lcd_pins {
pinctrl-single,pins= <
0x0A0 (((1 << 3)) | 0)
0x0A4 (((1 << 3)) | 0)
0x0A8 (((1 << 3)) | 0)
0x0AC (((1 << 3)) | 0)
0x0B0 (((1 << 3)) | 0)
0x0B4 (((1 << 3)) | 0)
0x0B8 (((1 << 3)) | 0)
0x0BC (((1 << 3)) | 0)
0x0C0 (((1 << 3)) | 0)
0x0C4 (((1 << 3)) | 0)
0x0C8 (((1 << 3)) | 0)
0x0CC (((1 << 3)) | 0)
0x0D0 (((1 << 3)) | 0)
0x0D4 (((1 << 3)) | 0)
0x0D8 (((1 << 3)) | 0)
0x0DC (((1 << 3)) | 0)

0x0E0 (((1 << 3)) | 0)
0x0E4 (((1 << 3)) | 0)
0x0E8 (((1 << 3)) | 0)
0x0EC (((1 << 3)) | 0)

;
};
`

I also have “lcdc” in exclusive use.

Thank you for your response!

I don’t have any expansion header so I disabled the cape manager. I wonder if this effects the behaviour of the tilcdc.

I’ll check my pinmux against yours and add in the exclusive use and see if it has an effect.

Thanks again,

Dave

Thanks again for the suggestions!

I made some progress. I looked at the lcdc_drv.c file and it looks like the framebuffer inode is created in the ‘init’ function.

I’m still trying to wrap my head around this but it seems that there are three kernel modules that are loaded and they are order dependent.

  • DRM: Direct Render Manager
  • TILCDC DRM: TI’s DRM Interface
  • TILCDC Panel: The LCD Panels

The DRM is loaded first. It queries the DTS for, among other things, TILCDC DRM and TILCDC Panel. In my case it loaded the TILCDC DRM first. The TILCDC DRM needed a reference to the TILCDC Panel before it would create the fb0 inode.

Within tilcdc_drv.c line 255 this function is called which determines if there is a TILCDC Panel declared within the DTS:

`

        priv->[is_componentized](https://elixir.bootlin.com/linux/v4.14.67/ident/is_componentized) =
                [tilcdc_get_external_components](https://elixir.bootlin.com/linux/v4.14.67/ident/tilcdc_get_external_components)(dev, NULL) > 0;

`

This line does two things, it sets 'is_componentized to false and sets external_connector to true.

This is tested on line 386 of the same file, the other test priv->num_encoders and priv->num_connectors, I think, relate to things like HDMI encoders for an off chip device.

`

        if (!priv->external_connector &&
            ((priv->num_encoders == 0) || (priv->num_connectors == 0))) {
                [dev_err](https://elixir.bootlin.com/linux/v4.14.67/ident/dev_err)(dev, "no encoders/connectors found\n");
                ret = -[ENXIO](https://elixir.bootlin.com/linux/v4.14.67/ident/ENXIO);
                goto [init_failed](https://elixir.bootlin.com/linux/v4.14.67/ident/init_failed);
        }

`

In my case, this test fails and the framebuffer is not created because it seems like the TILCDC Panel was loaded after the TILCDC DRM.

I’m not sure if I did the right solution but I modified the kernel config to make TILCDC DRM an external kernel module that would be loaded after the initial boot. Perhaps there is a better way to do this. I heard of something called a deferred probe but I’m don’t know if that is applicable.

Maybe I’m doing something wrong.

I haven’t worked on this since dynamic loading of overlays were a thing, but loading the device tree I believe loaded the driver for me.

I didn’t use capemanager, I used

/sys/kernel/config/device-tree/overlays/

I thought dynamic dtbo’s were a thing of the past? Kernel just isn’t set up for that type of thing. I’m not 100%.

Other commands involved in this process were:

/usr/local/bin/config-pin overlay cape-universaln
and then I’d echo the path to the .dtbo to a path similar to the /sys/kernel/config

I’ve been trying despite sickness/work-catch-up to do a live-stream on twitch/youtube of basically this exact process with my own lightweight GUI framework (UXmux), although I don’t know exactly what systems I’ll use (spi, rgb, etc).

What commands are you using to load drivers?

I’m still new to a lot of this stuff so my knowledge isn’t 100% so if someone sees something wrong with what I’m saying please correct me.

It looks like the DRM (Direct Render Manager) is the kernel module that manages the display subsystem, at least the display through the LCD pins, it’s loaded when the kernel starts up, Here it is within dmesg:

`

[ 2.070851] serial serial0: tty port ttyS3 registered
[ 2.078095] omap_rng 48310000.rng: Random Number Generator ver. 20
[ 2.085519] [drm] Initialized vgem 1.0.0 20120112 for virtual device on minor 0
[ 2.093108] usbcore: registered new interface driver udl
[ 2.102436] at24 0-0050: 32768 byte 24c256 EEPROM, writable, 1 bytes/write

`

Because the LCD panel is specified within the DTS:

`

lcd0:panel {
status = “okay”;
compatible = “ti,tilcdc,panel”;
pinctrl-names = “default”;
pinctrl-0 = <&lcd_pins>;
//enable-gpios = <&gpio1 20 GPIO_ACTIVE_HIGH>;
enable-gpios = <&gpio1 20 0>;
backlight = <&lcd_backlight>;

panel-info {
ac-bias = <255>;
ac-bias-intrpt = <0>;
dma-burst-sz = <16>;
bpp = <16>;
/* bpp = <32>; */
fdd = <0x80>;
sync-edge = <0>;
sync-ctrl = <1>;
raster-order = <0>;
fifo-th = <0>;
invert-pxl-clk = <1>;
};

display-timings {
native-mode=<&timing0>;
timing0: 800x480 {
clock-frequency = <45000000>;
hactive = <800>;
vactive = <480>;

vsync-len = <3>;
vfront-porch = <13>;
vback-porch = <32>;

hsync-len = <48>;
hfront-porch = <40>;
hback-porch = <88>;

hsync-active = <1>;
vsync-active = <1>;
de-active = <1>;
pixelclk-active = <1>;
};
};
};

`

and the TILCDC panel driver is built within the kernel it is also loaded during boot before the filesystem starts:

`

[ 24.971896] Bluetooth: HCI UART protocol QCA registered
[ 25.054865] panel panel: found backlight
[ 25.054921] panel panel: found enable GPIO
[ 25.083796] sd 0:0:0:0: Attached scsi generic sg0 type 0

`

TI’s LCD Controller module (TILCDC) plugs into the DRM. When the TILCDC gets loaded it queries the DRM for available displays (like a panel and/or HDMI). The kernel will attempt to load TILCDC DRM driver because it is specified in the DTS under the lcdc node within the am33xx.dtsi file.

`

lcdc: lcdc@4830e000 {
compatible = “ti,am33xx-tilcdc”;
reg = <0x4830e000 0x1000>;
interrupts = <36>;
ti,hwmods = “lcdc”;
status = “disabled”;
};

`

(The ‘status’ field is overridden to ‘okay’ within my dts file)

When I built the kernel I changed the TILCDC DRM driver to an external module instead of a built in one. Now instead of the kernel loading the module while booting it must wait until the the filesystem is loaded before it can attempt to load the module.

Here is where the kernel loads the TILCDC module

`

[ 25.100373] OF: graph: no port node found in /ocp/lcdc@4830e000
[ 25.121599] Bluetooth: hci0: change remote baud rate command in firmware
[ 25.177123] usbcore: registered new interface driver cdc_ether
[ 25.196841] OF: graph: no port node found in /ocp/lcdc@4830e000
[ 25.282171] tilcdc 4830e000.lcdc: Check if componentized
[ 25.282191] tilcdc 4830e000.lcdc: Not componentized
[ 25.282199] tilcdc 4830e000.lcdc: Look for remote node
[ 25.282217] OF: graph: no port node found in /ocp/lcdc@4830e000
[ 25.347611] tilcdc 4830e000.lcdc: Return 0!
[ 25.347624] tilcdc 4830e000.lcdc: Found encoders and/or connectors
[ 25.347638] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
[ 25.347643] [drm] No driver support for vblank timestamp query.
[ 25.420388] Console: switching to colour frame buffer device 100x30
[ 25.430403] tilcdc 4830e000.lcdc: fb0: frame buffer device

`

The kernel knows what modules are in the filesystem because it reads /lib/module/uname -r/modules.* files

`

cospan@guppy-buddy:/lib/modules/4.14.67+$ ls -l
total 2168
drwxr-xr-x 11 root root 4096 Sep 9 02:18 kernel
-rw-r–r-- 1 root root 583616 Oct 11 01:31 modules.alias
-rw-r–r-- 1 root root 610839 Oct 11 01:31 modules.alias.bin
-rw-r–r-- 1 root root 16162 Oct 11 01:29 modules.builtin
-rw-r–r-- 1 root root 18391 Oct 11 01:31 modules.builtin.bin
-rw-r–r-- 1 root root 172045 Oct 11 01:31 modules.dep
-rw-r–r-- 1 root root 270086 Oct 11 01:31 modules.dep.bin
-rw-r–r-- 1 root root 214 Oct 11 01:31 modules.devname
-rw-r–r-- 1 root root 89017 Oct 11 01:29 modules.order
-rw-r–r-- 1 root root 381 Oct 11 01:31 modules.softdep
-rw-r–r-- 1 root root 192388 Oct 11 01:31 modules.symbols
-rw-r–r-- 1 root root 239831 Oct 11 01:31 modules.symbols.bin
cospan@guppy-buddy:/lib/modules/4.14.67+$

`

These files are created when you run ‘depmod’ on the command line. So the first time I installed the new kernel and modules the LCD didn’t work because I needed to log in over serial and run depmod

I didn’t load the modules explicitly because the DTS told the kernel to load the modules for me whether those modules were built in to the kernel or as separate modules within the filesystem.

Another way that this could be accomplished is by using udev and writing some rules that will tell the kernel to load the module when some condition happens or by explicitly calling modprobe on a kernel module directly.

Again, if I got something wrong let me know.

Sorry I can’t be more help at the moment, the infrastructure has changed too much since I did this about 14 months ago. I guess that’s forever-ago in embedded linux kernel develop time. I’ll likely be doing this alongside you though over the next week or two, so I’ll keep you posted if I have any luck.