BeagleBoneBlack, DLP2000 projector and FreeBSD

Under the cryptic name DLPDLCR2000EVM Texas Instruments sells a cute gadet, which turns the BeagleBoneBlack into a miniature projector:

../../_images/dlpdlcr2000evm_dlp-lightcrafter-display-2000-board-image.JPG

To make it work with FreeBSD you will need a device tree overlay and a program to turn the project on via I2C.

The overlay in source form:

/dts-v1/;

/  {

        part-number = "BB-LCD7-NH";
        version = "00A7";
        fragment@0 {

                target = <0xdeadbeef>;
                __overlay__ {

                        pinmux_bb_lcd_lcd_pins {

                                pinctrl-single,pins = <0xa0 0x8 0xa4 0x8 0xa8 0x8 0xac 0x8 0xb0 0x8 0xb4 0x8 0xb8 0x8 0xbc 0x8 0xc0 0x8 0xc4 0x8 0xc8 0x8 0xcc 0x8 0xd0 0x8 0xd4 0x8 0xd8 0x8 0xdc 0x8 0x3c 0x8 0x38 0x8 0x34 0x8 0x30 0x8 0x2c 0x8 0x28 0x8 0x24 0x8 0x20 0x8 0xe0 0x8 0xe4 0x8 0xe8 0x8 0xec 0x8 0x8c 0x17>;
                                phandle = <0x1>;
                        };
                        pinmux_bb_i2c2_pins {

                                pinctrl-single,pins = <0x17c 0x73 0x178 0x73>;
                                phandle = <0x2>;
                        };
                        pinmux_edt_ft5x06_pins {

                                pinctrl-single,pins = <0x1a4 0x27>;
                                phandle = <0x3>;
                        };
                };
        };
        fragment@1 {

                target = <0xdeadbeef>;
                __overlay__ {

                        status = "okay";
                        blue-and-red-wiring = "crossed";
                };
        };
        fragment@2 {

                target = <0xdeadbeef>;
                __overlay__ {

                        status = "okay";
                        pinctrl-names = "default";
                        pinctrl-0 = <0x2>;
                        clock-frequency = <0x186a0>;
                        #address-cells = <0x1>;
                        #size-cells = <0x0>;
                        edt-ft5x06@38 {

                                status = "okay";
                                compatible = "edt,edt-ft5406", "edt,edt-ft5x06";
                                reg = <0x38>;
                                pinctrl-names = "default";
                                pinctrl-0 = <0x3>;
                                interrupt-parent = <0xdeadbeef>;
                                interrupts = <0x13 0x0>;
                                touchscreen-size-x = <0x320>;
                                touchscreen-size-y = <0x1e0>;
                        };
                };
        };
        fragment@3 {

                target-path = "/";
                __overlay__ {

                        #address-cells = <0x1>;
                        #size-cells = <0x1>;
                        panel {

                                status = "okay";
                                compatible = "ti,tilcdc,panel";
                                pinctrl-names = "default";
                                pinctrl-0 = <0x1>;
                                panel-info {

                                        ac-bias = <0xff>;
                                        ac-bias-intrpt = <0x0>;
                                        dma-burst-sz = <0x10>;
                                        bpp = <0x20>;
                                        fdd = <0x80>;
                                        tft-alt-mode = <0x0>;
                                        stn-565-mode = <0x0>;
                                        mono-8bit-mode = <0x0>;
                                        sync-edge = <0x0>;
                                        sync-ctrl = <0x0>;
                                        raster-order = <0x0>;
                                        fifo-th = <0x0>;
                                };
                                display-timings {

                                        native-mode = <0x4>;
                                        640x360 {

                                                clock-frequency = <0x5a06e0>;
                                                hactive = <0x280>;
                                                vactive = <0x168>;
                                                hfront-porch = <0xe>;
                                                hback-porch = <0xc>;
                                                hsync-len = <0x4>;
                                                vback-porch = <0x9>;
                                                vfront-porch = <0x2>;
                                                vsync-len = <0x3>;
                                                hsync-active = <0x0>;
                                                vsync-active = <0x0>;
                                                pixelclk-active = <0x1>;
                                                phandle = <0x4>;
                                        };
                                };
                        };
                };
        };
        __symbols__ {

                timing0 = "/fragment@3/__overlay__/panel/display-timings/640x360";
                edt_ft5x06_pins = "/fragment@0/__overlay__/pinmux_edt_ft5x06_pins";
                bb_i2c2_pins = "/fragment@0/__overlay__/pinmux_bb_i2c2_pins";
                bb_lcd_lcd_pins = "/fragment@0/__overlay__/pinmux_bb_lcd_lcd_pins";
        };
        __fixups__ {

                am33xx_pinmux = "/fragment@0:target:0";
                lcdc = "/fragment@1:target:0";
                i2c2 = "/fragment@2:target:0";
                gpio3 = "/fragment@2/__overlay__/edt-ft5x06@38:interrupt-parent:0";
        };
        __local_fixups__ {

                fragment@2 {

                        __overlay__ {

                                edt-ft5x06@38 {

                                        pinctrl-0 = <0x0>;
                                };
                        };
                };
                fragment@2 {

                        __overlay__ {

                                pinctrl-0 = <0x0>;
                        };
                };
                fragment@3 {

                        __overlay__ {

                                panel {

                                        display-timings {

                                                native-mode = <0x0>;
                                        };
                                };
                        };
                };
                fragment@3 {

                        __overlay__ {

                                panel {

                                        pinctrl-0 = <0x0>;
                                };
                        };
                };
        };
};

And a small C-program for the I2C stuff:

#include <assert.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/endian.h>
#include <dev/iicbus/iic.h>

static int
write_bytes(int fd, int slave, int len, uint8_t *buf)
{
        struct iic_msg msg[2];
        struct iic_rdwr_data rdwr;

        msg[0].slave = slave << 1;
        msg[0].flags = IIC_M_WR;
        msg[0].len = len;
        msg[0].buf = buf;

        rdwr.msgs = msg;
        rdwr.nmsgs = 1;

        return (ioctl(fd, I2CRDWR, &rdwr));
}


static int
read_bytes(int fd, int slave, int off, int len, uint8_t *buf)
{
        struct iic_msg msg[2];
        struct iic_rdwr_data rdwr;
        uint8_t offset[2];

        offset[0] = 0x15;
        offset[1] = off;
        msg[0].slave = slave << 1;
        msg[0].flags = IIC_M_WR;
        msg[0].len = sizeof( offset );
        msg[0].buf = offset;

        msg[1].slave = slave << 1;
        msg[1].flags = IIC_M_RD;
        msg[1].len = len;
        msg[1].buf = buf;

        rdwr.msgs = msg;
        rdwr.nmsgs = 2;

        return (ioctl(fd, I2CRDWR, &rdwr));
}

static int
reset(int fd)
{
        struct iiccmd ic;
        int i;

        memset(&ic, 0, sizeof ic);
        i = ioctl(fd, I2CRSTCARD, &ic);
        assert(i == 0);
        return (i);
}

static void
rdall(int fd)
{
        int i, j;
        uint8_t buf[16];

        for (j = 0; j < 0x20; j++) {
                i = read_bytes(fd, 0x1b, j, 4, buf);
                printf("J %02x I %d %02x %02x %02x %02x\n",
                        j, i, buf[0], buf[1], buf[2], buf[3]);
        }
        j = 0xb5;
        i = read_bytes(fd, 0x1b, j, 4, buf);
        printf("J %02x I %d %02x %02x %02x %02x\n",
                j, i, buf[0], buf[1], buf[2], buf[3]);
}

static void
write_reg(int fd, int reg, unsigned val)
{
        uint8_t buf[5];
        int i;


        buf[0] = reg;
        be32enc(buf + 1, val);
        i = write_bytes(fd, 0x1b, 5, buf);
        printf("WRITE reg=0x%x val=0x%x i=%d [%02x %02x %02x %02x %02x]\n",
            reg, val, i,
            buf[0], buf[1], buf[2], buf[3], buf[4]);
        assert(i == 0);
}

int
main(int argc, char **argv)
{
        uint8_t buf[16];
        int i, j;

        int fd = open("/dev/iic1", O_RDWR);
        assert(fd >= 0);

        write_reg(fd, 0xb, 0);
        write_reg(fd, 0xc, 0x1b);
        rdall(fd);
        return (0);
        return (0);
}

Compile the device tree overlay:

dtc -I dts -O dtb < the_dts_from_above > /boot/dtb/overlays/dtb.dtbo

Compile the i2c code:

cc -o /root/dlpiic the_c_source_from_above

Add this line in /boot/loader.conf:

fdt_overlays="dlp.dtbo"

Create /etc/rc.d/dlp with this content:

# BEFORE: disks

/usr/sbin/gpioctl -f /dev/gpioc1 -v -c 16 OUT
/usr/sbin/gpioctl -f /dev/gpioc1 -v 16 1
sleep .3
/root/dlpiic

Remember to make it executable:

chmod +x /etc/rc.d/dlp

and reboot the BBB…

Install Xorg, start the X-server run “xfireworks” and turn of the light…

phk