-
The LCD works!
10/07/2022 at 07:41 • 4 commentsAfter finding and fixing a few silly mistakes, I finally got the LCD working:
The issues I discovered that I had to fix were:
- The registers for HFP, HBP, and Hsync pulse width are 8 bits on the sn65dsi83, but I was trying to set several of these to values above 255. This was causing them to wrap around, meaning my horizontal line frequency was different than I wanted. I had to adjust these values to be <=255.
- I recalled that someone had told me that 4 lanes of DSI doesn't seem to work with the sn65 (at least with the current version of the Linux driver). I had most recently been testing with 4 lanes, so I was getting no LVDS output. Switching to 3 lanes (and adjusting the desired LVDS clock speed downwards so that the DSI clock would be under 500MHz) got the sn65 producing LVDS signals that seemed to work.
- The LVDS connector on the other side of my board had come off. At some point I had tried swapping out the sn65 IC for a fresh one, thinking I might have burned out the LVDS outputs by shorting them to the inconveniently adjacent 1.8V supply pins. During this rework process, the solder on the LVDS connector must have gotten hot enough to melt (it is essentially right behind the sn65).
I'm fortunate that the LCD doesn't seem to mind if I use a different timing configuration than is specified in its datasheet.
Next steps:
- Merge all my various git branches into one branch (the LCD, the touchscreen controller, the RTC, and the sound card)
- Get PCBs made of the bodge board that I've designed which should let me connect the TPS61177A chip onto my board the right way around.
- Try closing up the case.
Once I do those things, this should be a functional tablet! Mostly. There's still the whole software thing to deal with. And I need to figure out how to either use the iPad's original wifi/BT chip or connect the CM4's antenna port to one of the iPad's own antennas. Also I'd like to test the NVMe slot. Oh, and I haven't tested the audio codec+speaker amplifier on this revision of the board, I think.
But still! Tangible progress!
-
Some progress on the LCD
07/04/2022 at 06:24 • 4 commentsThe iPad's LCD wants its video input data in the LVDS format, but the CM4 can't output LVDS. It can, however, output DSI video, and there are chips like the sn65dsi83-q1 from TI which can convert from DSI to LVDS.
About a year ago, I had gotten the sn65dsi83 somewhat working on the previous revision of my board. However, I ran into an issue with that board design that was difficult to work around: That board was designed to work as a hat for a full-size Pi (for ease of controlling which parts of the board were connected), so it only supported the 2 lanes of DSI that are exposed on the 15-pin display FFC on the Pi 4. This made it impossible to get clock speeds in the right ranges to make everything happy:
The LCD panel from the iPad wants an LVDS clock between 97MHz and 103MHz. The sn65dsi83 driver calculates a DSI clock speed for for a desired LVDS clock frequency based on the pixel rates required and how many lanes of DSI you're using. (In this case, it wanted a 600MHz clock, which the sn65 would then divide by 6 to get 100MHz.) The Pi's DSI driver, vc4_dsi, then rounds this requested DSI clock speed up to the nearest integer division of 1.5GHz. In the case of 2 DSI lanes, this meant the Pi was trying to send a 750MHz DSI clock, and divide by 6 to get a 125MHz LVDS clock. These clock speeds are out of spec for both the sn65 (which only supports up to 500MHz DSI clocks) and the panel (which wants 100MHz clocks).
The fix would be to use 4 lanes of DSI -- with 4 lanes, the sn65dsi83 driver would request a 300MHz clock (which the vc4_dsi driver would happily provide, as that's one fifth of 1500 MHz), which the sn65 would then divide by 3 to get the 100MHz LVDS clock. I set aside any further work on the sn65 until I had my second board revision.
Picking up where I left off
Now that I've got my CM4-based board (which has all 4 lanes of DSI routed), it was time to try getting video working again.
One of the first things I did was to skim through the updates on this massive thread on the Raspberry Pi forums, which is the de facto place to discuss using the sn65dsi83 with the Raspberry Pi. So, so many updates.
An engineer from the Raspberry Pi foundation, who goes by 6by9 on the forums, maintains a branch of the Linux kernel with patches to the sn65dsi83 driver. I pulled the latest version of their branch and merged it with the changes from my branch from last year. I double-checked the settings in the device tree overlay file (updating e.g. which GPIO pins are connected to the enable pin on the sn65 and its voltage regulator).
Before trying it out, I looked at the area of my board near the sn65 to check for short circuits and such. (This is my last copy of the chip, and they're only available from a few places, which are selling at quite a bit above MSRP.)
Satisfied that I wasn't going to fry the chip, I enabled the dtoverlay. I was greeted with this error message on boot:
[ 18.154108] sn65dsi83 10-002d: failed to attach dsi to host: -517
I used some tricks I've learned during this project to get more information out of the kernel at boot: specifically, adding a bunch of dyndbg flags to the kernel command line in /boot/cmdline.txt:
ti_sn65dsi83.dyndbg="+pmf" vc4.dyndbg="+pmf" panel_simple.dyndbg="+pmf" drm_kms_helper.dyndbg="+pmf" component.dyndbg="+pmf"
(This enables debug messages in the ti_sn65dsi83, vc4, panel_simple, ... kernel modules.)
After adding some more debug messages to those modules, I ended up with this in my boot log:
[ 17.888125] calling sn65dsi83_driver_init+0x0/0x1000 [ti_sn65dsi83] @ 366 [ 17.900948] sn65dsi83 10-002d: supply vcc not found, using dummy regulator [ 17.902829] systemd-journald[136]: Successfully sent stream file descriptor to service manager. [ 17.907747] systemd-journald[136]: Successfully sent stream file descriptor to service manager. [ 17.918315] systemd-journald[136]: Successfully sent stream file descriptor to service manager. [ 17.942603] systemd-journald[136]: Successfully sent stream file descriptor to service manager. [ 17.948459] vc4_dsi fe700000.dsi: vc4_dsi_host_attach called [ 17.948499] component:__component_add: vc4_dsi fe700000.dsi: adding component (ops vc4_dsi_ops [vc4]) [ 17.948595] component:try_to_bring_up_master: vc4-drm gpu: trying to bring up master [ 17.948612] component:find_components: vc4-drm gpu: Looking for component 0 [ 17.948629] component:find_components: vc4-drm gpu: Looking for component 1 [ 17.948645] component:find_components: vc4-drm gpu: Looking for component 2 [ 17.948661] component:find_components: vc4-drm gpu: Looking for component 3 [ 17.948889] component:find_components: vc4-drm gpu: found component fe700000.dsi, duplicate 0 [ 17.948916] component:find_components: vc4-drm gpu: Looking for component 4 [ 17.948932] component:find_components: vc4-drm gpu: found component fe004000.txp, duplicate 0 [ 17.948948] component:find_components: vc4-drm gpu: Looking for component 5 [ 17.948963] component:find_components: vc4-drm gpu: found component fe206000.pixelvalve, duplicate 0 [ 17.948978] component:find_components: vc4-drm gpu: Looking for component 6 [ 17.948993] component:find_components: vc4-drm gpu: found component fe207000.pixelvalve, duplicate 0 [ 17.949008] component:find_components: vc4-drm gpu: Looking for component 7 [ 17.949022] component:find_components: vc4-drm gpu: found component fe20a000.pixelvalve, duplicate 0 [ 17.949038] component:find_components: vc4-drm gpu: Looking for component 8 [ 17.949052] component:find_components: vc4-drm gpu: found component fe216000.pixelvalve, duplicate 0 [ 17.949067] component:find_components: vc4-drm gpu: Looking for component 9 [ 17.949082] component:find_components: vc4-drm gpu: found component fec12000.pixelvalve, duplicate 0 [ 17.949106] component:try_to_bring_up_master: vc4-drm gpu: calling master->ops->bind (ops vc4_drm_ops [vc4]) [ 17.953768] component:component_bind: vc4-drm gpu: binding fe400000.hvs (ops vc4_hvs_ops [vc4]) [ 17.954158] vc4-drm gpu: bound fe400000.hvs (ops vc4_hvs_ops [vc4]) [ 17.954247] component:component_bind: vc4-drm gpu: binding fef00700.hdmi (ops vc4_hdmi_ops [vc4]) [ 17.993251] Registered IR keymap rc-cec [ 17.994710] rc rc0: vc4 as /devices/platform/soc/fef00700.hdmi/rc/rc0 [ 18.014550] calling llc_init+0x0/0x1000 [llc] @ 396 [ 18.014615] initcall llc_init+0x0/0x1000 [llc] returned 0 after 3 usecs [ 18.056791] input: vc4 as /devices/platform/soc/fef00700.hdmi/rc/rc0/input1 [ 18.076250] uart-pl011 fe201000.serial: no DMA platform data [ 18.081757] calling vlan_proto_init+0x0/0xc4 [8021q] @ 396 [ 18.081844] 8021q: 802.1Q VLAN Support v1.8 [ 18.081935] initcall vlan_proto_init+0x0/0xc4 [8021q] returned 0 after 90 usecs [ 18.095931] calling hdmi_codec_driver_init+0x0/0x1000 [snd_soc_hdmi_codec] @ 167 [ 18.096558] initcall hdmi_codec_driver_init+0x0/0x1000 [snd_soc_hdmi_codec] returned 0 after 550 usecs [ 18.122330] systemd-journald[136]: Successfully sent stream file descriptor to service manager. [ 18.125745] systemd-journald[136]: Successfully sent stream file descriptor to service manager. [ 18.128567] systemd-journald[136]: Successfully sent stream file descriptor to service manager. [ 18.134728] systemd-journald[136]: Successfully sent stream file descriptor to service manager. [ 18.154051] vc4_dsi fe700000.dsi: vc4_dsi_host_attach got error from component_add [ 18.154088] sn65dsi83 10-002d: devm_mipi_dsi_attach got error from mipi_dsi_attach: -517 [ 18.154108] sn65dsi83 10-002d: failed to attach dsi to host: -517 [ 18.154812] initcall sn65dsi83_driver_init+0x0/0x1000 [ti_sn65dsi83] returned 0 after 57438 usecs
I notice that the -517 error is happening just after the HDMI audio is initialiing, and recall this message. Double-checking that 517 = EPROBE_DEFER, I apply the workaround from that message. This seems to help -- the driver successfully loads:
[ 18.242587] calling sn65dsi83_driver_init+0x0/0x1000 [ti_sn65dsi83] @ 426 [ 18.247840] rtc-pcf85063 1-0051: registered as rtc0 [ 18.248975] rtc-pcf85063 1-0051: setting system clock to 2022-07-05T06:55:21 UTC (1657004121) [ 18.251207] initcall pcf85063_driver_init+0x0/0x1000 [rtc_pcf85063] returned 0 after 8361 usecs [ 18.257846] sn65dsi83 10-002d: supply vcc not found, using dummy regulator [ 18.271404] vc4_dsi fe700000.dsi: vc4_dsi_host_attach called [ 18.271443] component:__component_add: vc4_dsi fe700000.dsi: adding component (ops vc4_dsi_ops [vc4]) [ 18.271537] component:try_to_bring_up_master: vc4-drm gpu: trying to bring up master [ 18.271553] component:find_components: vc4-drm gpu: Looking for component 0 [ 18.271571] component:find_components: vc4-drm gpu: Looking for component 1 [ 18.271587] component:find_components: vc4-drm gpu: Looking for component 2 [ 18.271601] component:find_components: vc4-drm gpu: Looking for component 3 [ 18.271618] component:find_components: vc4-drm gpu: found component fe700000.dsi, duplicate 0 [ 18.271635] component:find_components: vc4-drm gpu: Looking for component 4 [ 18.271650] component:find_components: vc4-drm gpu: found component fe004000.txp, duplicate 0 [ 18.271665] component:find_components: vc4-drm gpu: Looking for component 5 [ 18.271680] component:find_components: vc4-drm gpu: found component fe206000.pixelvalve, duplicate 0 [ 18.271696] component:find_components: vc4-drm gpu: Looking for component 6 [ 18.271711] component:find_components: vc4-drm gpu: found component fe207000.pixelvalve, duplicate 0 [ 18.271726] component:find_components: vc4-drm gpu: Looking for component 7 [ 18.271740] component:find_components: vc4-drm gpu: found component fe20a000.pixelvalve, duplicate 0 [ 18.271756] component:find_components: vc4-drm gpu: Looking for component 8 [ 18.271771] component:find_components: vc4-drm gpu: found component fe216000.pixelvalve, duplicate 0 [ 18.271786] component:find_components: vc4-drm gpu: Looking for component 9 [ 18.271801] component:find_components: vc4-drm gpu: found component fec12000.pixelvalve, duplicate 0 [ 18.271820] component:try_to_bring_up_master: vc4-drm gpu: calling master->ops->bind (ops vc4_drm_ops [vc4]) [ 18.273963] component:component_bind: vc4-drm gpu: binding fe400000.hvs (ops vc4_hvs_ops [vc4]) [ 18.274341] vc4-drm gpu: bound fe400000.hvs (ops vc4_hvs_ops [vc4]) [ 18.274429] component:component_bind: vc4-drm gpu: binding fef00700.hdmi (ops vc4_hdmi_ops [vc4]) [ 18.279785] Registered IR keymap rc-cec [ 18.280394] rc rc0: vc4 as /devices/platform/soc/fef00700.hdmi/rc/rc0 [ 18.281000] input: vc4 as /devices/platform/soc/fef00700.hdmi/rc/rc0/input1 [ 18.282706] vc4_hdmi fef00700.hdmi: 'dmas' DT property is missing or empty, no HDMI audio [ 18.282773] vc4-drm gpu: bound fef00700.hdmi (ops vc4_hdmi_ops [vc4]) [ 18.282933] component:component_bind: vc4-drm gpu: binding fef05700.hdmi (ops vc4_hdmi_ops [vc4]) [ 18.300472] Registered IR keymap rc-cec [ 18.301060] rc rc1: vc4 as /devices/platform/soc/fef05700.hdmi/rc/rc1 [ 18.301764] input: vc4 as /devices/platform/soc/fef05700.hdmi/rc/rc1/input2 [ 18.306737] vc4_hdmi fef05700.hdmi: 'dmas' DT property is missing or empty, no HDMI audio [ 18.306924] vc4-drm gpu: bound fef05700.hdmi (ops vc4_hdmi_ops [vc4]) [ 18.307033] component:component_bind: vc4-drm gpu: binding fe700000.dsi (ops vc4_dsi_ops [vc4]) [ 18.308798] vc4-drm gpu: bound fe700000.dsi (ops vc4_dsi_ops [vc4]) [ 18.308905] component:component_bind: vc4-drm gpu: binding fe004000.txp (ops vc4_txp_ops [vc4]) [ 18.309281] vc4-drm gpu: bound fe004000.txp (ops vc4_txp_ops [vc4]) [ 18.309366] component:component_bind: vc4-drm gpu: binding fe206000.pixelvalve (ops vc4_crtc_ops [vc4]) [ 18.309691] vc4-drm gpu: bound fe206000.pixelvalve (ops vc4_crtc_ops [vc4]) [ 18.309774] component:component_bind: vc4-drm gpu: binding fe207000.pixelvalve (ops vc4_crtc_ops [vc4]) [ 18.310074] vc4-drm gpu: bound fe207000.pixelvalve (ops vc4_crtc_ops [vc4]) [ 18.310157] component:component_bind: vc4-drm gpu: binding fe20a000.pixelvalve (ops vc4_crtc_ops [vc4]) [ 18.310449] vc4-drm gpu: bound fe20a000.pixelvalve (ops vc4_crtc_ops [vc4]) [ 18.310528] component:component_bind: vc4-drm gpu: binding fe216000.pixelvalve (ops vc4_crtc_ops [vc4]) [ 18.310772] vc4-drm gpu: bound fe216000.pixelvalve (ops vc4_crtc_ops [vc4]) [ 18.312594] component:component_bind: vc4-drm gpu: binding fec12000.pixelvalve (ops vc4_crtc_ops [vc4]) [ 18.312989] vc4-drm gpu: bound fec12000.pixelvalve (ops vc4_crtc_ops [vc4]) [ 18.330091] [drm] Initialized vc4 0.0.0 20140616 for gpu on minor 1 [ 18.340221] brcmfmac: brcmf_cfg80211_set_power_mgmt: power save enabled [ 18.497222] sn65dsi83 10-002d: failed to lock PLL, ret=-110 [ 18.516260] Console: switching to colour frame buffer device 128x48 [ 18.537765] vc4-drm gpu: [drm] fb0: vc4drmfb frame buffer device [ 18.573145] initcall sn65dsi83_driver_init+0x0/0x1000 [ti_sn65dsi83] returned 0 after 322753 usecs
That line "[ 18.516260] Console: switching to colour frame buffer device 128x48" is encouraging! Ok, how do I test this now?
Searching around, I found https://github.com/richinfante/fbutils, which is a collection of tools for displaying images from the Linux framebuffer. (Graphics without X11 or Wayland!) Unfortunately, nothing seemed to be working - the LCD screen is just black.
Giving up on the framebuffer for a bit, I then installed the Raspberry Pi desktop with sudo apt install raspberrypi-ui-mods
This doesn't seem to help, either -- the LCD is still just black. Then I realize I forgot to plug in the LVDS cable 🤦
I plug in the LVDS cable, and.... still nothing!
Reading over my kernel boot logs again, I notice an error message from the sn65dsi83 driver:
[ 18.497222] sn65dsi83 10-002d: failed to lock PLL, ret=-110
Am I on the right branch?
I remembered that 6by9 actually had three branches relating to the sn65: rpi-5.15.y-dsi, rpi-5.15.y-sn65dsi8x, and rpi-5.15.y-sn65dsi83. The commits on the -sn65dsi8x branch were the most recent, so I had gone with that at first, but figured I should try the other two branches as well.
The -dsi branch was unable to start X -- it would give me this error:
Fatal server error: (EE) no screens found(EE)
Looking at the boot logs, I noticed this problem:
[ 17.307990] component:try_to_bring_up_master: vc4-drm gpu: master has incomplete components
Moving on to the third branch (rpi-5.15.y-sn65dsi83) is much more successful, and xrandr shows the right resolution:
meatmanek@cm4:~ $ DISPLAY=:0 xrandr Screen 0: minimum 320 x 200, current 1024 x 768, maximum 7680 x 7680 HDMI-1 disconnected primary (normal left inverted right x axis y axis) HDMI-2 disconnected (normal left inverted right x axis y axis) LVDS-1 connected 1024x768+0+0 (normal left inverted right x axis y axis) 0mm x 0mm 1024x768 59.98*+ meatmanek@cm4:~ $ sudo i2cget -f -y 10 0x2d 0xe1 0x00
But why is my screen still black?
Probing one of the DSI signal lines while running xeyes, I could see that there's definitely video signal coming through.
The oscilloscope in this video has its trigger hold-off configured so that each trace the oscilloscope displays is slightly later into the next frame of video, so the effect is that we slowly scan down the image. If you watch carefully, you can notice the eyes' pupils in the signal on the oscilloscope.
And yet, still, no image was showing up on the LCD.
I started reading through the configuration registers on the sn65 to see if anything stood out that could explain why nothing was showing up. Suddenly, the sn65 stopped responding entirely on i2c.
A detour while we fix some basic stuff
Breaking out the trusty logic analyzer, I discovered that the enable pin was being weird - it's just hovering at 1.5V with some weird noise on it, instead of being at the 1.8V that the NTB0102 logic level converter is supposed to drive it to. Also, the 3.3V side is being weird, too -- it's supposed to be 0V or 3.3V, not have weird voltage shifts.
I wonder if the NTB0102 is improperly soldered or something. After giving it a bit more solder and wicking away the excess, things look better:
Once I have this fixed, I learn from the sn65's error register that it's not able to lock its PLL to the DSI clock signal. Looking at the DSI clock with my oscilloscope, I see no clock, just a DC offset and a bit of noise.
It took me way longer than I'd like to admit to realize that this was caused by even more bad soldering:
With the number of pins disconnected here, it's actually lucky (or unlucky?) that the DSI data lane I chose to probe at first happened to have signal.
Stuck with no LVDS signal
After fixing the soldering, I'm currently stuck with the following situation:
- The Raspberry Pi seems to be outputting DSI correctly, and I can see signal arriving at all DSI pins of the sn65
- The sn65 doesn't complain about any errors in its error register, including any clock errors.
- The sn65 doesn't output any LVDS signal unless I enable its test pattern.
You had one job, sn65dsi83! Convert my DSI to LVDS!
My current best hypothesis is that my problem has to do with the DSI data not having a horizontal blanking interval that is as long as the LVDS data's blanking interval should be: it seems like the Pi is producing a new horizontal line every 13.16 microseconds, while the LVDS signal should produce a new line every 20.84 microseconds.
My "backlight"
I should note that because of the backlight controller issues documented in a previous log entry, my board can't currently drive the backlight built in to the LCD. (I tried hand-soldering the backlight controller chip upside-down but it still doesn't work. I'm planning to make a little bodge PCB that I can solder down, which has pads on the bottom that would connect to my board as well as pads on the top that would connect to the TPS61177 in the right orientation.)
But it turns out, the white reflector used for the backlight isn't 100% opaque, so if you shine enough light in the back, you can see it through the LCD, at least directly underneath where you shine it.
At this point I've been trying to get video working for about 3 weeks, so I figured I should publish a log entry for what I've been up to, even if it hasn't been successful. Hopefully I'll have a better update soon!
-
Getting the GT9110 touchscreen controller working
07/04/2022 at 02:49 • 4 commentsI've been testing out the GT9110 for the past few days, and finally got it working today.
My board uses a Goodix GT9110 touchscreen controller, which is closely related to a few other parts (GT911, GT928, etc.), and is basically the only touchscreen controller that I could find which met my requirements:
- Has enough drive and sense pins to work with my touchscreen
- A reasonable footprint which doesn't require expensive, high-density PCBs (which excludes the mXT2952, an 0.5mm-pitch BGA)
- Available in small quantities for a reasonable price (which excludes the MXT2912TD-A)
- Actual datasheets are available (which excludes a bunch of Cypress chips)
- Linux drivers are available
(I should note that I haven't found an English datasheet for the GT9110, only one in Chinese. However, I did find English datasheets for related chips, like the GT911 and GT9113. These, plus Google Translate, were enough to lay out my PCB.)
On my previous iteration of the board, the i2c communication to the GT9110 was unreliable -- it wouldn't respond on i2c until a little while after the INT pin went high, which made the Linux goodix driver unhappy. I had guessed that might've been because the bodge I had to do for the INT pin wasn't making a good connection.
Initial progress
I dug up the device tree overlay file I had written for testing, and updated the GPIO numbers to match my new board revision.
Fortunately, the new board doesn't have the same communications issues from the old board -- the Linux driver was able to talk to the GT9110 successfully, and create a file under /dev/input/event*.
Getting stuck
However, beyond initializing the driver, nothing else would happen. The INT pin would go high during driver initialization, and wouldn't change when I touched the screen. No further i2c communication was happening. Looking at the drive pins with an oscilloscope, I could see nothing was happening.
An error immediately stood out to me:
[ 67.112771] Goodix-TS 1-005d: Direct firmware load for goodix_9110_cfg.bin failed with error -2
I searched the web for a goodix_9110_cfg.bin, but couldn't find any example files. I did, however, find a few relevant threads:
- https://forums.raspberrypi.com/viewtopic.php?t=267434
- https://forum.khadas.com/t/connect-goodix-touchscreen-to-vim3/13955
- https://forums.raspberrypi.com/viewtopic.php?t=240001
Nobody seems to mention needing a goodix_9110_cfg.bin (or goodix_911_cfg.bin, goodix_928_cfg.bin, or whatever); everybody managed to get their touchscreens working after a while by fixing basic issues with their device tree overlay files.
Thinking I might have the wrong settings for my interrupt pin, I tried various combinations of pull-up/down/hi-Z, interrupt configuration (IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_LEVEL_LOW). None of these seemed to help - several of those combinations prevented i2c communications entirely.
Eventually, I gave up on that and focused on the firmware load error again. Adding a bunch of debug printfs to the driver and watching i2c communication with my logic analyzer (I'm using the Logic 8 from Saleae), I could see that it was:
- Identifying the GT9110 on i2c
- Trying to load the firmware from disk, and complaining that it isn't there. (The internet tells me it's looking in /lib/firmware, among other places)
- Reading a bunch of data from the GT9110 starting at register 0x8047, which is the beginning of the configuration registers.
- Leaving the INT pin high, never signalling that it had data.
If I'm understanding this correctly, this means all of the configuration registers were set to 0. Unlike a lot of other chips, it seems like the GT9110 has no default values. I probably want to set those to nonzero values.
From my reading of the driver source code, the only way to change the configuration is through a firmware file, so I guess I really need to find or write one.
Breakthrough
I eventually stumbled on this Stack Overflow question, which pointed me to this Arduino library for interfacing with gt9x controllers, where they've defined a constant called g911xFW which is 186 bytes long. This length happens to match the GOODIX_CONFIG_911_LENGTH constant defined in the Linux driver code.
(The Stack Overflow question also had a link to the GT911 Programming Guide, which is handy.)
I wrote these bytes to a file, copied it over to my CM4 and put it in /lib/firmware/goodix_9110_cfg.bin, and tried loading my driver again. This time, the i2c and interrupt lines were extremely active:
Zooming in, we can tell that immediately after the INT pin goes low, the driver is requesting several registers, starting with 0x814E.
This includes registers 0x8150 through 0x8153, which are the X and Y coordinates of touch 1. This is good! Let's find out if the data is meaningful.
Visualizing the touch data
I wrote a little script to visualize the touch data from /dev/input/eventN using ncurses. Running this with the GT9110 using the configuration we just found, we see that we just get garbage data:
I ended up reformatting the configuration data into something a little more human-readable, with comments explaining which register each value belongs to. A few things stood out:
- The X and Y dimensions (registers 0x8048 to 0x804b) were set to 720x1280, whereas I need 768x1024
- The Sensor_CH0...Sensor_CH13 registers (0x80b7 to 0x80c4) were set to 0x02, 0x04, ..., 0x1C. Similarly, the Driver_CH0 through Driver_CH24 channels were all set to various numbers between 0x02 and 0x28. I'm surprised these didn't just start at 0x01 and go up from there.
- The registers between Sensor_CH13 and Driver_CH0 (0x80c5 through 0x80d4) were all set to 0x00, as were the registers after Driver_CH24 and before Config_Chksum (0x80ee through 0x80fe). These are all marked as "Reserved" in the GT911 programming guide, but I'm guessing these would be Sensor_CH14 to Sensor_CH29 and Driver_CH25 to Driver_CH41 in the GT9110. (There are exactly the right number of reserved registers here for that to make sense).
I tried taking a stab at modifying the configuration data:
- bumping the very first register, config version (supposedly the GT9110 will refuse to take new configuration if this is less than the current version register)
- changing the resolution
- Setting Sensor_CH0...Sensor_CH29 to 0x01..0x1E
- Setting Driver_CH0..Driver_CH39 to 0x01..0x28 (I don't need to set Driver_CH40 or CH41, since my board only uses 40 drive pins)
I wrote a script that converts my human-readable format to a .bin file by stripping out comments and serializing just the values. It also calculates the checksum according to the algorithm from the Linux driver.
Trying out my modified configuration resulted in the INT-line-stays-high behavior again:
Hmm.
Eventually, I stumbled on this comment from nik-sharky, the author of that Arduino library, who had posted the configuration of their GT9110-based tablet. Trying this out leads to much better results!
I'm not sure exactly what caused the problem, but this configuration starts Sensor_CH0 at 0x00 instead of 0x01. Maybe setting one of these sensor pins to an invalid value causes the GT9110 to crash or something?
I was able to tweak this file with my script, changing resolution and disabling the 2 unneeded driver channels, and things still work.
Conclusion
Getting a GT9110 or other GT9x chip working on a custom PCB seems like it's a lot harder than getting it working on an existing board, since you need to come up with a configuration. An existing board would presumably already be configured, so you just need to hook up i2c and the reset/interrupt pins, and get a working device tree entry. (Once you've configured the chip, it seems like it stores the configuration in non-volatile memory so you don't need to reconfigure it again later. This would explain why nobody else needed to mess with goodix_9110_cfg.bin.)
Having a USB logic analyzer / mixed signal device like the Saleae is super handy for debugging embedded problems like this. While it's possible to watch i2c traffic with Linux debugging tools, it's very nice to be able to visually see the state of the interrupt pin and the i2c communication. Having the ability to copy-paste is super nice, too -- something that would be difficult with a standalone oscilloscope.
-
Real-time clock: It's nice when something is easy.
06/29/2022 at 07:18 • 0 commentsAt the very last minute before ordering my PCBs, I realized I probably wanted a real-time clock on the board somewhere. I searched on Digi-Key, found the RV-8263-C7, which was:
- in stock (not anymore, lol)
- cheap
- tiny
- requires minimal external components (just a decoupling cap and a pull-up resistor on an output enable pin)
- has a Linux kernel driver (this chip has the same protocol as the PCF85063)
I found an empty spot on my board with I2C and VBAT lines nearby, and laid it out in a few minutes.
Tonight, I decided to see if it was working:
Throw together a quick dtoverlay file:
/dts-v1/; /plugin/; / { compatible = "brcm,bcm2835"; fragment@0 { target = <&i2c1>; __overlay__ { #address-cells = <1>; #size-cells = <0>; status = "okay"; rv8263: rtc@51 { compatible = "microcrystal,rv8263"; reg = <0x51>; // quartz-load-femtofarads = <12500>; }; }; }; };
(This is basically just device tree overlay boilerplate plus the example from the documentation.)
Compile with make -j4 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs, copy the output dtbo file to the CM4's /boot/overlays directory, and run dtoverlay pipad-rtc.dtbo to load the overlay. Test with sudo hwclock --systohc then sudo hwclock --get. Add dtoverlay=pipad-rtc to /boot/config.txt, reboot, check sudo hwclock --get again, and it's still working.
Remove the fake-hwclock package and disable the systemd unit with the instructions on https://pimylifeup.com/raspberry-pi-rtc/, and double-check that it's actually reading the hardware clock on startup (and not just using NTP) by using rfkill block wifi to turn off wifi, reboot, and check that the time is accurate.
No incorrect PCB footprints or unwired pins, no Linux kernel debugging. Just things working correctly on the first try. Feels good.
-
Figuring out why the CM4 wouldn't boot when attached to my board
06/26/2022 at 08:27 • 1 commentIn my previous log entry, I had fried my CM4 by shorting 5V to 3.3V, so I had to order a replacement, which arrived last week.
The next thing for me to test is whether I can boot the CM4 on my board, so I installed Raspberry Pi OS and set enable_uart=1 in /boot/config.txt. With the CM4 still in the CM4IO, I verified that I could see boot messages and log into the CM4 over serial.
I bodged some wires onto the UART traces on my board and connected them to my USB-serial adapter. I moved the CM4 off the CM4IO and onto my own board, and booted it, and.... nothing. Zero output from the UART.
I figured it must be crashing somewhere during the bootloader or early kernel boot settings, so I moved the CM4 back to the CM4IO and:
- added console=ttyS0,115200n8 loglevel=7 earlycon=uart8250,mmio32,0xfe215040 to my /boot/cmdline.txt, to get early kernel messages.
- set BOOT_UART=1 in boot.conf to get output from the bootloader.
Still nothing...
After a while, I figured out that the TX pin on my 100-pin connector wasn't properly soldered to my board. Of all the pins to have issues, it had to be that pin!
At this point, it still isn't booting, but I can at least see kernel messages. I would get a warning at some point during boot:
[ 3.178888] ------------[ cut here ]------------ [ 3.183639] Firmware transaction timeout [ 3.183693] WARNING: CPU: 2 PID: 1 at drivers/firmware/raspberrypi.c:67 rpi_firmware_property_list+0x1dc/0x250 [ 3.197794] Modules linked in: [ 3.200888] CPU: 2 PID: 1 Comm: swapper/0 Not tainted 5.15.32-v8+ #1538 [ 3.207583] Hardware name: Raspberry Pi Compute Module 4 Rev 1.1 (DT) [ 3.214098] pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 3.221144] pc : rpi_firmware_property_list+0x1dc/0x250 [ 3.226432] lr : rpi_firmware_property_list+0x1dc/0x250 [ 3.231719] sp : ffffffc00960b9b0 [ 3.235068] x29: ffffffc00960b9b0 x28: 0000000000000000 x27: 0000000000000004 [ 3.242300] x26: ffffffc009a5d008 x25: ffffff8002aa8b80 x24: 0000000000001000 [ 3.249530] x23: 0000000000000014 x22: ffffffc008e8a9c8 x21: ffffff80020b7200 [ 3.256758] x20: ffffffc009a5d000 x19: ffffffc009491158 x18: 0000000000000010 [ 3.263986] x17: 0000000000000001 x16: 0000000000000019 x15: 0720072007200720 [ 3.271214] x14: 0720072d072d072d x13: 74756f656d697420 x12: ffffffc0093a6670 [ 3.278442] x11: 0000000000000003 x10: ffffffc00938e630 x9 : ffffffc0080ec768 [ 3.285670] x8 : 0000000000017fe8 x7 : c0000000ffffefff x6 : 0000000000000001 [ 3.292899] x5 : 0000000000057fa8 x4 : 0000000000000000 x3 : 0000000000000001 [ 3.300126] x2 : 0000000000000000 x1 : 529515f3f0203b00 x0 : 0000000000000000 [ 3.307354] Call trace: [ 3.309824] rpi_firmware_property_list+0x1dc/0x250 [ 3.314760] rpi_firmware_property+0x78/0x110 [ 3.319169] rpi_domain_on+0x6c/0x90 [ 3.322789] genpd_power_on.part.23+0x188/0x220 [ 3.327375] __genpd_dev_pm_attach+0x104/0x238 [ 3.331870] genpd_dev_pm_attach+0x6c/0x88 [ 3.336014] dev_pm_domain_attach+0x28/0x40 [ 3.340249] platform_probe+0x58/0xe0 [ 3.343957] really_probe+0xc0/0x318 [ 3.347573] __driver_probe_device+0x80/0xe8 [ 3.351892] driver_probe_device+0x88/0x118 [ 3.356123] __driver_attach+0x78/0x110 [ 3.360004] bus_for_each_dev+0x7c/0xd0 [ 3.363890] driver_attach+0x2c/0x38 [ 3.367505] bus_add_driver+0x194/0x1f8 [ 3.371385] driver_register+0x6c/0x128 [ 3.375265] __platform_driver_register+0x30/0x40 [ 3.380026] xhci_plat_init+0x38/0x44 [ 3.383737] do_one_initcall+0x54/0x2a0 [ 3.387619] kernel_init_freeable+0x250/0x2d8 [ 3.392031] kernel_init+0x2c/0x130 [ 3.395564] ret_from_fork+0x10/0x20 [ 3.399186] ---[ end trace b159b7c67bd50e8b ]---
Then after a few more messages, it would hang.
From what I can tell from this issue where people saw the same "Firmware transaction timeout" warning, this happens when the code running on the VC4 locks up.
Getting more output
The internet told me to add initcall_debug to my kernel command line to get more output on startup. This prints a message per kernel subsystem on boot.
I could tell from this that the Firmware transaction timeout was happening within xhci_plat_init.
[ 7.367700] calling xhci_plat_init+0x0/0x44 @ 1 [ 8.405222] ------------[ cut here ]------------ [ 8.409920] Firmware transaction timeout, chan 8 data def00000 ... [ 8.632151] initcall xhci_plat_init+0x0/0x44 returned 0 after 1230233 usecs
(XHCI has to do with USB, but nothing made sense yet. USB seems to be working -- I am able to use usbboot to mount the CM4's eMMC storage on another computer.)
I could also see that the boot was hanging in gpio_led_driver_init, because I saw this message:
[ 17.890149] calling gpio_led_driver_init+0x0/0x30 @ 1
with no corresponding line telling us that it had finished, e.g.:
initcall gpio_led_driver_init+0x0/0x30 returned X after Y usecs
Even more output
I spent a bit of time staring at RPi-related kernel code: the rpi_firmware_transaction function (where the timeout is happening) seems to use an "mbox" to communicate with the Pi's firmware. mbox is some generic code in the kernel, but each platform has its own implementation. The CM4's mbox is implemented in bcm2835-mailbox.c.
I noticed that bcm-2835-mailbox.c has a few dev_dbg() calls, which would print debug messages. To get these to print, you need to use the kernel's dynamic debug functionality: Per this stackoverflow answer, the kernel needs to be compiled with CONFIG_DYNAMIC_DEBUG=y. Then, you can add something like dyndbg="file bcm2835-mailbox.c +p" to your kernel command line. This will enable all dev_dbg() calls in that file.
Doing that, I saw this pattern of Request 0xDED00008 followed by Reply 0xDED00008:
[ 6.626527] [1] bcm2835_mailbox:bcm2835_send_data:90: bcm2835-mbox fe00b880.mailbox: Request 0xDEF00008 [ 6.626537] bcm2835_mailbox:bcm2835_mbox_irq:77: bcm2835-mbox fe00b880.mailbox: Reply 0xDEF00008
This stops around the time of that Firmware transaction timeout message:
[ 7.372996] [1] bcm2835_mailbox:bcm2835_send_data:90: bcm2835-mbox fe00b880.mailbox: Request 0xDEF00008 [ 8.405222] ------------[ cut here ]------------ [ 8.409920] Firmware transaction timeout, chan 8 data def00000
From this point on, all Requests have no corresponding Reply.
This seems to confirm that the firmware was locking up during the xhci_plat_init call (as opposed to locking up prior to that call, and xhci_plat_init is just the first thing to notice)
Success!
I remembered that there's an entry in config.txt regarding USB behavior:
... [cm4] # Enable host mode on the 2711 built-in XHCI USB controller. # This line should be removed if the legacy DWC2 controller is required # (e.g. for USB device mode) or if USB support is not required. otg_mode=1 ...
After commenting out the otg_mode line, my CM4 actually boots while attached to my board!
Next step: figure out why wifi isn't working, so that I can use SSH instead of serial.
-
CM4 carrier board: chip shortage pain, bodges, silly mistakes, and tragedy.
06/14/2022 at 07:56 • 1 commentChip shortage problems
Shortly after my last project log, I tried to place an order for all the new components that I'd need for the next revision of my board, as well as re-stocking anything that I had already used most of. I discovered that my backlight controller of choice had gone out of stock with indefinite lead times, so I had to swap to a different backlight driver. (I switched from the TPS61176 to the TPS61177, which was the next cheapest suitable controller on DigiKey.)
I ordered my parts then modified my PCB design to accommodate the new chip.
Fit Testing
Once I had parts in hand, I made a mockup of my board by printing the front and back solder mask layers on separate sheets of paper, and then gluing them to either side of a piece of cardboard taken from a soda can box. (This stackup comes remarkably close to the 0.8mm thickness of my PCB.) With some double-sided tape, I affixed the connectors for the largest components -- the CM4 and the NVMe SSD -- snapped the CM4 and SSD in place, and did a fit test in my iPad case. The screen did not want to drop into place.
I found a few things that I could change to improve the fit:
- I rotated the CM4 180 degrees, so that the BCM2711 processor (the thickest part of the CM4) is closer to the center of the case (where the iPad is thicker, so there's more room)
- I moved almost all of the other components from the front to the back of the board, in between the SSD and the CM4, leaving only the connectors (dock, ambient light sensor, touchscreen, buttons) and the touchscreen controller and audio codec. (The touchscreen and audio bits are on the other end of the board, and I didn't seem to have any trouble fitting that end.) This meant the battery charger, 3.3v buck-boost (for the SSD), 5V boost (for the CM4), the DSI-LVDS converter, and the backlight driver all moved to the back.
I also needed to adjust the edge of the board to leave a spot where the LVDS cable can come up from the back of the case to the front of the board.
All these changes required re-routing a significant chunk of the board, which took me a few weeks.
Touchscreen controller testing
Meanwhile, I decided to test the GT9113/GT9110 touchscreen controller on the previous iteration of my board. I had messed up the orientation of the FFC connectors for the touchscreen on that revision, but I figured I should at least test whether I can get the Linux kernel to talk to it. I'm glad I did -- I discovered a few things that were wrong with my PCB.
First of all, the Linux kernel driver wants the INT pin from the controller to be wired to a GPIO pin. I hadn't connected that pin anywhere, and didn't leave myself any extra space on the pad to solder a wire to. I tried several things to bodge a connection to this wire before finding something that worked:
- poking an enameled wire at the edge of the chip and trying to get it to solder in place: the pins on the chip are visible on the side, but even my finest soldering iron tip is enormous compared to the 0.4mm between adjacent pins.
- removing the IC, soldering a 40AWG wire to the pad, and soldering the IC back down. It turns out, even 40ga wire is thick enough to keep the rest of the pads from reliably making contact when soldering. I still feel like I could've gotten this to work by adding more solder, but it was just too finicky, so I got frustrated and tried another approach:
- (what ultimately ended up working, mostly?) I took a standard 0.1" header pin, and used a file on 3 sides to sharpen it to a point that's flush with the 4th side. I then glued this in place with some UV-cure solder mask, which is my new favorite thing for bodges. Assuming you use a thin layer, it cures in like 10 seconds under the provided UV flashlight. This is what's shown in the photo above.
Another thing that I discovered was that I couldn't get the GT9113 to be discoverable on i2c at all. (The GT9113 has nearly identical pinout to the GT9110, but is meant to be used with a GTM802 companion chip, which I think is basically just a microcontroller.) I figured these chips would be basically identical, but maybe they're more different than their pinouts suggest.
After swapping the 9113 for the 9110, I was able to talk to it over i2c. However, I had a strange problem: the GT9110 wouldn't respond on the I2C bus for the first few requests made by the Linux driver. After that, it would respond fine. I'm not really sure what was happening. Maybe my GT9110 is bad? Maybe my bodged connection is marginal, so I've effectively made an RC circuit on the INT pin?
In any case, I'm pretty sure I'd be able to work around it with driver-level hacks, so I incorporated these changes into my PCB design:
- INT pin wired to a CM4 GPIO pin.
- Drop the traces for the GT9113's ARM_RST and Wake pins, since I'm committing to the GT9110.
Fit testing again
When I finally finished all of those routing changes, I made a new mockup using the same cardboard+printer paper technique as before. This almost fit, but not without some pressure holding the screen down (and flexing it slightly 😬). I discovered that the back of the screen was pressing against the button connector, and that I could make everything fit cleanly by moving that connector to the back.
With J4 on the back, it seems like everything would fit cleanly - the screen closed nicely, without requiring any pressure.
I rerouted the traces for the button connector (and moved some other traces from the back to the front to make room), added a bajillion tiny test points (0.25mm x 1mm) to the board - essentially replacing every NC flag in my schematic with a test point.
Ordering, assembly, and another fit check
At some point, I finally felt confident ordering my PCBs. (I also threw in boards for a quick side-side project). They arrived about a week ago, and I soldered together my prototype over the course of two nights:
Essentially, I mirrored the footprint for the TPS61177. I have attempted to fix this by flipping the chip upside-down and painstakingly bodging wires to nearby components and traces:
(I also replaced the inductor and IC with fresh components.) When applying power, this no longer tries to draw 9 amps. Will it actually work? I won't be able to test this until I can get something (preferably the CM4) configuring it over i2c and giving it PWM input.
Can this actually run the CM4?
After verifying that I have what seems to be a stable 5V rail, and nothing else trying to catch fire, I enabled serial console on my CM4 while it was still on the CM4IO, tested that it worked with a USB-TTL adapter I had lying around, then anxiously moved the CM4 over to my board.
Applying power, I get nothing on the serial port. 5V and 3.3V rails seem stable...
I soldered an LED and resistor between 3.3V and the activity LED pads (I need to adjust my schematic for the activity LED -- I had just put the LED between the activity pin and ground, but it's an active-low, open collector pin.).
The activity LED flashes a few times -- when looking at the voltage on it with my logic analyzer, it seems like there's actually a ton of activity at first; but still, no output on the serial port.
I moved the CM4 back to the CM4IO to verify that serial output was still working there, and at one point I used my multimeter to probe the GPIO pins to verify that I had the GPIO pins in the correct orientation. While probing the 5V pin, my probe slipped, and shorted 5V to 3.3V! The CM4 is now dead 😢.
I have ordered a new CM4 from Welectron, a German distributor, since that was the only place that had any CM4s in stock according to rpilocator. One silly slip of a multimeter probe has cost me 64 EUR and a week or two of waiting for the replacement to arrive.
(The internet suggests I might have just damaged the PMIC on the CM4, and that I might be able to repair it by replacing the MXL7704 chip, so I'll be ordering some of those from AliExpress soon, but those will likely take longer than the replacement CM4 to arrive. EDIT: this source claims theMXL7704-P4 on the CM4 is different than the -R4, and isn't compatible: https://forums.raspberrypi.com/viewtopic.php?t=322613)
-
RPi CM4 and Radxa CM3 versions of the board
01/26/2022 at 07:53 • 2 commentsSince the last time I had a board made, I've discovered several revisions that I needed to make:
- The 5V boost of the bq25895 cuts out long enough to shut off the Pi when you connect USB power.
- I needed extra lanes of DSI going from the CM4 to the SN65DSI83 chip in order for all the clock rates to fall within the ranges allowed by the SN65 and the iPad's display.
- The footprints for my touchscreen FFCs were backwards
I took this opportunity to design a version of the board where I actually used the CM4 instead of having separate 40-pin connector / FFC for DSI / USB port on the board. I threw an M.2 slot on there for NVMe support, as well.
The CM4-based version of my board. When I was nearly finished with that design, I learned about the Radxa CM3, which is the same form factor as the Raspberry Pi CM4 but has a third board-to-board connector providing several features that would be quite convenient for this application:
- Audio codec with headphone out, mono speaker out, and microphone in
- Battery charger
- Power button handling
- Native LVDS support on the DSI0 pins (and the extra DSI0 pins are broken out)
- HDMI pins can also output DisplayPort (the dock connector on the iPad can speak DisplayPort, which is used to drive the 30-pin to HDMI adapter.)
In addition, the Rockchip RK3566 that the Radxa CM3 is built around actually seems to have the ability to sleep, which is especially important on a tablet. On the CM4, I'll have to suspend to disk or just shut down or something.
I think if I the Radxa CM3 had existed when I started this project, I might've chosen it over the Pi CM4, as it should require a lot less glue circuitry to make it work in this application. (I had originally considered the Pine64 SOPine and the Nvidia Jetson Nano, but I think I excluded these because the SO-DIMM connectors that they require wouldn't fit between the case and the screen.) As a bonus, the RK3566 is less tall than the BCM2711, which may buy me a little vertical room, depending on the other components on the CM3.
I've also designed a version of my board based around the Radxa CM3. Once I figured out the position and orientation of the third connector on the CM3, this wasn't too bad -- it has far fewer components than the CM4 version of the board. It's essentially just connectors, the touchscreen controller, a buck-boost converter for the NVMe slot, and some audio switches.
Radxa CM3-based version of my board Since the Radxa CM3 / RK3566 clearly has less software support than the Pi (I think as of the time of writing, you can't even get HDMI output, let alone LVDS.), I'm planning to hedge my bets and have both boards made.
-
Colors! (sn65dsi83 test pattern)
08/23/2021 at 06:26 • 0 commentsThanks to the help of some folks on the Raspberry Pi forums, I've got the sn65dsi83 driver loading, and the Pi generating enough DSI signal for the SN65DSI83 to be able to generate a test pattern (it uses the DSI clock to generate the LVDS clock)
Hopefully I can get those black lines to go away.
-
Using ftrace to debug device tree problems
08/18/2021 at 07:28 • 1 commentFor the past few days, I've been trying to get the sn65ds83 driver (recently added to linux) running and talking to my board. It has been a (re-)learning experience trying to get this device tree overlay to cooperate.
Following my own advice from the last time I touched device trees, I used udevadm monitor and dmesg -w to watch for kernel/udev messages while loading my overlay with sudo dtoverlay pipad_screen. This gave me this helpful error message:
[ 178.309859] sn65dsi83: probe of 1-002d failed with error -2
From what I can tell from looking at the kernel source, this implies that the sn65dsi83_probe function was returning -2. Unfortunately, the sn65dsi83_probe function can return errors from several different places. Fortunately, there's a kernel feature called ftrace that can trace every function call that happens within the kernel. The easiest way to use it is to use the trace-cmd command, which you can install on Raspberry Pi OS with apt install trace-cmd. You record with trace-cmd record -p function_graph (with various options for filtering), then view the results with trace-cmd report.
Supposedly, you can trace everything by just running trace-cmd record -p function_graph with no filter options, but when I tried this, I had a lot of dropped events and didn't see the sn65dsi83_probe function in the results.
After learning some things about ftrace (by manually constructing a list of functions to filter on with the -l flag), I discovered that ftrace can only trace functions in kernel modules that are loaded before ftrace starts. Normally, the ti-sn65dsi83 module is loaded automatically when I run dtoverlay pipad_screen, but that prevents ftrace from seeing it.
If I manually modprobe ti-sn65dsi83 before running trace-cmd record -p function_graph -F dtoverlay pipad_screen, I get no dropped events, and the trace for the sn65dsi83_probe function was there in the output of trace-cmd report:
sn65dsi83_probe() { devm_kmalloc() { __kmalloc_track_caller() { kmalloc_slab(); should_failslab(); } devres_add() { _raw_spin_lock_irqsave() { preempt_count_add(); } _raw_spin_unlock_irqrestore() { preempt_count_sub(); } } } of_device_get_match_data() { of_match_node() { _raw_spin_lock_irqsave() { preempt_count_add(); } __of_match_node.part.0() { __of_device_is_compatible() { __of_find_property(); of_prop_next_string(); } __of_device_is_compatible() { __of_find_property(); of_prop_next_string(); of_prop_next_string(); } } _raw_spin_unlock_irqrestore() { preempt_count_sub(); } } } devm_gpiod_get() { ... } }
This implies that sn65dsi83_probe was returning immediately after the call to devm_gpiod_get finished. Oh, right, I never put the enable GPIO pin for the sn65dsi83 in my dts file. I figured the property would be called enable because that's the string being passed to devm_gpiod_get, but nope. From looking at documentation for other bridges, I inferred that it's enable-gpios.
Adding enable-gpios = <&gpio 5 0>; to my device tree and trying again, this time I get from dmesg -w:
[ 95.093834] sn65dsi83: probe of 1-002d failed with error -22
Looking at my report, this time I see:
sn65dsi83_probe() { devm_kmalloc() { ... } of_device_get_match_data() { ... } devm_gpiod_get() { ... } of_graph_get_endpoint_by_regs() { ... } of_property_count_elems_of_size() { ... } of_graph_get_remote_port_parent() { ... } of_node_put(); }
Looks like it's failing just after of_graph_get_remote_port_parent and of_node_put. These aren't called directly from sn65dsi83_probe, but by sn65dsi83_parse_dt. Because sn65dsi83_parse_dt is defined as a static function, it can't be traced by ftrace, and everything it calls appears directly beneath sn65dsi83_probe.
I think I'm being caught by this code:
ctx->dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); ctx->host_node = of_graph_get_remote_port_parent(endpoint); of_node_put(endpoint); if (ctx->dsi_lanes < 0 || ctx->dsi_lanes > 4) return -EINVAL;
Sure enough, EINVAL is defined as 22. This probably means I need to fix/add a data-lanes entry in my device tree. Looking at the sn65dsi86 driver (
which is better-documentedTurns out, the sn65dsi83 driver is perfectly well-documented, I just didn't cherry-pick that commit onto my own kernel branch. 🤦♂️), it looks like this needs to go on the endpoint node, not the bridge node. Once I fixed this, and moved my port to port@0 (instead of port@1 which I had for some reason), I no longer get the failed with error -22 message. Now I have no errors at all when loading the overlay.Time to figure out what the next step is, I guess.
For posterity, heres a gist of my dts file.
-
First light: testing and fixing the backlight controller
07/10/2021 at 05:07 • 0 commentsBased on my guess of how complicated testing the different components will be, I decided to test the LCD backlight controller next.
For this part of my PCB, I basically built the example circuit from the datasheet of the TPS61176. This chip is essentially a DC-DC boost converter and 6 constant-current/PWM sinks. You control it by providing a PWM signal on its PWM/EN input. It decodes this signal and then runs its own PWM on each IFB# pin.
Not wanting to risk frying my iPad's display, I wanted to first test with my own string of LEDs. However, I'm a dummy and I neglected to put any test points on this part of my PCB, and because both ends of the trace end up at fine-pitch footprints, I opted to scrape away some solder mask and solder some magnet wire directly to some traces.
Magnet wire soldered directly to traces. Don't forget to add test points to your PCB! I dead-bug soldered a few LEDs together to make my test string, and connected them to the common anode and one of the cathode lines, which works great:
fading the brightness from 0% to 100% The TPS61176 will increase the anode voltage until your LEDs start actually drawing current, so it basically supports strings of LEDs with arbitrary forward voltage, within its voltage range.
After double-checking that I hadn't flipped the pinout for the display, I decided to connect the actual LCD panel. Powered it up and ....nothing. Black. The anode voltage was 3.6V (so, not boosted at all). My hypothesis is that it's detecting an open circuit on all the LED strings, so it disabled the boost converter. Some basic testing confirms:
The anode voltage jumps (higher than my Saleae Logic 8 can measure), and then falls back down. I wonder how high that voltage is going? Is it higher than the 18V required to drive the backlight on the LTN097XL01-A01 panel in my iPad?
I soldered a few more wires onto my board: one at the MODE/FAULT pin, and one at the OVP pin (which is connected to a voltage divider with 143k / 1M, which divides the anode voltage by 8). Rerunning the test gives me:
1.5V at the OVP pin. There's my problem! Turns out the reference voltage for the OVP pin is 1.5V, and with the 1/8 division, I'm only getting 12V max. I need to adjust the ratio on my voltage divider.
After swapping the 143k resistor for an 82k resistor, the backlight works!
the backlight on my LTN097XL01-A01 glowing