.include "arch/${MACHINE_ARCH}/Makefile.inc"
-SRCS+= fb.c
+SRCS+= fb_edid.c fb.c
+
+# re-use EDID parsing/validation code from NetBSD.
+.PATH: ${NETBSDSRCDIR}/sys/dev/videomode
+SRCS+= edid.c pickmode.c videomode.c vesagtf.c
+
+# Put this dir and the EDID headers (dev/videomode/*.h) in the search path.
+CPPFLAGS+= -I${.CURDIR} -I${NETBSDSRCDIR}/sys
DPADD+= ${LIBCHARDRIVER} ${LIBSYS}
LDADD+= -lchardriver -lsys
--- /dev/null
+Frame Buffer Driver
+===================
+
+Overview
+--------
+
+This is the driver for the frame buffer. Currently it only supports the
+DM37XX (BeagleBoard-xM).
+
+Testing the Code
+----------------
+
+Starting up an instance:
+
+service up /usr/sbin/fb -dev /dev/fb0 -args edid.0=cat24c256.3.50
+
+The arguments take the following form:
+
+ edid.X=L where X is the frame buffer device (usually 0) and L is
+ the service label of the service to perform the EDID reading. In
+ the example above, it's the EEPROM with slave address 0x50 on
+ the 3rd I2C bus. If you want to use the defaults and skip EDID
+ reading, you may omit the arguments.
+
-/* Architecture dependent part for the framebuffer on the OMAP3. Since we don't
- * have support for EDID (which requires support for i2c, also something we
- * don't have, yet), but we do have a screen with 1024*600 resolution for our
- * testing purposes, we hardcode that resolution here. There's obvious room for
- * improvement. */
+/* Architecture dependent part for the framebuffer on the OMAP3.
+ * There's obvious room for improvement.
+ */
#include <minix/chardriver.h>
#include <minix/drivers.h>
#include <minix/fb.h>
#include <minix/type.h>
#include <minix/vm.h>
+#include <minix/log.h>
#include <assert.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdint.h>
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+#include <dev/videomode/edidreg.h>
#include "dss.h"
+#include "fb.h"
+
+/* default / fallback resolution if EDID reading fails */
#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 600
#define PAGES_NR 2
+#define NSUPPORTED_MODES (4)
+
+/* List of valid modes from TRM 7.1
+ * Other modes might work (like the default 1024x600), but no guarantees.
+ */
+struct supported_modes {
+ int hdisplay;
+ int vdisplay;
+} omap_supported_modes[NSUPPORTED_MODES] = {
+ { .hdisplay = 1024, .vdisplay = 768 }, /* XGA */
+ { .hdisplay = 1280, .vdisplay = 800 }, /* WXGA */
+ { .hdisplay = 1400, .vdisplay = 1050 }, /* SXGA+ */
+ { .hdisplay = 1280, .vdisplay = 720 } /* HD 720p */
+};
+
+/* local function prototypes */
+static struct videomode *choose_mode(struct edid_info *info);
+static void configure_with_defaults(int minor);
+static int configure_with_edid(int minor, struct edid_info *info);
+
+/* globals */
static vir_bytes dss_phys_base; /* Address of dss phys memory map */
static vir_bytes dispc_phys_base; /* Address of dispc phys memory map */
static vir_bytes fb_vir;
.panel_color = 0xFFFFFF /* WHITE */
};
-static const struct fb_fix_screeninfo fbfs = {
+static struct panel_config omap_cfg[FB_DEV_NR];
+
+static const struct fb_fix_screeninfo default_fbfs = {
.xpanstep = 0,
.ypanstep = 0,
.ywrapstep = 0,
.mmio_len = 0 /* these are set to 0 */
};
-static struct fb_var_screeninfo fbvs = {
+static struct fb_fix_screeninfo omap_fbfs[FB_DEV_NR];
+
+static const struct fb_var_screeninfo default_fbvs = {
.xres = SCREEN_WIDTH,
.yres = SCREEN_HEIGHT,
.xres_virtual = SCREEN_WIDTH,
}
};
+static struct fb_var_screeninfo omap_fbvs[FB_DEV_NR];
+
+/* logging - use with log_warn(), log_info(), log_debug(), log_trace() */
+static struct log log = {
+ .name = "fb",
+ .log_level = LEVEL_INFO,
+ .log_func = default_log
+};
+
static inline u32_t
readw(vir_bytes addr)
{
*((volatile u32_t *) addr) = val;
}
+static struct videomode *
+choose_mode(struct edid_info *info)
+{
+ int i, j;
+
+ /* choose the highest resolution supported by both the SoC and screen */
+ for (i = info->edid_nmodes - 1; i >= 0; i--) {
+ for (j = NSUPPORTED_MODES - 1; j >= 0; j--) {
+
+ if (info->edid_modes[i].hdisplay ==
+ omap_supported_modes[j].hdisplay &&
+ info->edid_modes[i].vdisplay ==
+ omap_supported_modes[j].vdisplay) {
+
+ return &(info->edid_modes[i]);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static int
+configure_with_edid(int minor, struct edid_info *info)
+{
+ struct videomode *mode;
+
+ if (info == NULL || minor < 0 || minor >= FB_DEV_NR) {
+ log_warn(&log, "Invalid minor #%d or info == NULL\n", minor);
+ return -1;
+ }
+
+ /* If debugging or tracing, print the contents of info */
+ if (log.log_level >= LEVEL_DEBUG) {
+ log_debug(&log, "--- EDID - START ---\n");
+ edid_print(info);
+ log_debug(&log, "--- EDID - END ---\n");
+ }
+
+ /* Choose the preferred mode. */
+ mode = choose_mode(info);
+ if (mode == NULL) {
+ log_warn(&log, "Couldn't find a supported resolution.\n");
+ return -1;
+ }
+
+ /*
+ * apply the default settings since we don't overwrite every field
+ */
+ configure_with_defaults(minor);
+
+ /*
+ * apply the settings corresponding to the given EDID
+ */
+
+ /* panel_config */
+ omap_cfg[minor].lcd_size = ((mode->vdisplay - 1) << 16 | (mode->hdisplay - 1));
+
+ if (EDID_FEATURES_DISP_TYPE(info->edid_features) ==
+ EDID_FEATURES_DISP_TYPE_MONO) {
+ omap_cfg[minor].panel_type = 0x00; /* Mono */
+ } else {
+ omap_cfg[minor].panel_type = 0x01; /* RGB/Color */
+ }
+
+ /* fb_fix_screeninfo */
+ omap_fbfs[minor].line_length = mode->hdisplay * 4;
+
+ /* fb_var_screeninfo */
+ omap_fbvs[minor].xres = mode->hdisplay;
+ omap_fbvs[minor].yres = mode->vdisplay;
+ omap_fbvs[minor].xres_virtual = mode->hdisplay;
+ omap_fbvs[minor].yres_virtual = mode->vdisplay*2;
+
+ return OK;
+}
+
+static void
+configure_with_defaults(int minor)
+{
+ if (minor < 0 || minor >= FB_DEV_NR) {
+ log_warn(&log, "Invalid minor #%d\n", minor);
+ return;
+ }
+
+ /* copy the default values into this minor's configuration */
+ memcpy(&omap_cfg[minor], &default_cfg, sizeof(struct panel_config));
+ memcpy(&omap_fbfs[minor], &default_fbfs, sizeof(struct fb_fix_screeninfo));
+ memcpy(&omap_fbvs[minor], &default_fbvs, sizeof(struct fb_var_screeninfo));
+}
+
static void
arch_configure_display(int minor)
{
if (!initialized) return;
if (minor != 0) return;
- off = fbvs.yoffset * fbvs.xres_virtual * (fbvs.bits_per_pixel/8);
+ off = omap_fbvs[minor].yoffset * omap_fbvs[minor].xres_virtual * (omap_fbvs[minor].bits_per_pixel/8);
writew((vir_bytes) OMAP3_DISPC_GFX_BA0(dispc_phys_base),
fb_phys + (phys_bytes) off);
if (!initialized) return ENXIO;
if (minor != 0) return ENXIO;
- *fbvsp = fbvs;
+ *fbvsp = omap_fbvs[minor];
return OK;
}
if (minor != 0) return ENXIO;
/* For now we only allow to play with the yoffset setting */
- if (fbvsp->yoffset != fbvs.yoffset) {
- if (fbvsp->yoffset < 0 || fbvsp->yoffset > fbvs.yres) {
+ if (fbvsp->yoffset != omap_fbvs[minor].yoffset) {
+ if (fbvsp->yoffset < 0 || fbvsp->yoffset > omap_fbvs[minor].yres) {
return EINVAL;
}
- fbvs.yoffset = fbvsp->yoffset;
+ omap_fbvs[minor].yoffset = fbvsp->yoffset;
}
/* Now update hardware with new settings */
if (!initialized) return ENXIO;
if (minor != 0) return ENXIO;
- *fbfsp = fbfs;
+ *fbfsp = omap_fbfs[minor];
return OK;
}
}
int
-arch_fb_init(int minor, struct device *dev)
+arch_fb_init(int minor, struct device *dev, struct edid_info *info)
{
+ int r;
u32_t rdispc;
struct minix_mem_range mr;
- const struct panel_config *panel_cfg = &default_cfg;
+ const struct panel_config *panel_cfg = &omap_cfg[minor];
assert(dev != NULL);
if (minor != 0) return ENXIO; /* We support only one minor */
+
if (initialized) {
dev->dv_base = fb_vir;
dev->dv_size = fb_size;
return OK;
+ } else if (info != NULL) {
+ log_debug(&log, "Configuring Settings based on EDID...\n");
+ r = configure_with_edid(minor, info);
+ if (r != OK) {
+ log_warn(&log, "EDID config failed. Using defaults.\n");
+ configure_with_defaults(minor);
+ }
+ } else {
+ log_debug(&log, "Loading Default Settings...\n");
+ configure_with_defaults(minor);
}
initialized = 1;
writew(OMAP3_DISPC_GFX_PIXEL_INC(dispc_phys_base), 1);
/* Allocate contiguous physical memory for the display buffer */
- fb_size = fbvs.yres_virtual * fbvs.xres_virtual *
- (fbvs.bits_per_pixel / 8);
+ fb_size = omap_fbvs[minor].yres_virtual * omap_fbvs[minor].xres_virtual *
+ (omap_fbvs[minor].bits_per_pixel / 8);
fb_vir = (vir_bytes) alloc_contig(fb_size, 0, &fb_phys);
if (fb_vir == (vir_bytes) MAP_FAILED) {
panic("Unable to allocate contiguous memory\n");
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdint.h>
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+#include <dev/videomode/edidreg.h>
+
#include "logos.h"
+#include "fb_edid.h"
#include "fb.h"
-#define FB_DEV_NR 1
/*
* Function prototypes for the fb driver.
*/
static int
fb_open(message *m)
{
+ int r;
static int initialized = 0;
+ static struct edid_info info;
+ static struct edid_info *infop = NULL;
if (m->DEVICE < 0 || m->DEVICE >= FB_DEV_NR) return ENXIO;
- if (arch_fb_init(m->DEVICE, &fb_device[m->DEVICE]) == OK) {
+ if (!initialized) {
+ r = fb_edid_read(m->DEVICE, &info);
+ infop = (r == 0) ? &info : NULL;
+ }
+
+ if (arch_fb_init(m->DEVICE, &fb_device[m->DEVICE], infop) == OK) {
open_counter[m->DEVICE]++;
if (!initialized) {
if (has_restarted) {
}
int
-main(void)
+main(int argc, char *argv[])
{
+ env_setargs(argc, argv);
+ fb_edid_args_parse();
+
sef_local_startup();
chardriver_task(&fb_tab, CHARDRIVER_SYNC);
return OK;
#ifndef __FB_H__
#define __FB_H__
-int arch_fb_init(int minor, struct device *dev);
+#include <minix/fb.h>
+
+int arch_fb_init(int minor, struct device *dev, struct edid_info *info);
int arch_get_device(int minor, struct device *dev);
int arch_get_varscreeninfo(int minor, struct fb_var_screeninfo *fbvsp);
int arch_put_varscreeninfo(int minor, struct fb_var_screeninfo *fbvs_copy);
int arch_pan_display(int minor, struct fb_var_screeninfo *fbvs_copy);
#define FB_MESSAGE "Hello, world! From framebuffer!\n"
+#define FB_DEV_NR 1
#endif /* __FB_H__ */
--- /dev/null
+/*
+ * Handle reading the EDID information, validating it, and parsing it into
+ * a struct edid_info. EDID reads are done using the Block Device Protocol
+ * as it's already supported by the cat24c256 driver and there is no need
+ * to add yet another message format/type.
+ */
+
+#include <minix/fb.h>
+#include <minix/chardriver.h>
+#include <minix/drivers.h>
+#include <minix/ds.h>
+#include <minix/rs.h>
+#include <minix/log.h>
+#include <minix/sysutil.h>
+#include <minix/type.h>
+#include <minix/vm.h>
+#include <sys/ioc_fb.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+#include <dev/videomode/edidreg.h>
+
+#include "fb_edid.h"
+#include "fb.h"
+
+static int do_read(endpoint_t endpt, uint8_t *buf, size_t bufsize);
+
+/* logging - use with log_warn(), log_info(), log_debug(), log_trace() */
+static struct log log = {
+ .name = "edid",
+ .log_level = LEVEL_INFO,
+ .log_func = default_log
+};
+
+/*
+ * Labels corresponding to drivers which provide EDID.
+ */
+static char edid_providers[FB_DEV_NR][RS_MAX_LABEL_LEN+1];
+
+/*
+ * Populate edid_providers from command line arguments. The service command
+ * should get EDID providers like this: "-args edid.0=tda19988.1.3470" where
+ * 0 is the minor number of the frame buffer, tda19988 is the device driver,
+ * 1 is the i2c bus and 3470 is the slave address (the TDA19988 has 2 slave
+ * addresses 0x34 and 0x70).
+ */
+int
+fb_edid_args_parse(void)
+{
+ int i;
+ int r;
+ char key[32];
+
+ for (i = 0; i < FB_DEV_NR; i++) {
+
+ memset(key, '\0', 32);
+ snprintf(key, 32, "edid.%d", i);
+
+ memset(edid_providers[i], '\0', RS_MAX_LABEL_LEN);
+ r = env_get_param(key, edid_providers[i], RS_MAX_LABEL_LEN);
+ if (r == OK) {
+ log_debug(&log, "Found key:%s value:%s\n", key, edid_providers[i]);
+ } else {
+ /* not an error, user is allowed to omit EDID
+ * providers in order to skip EDID reading and use
+ * the default settings.
+ */
+ log_debug(&log, "Couldn't find key:%s\n", key);
+ }
+ }
+
+ return OK;
+}
+
+/*
+ * Send a read request to the block driver at endpoint endpt.
+ */
+static int
+do_read(endpoint_t driver_endpt, uint8_t *buf, size_t bufsize)
+{
+ int r;
+ message m;
+ cp_grant_id_t grant_nr;
+
+ /* Open Device - required for drivers using libblockdriver */
+ memset(&m, '\0', sizeof(message));
+ m.m_type = BDEV_OPEN;
+ m.BDEV_ACCESS = R_BIT;
+ m.BDEV_ID = 0;
+ m.BDEV_MINOR = 0;
+
+ r = sendrec(driver_endpt, &m);
+ if (r != OK) {
+ log_debug(&log, "sendrec(BDEV_OPEN) failed (r=%d)\n", r);
+ return r;
+ }
+
+ grant_nr = cpf_grant_direct(driver_endpt, (vir_bytes) buf,
+ bufsize, CPF_READ | CPF_WRITE);
+
+ /* Perform the read */
+ memset(&m, '\0', sizeof(message));
+ m.m_type = BDEV_READ;
+ m.BDEV_MINOR = 0;
+ m.BDEV_COUNT = bufsize;
+ m.BDEV_GRANT = grant_nr;
+ m.BDEV_FLAGS = BDEV_NOPAGE; /* the EEPROMs used for EDID are pageless */
+ m.BDEV_ID = 0;
+ m.BDEV_POS_LO = 0;
+ m.BDEV_POS_HI = 0;
+
+ r = sendrec(driver_endpt, &m);
+ cpf_revoke(grant_nr);
+ if (r != OK) {
+ log_debug(&log, "sendrec(BDEV_READ) failed (r=%d)\n", r);
+ /* Clean-up: try to close the device */
+ memset(&m, '\0', sizeof(message));
+ m.m_type = BDEV_CLOSE;
+ m.BDEV_MINOR = 0;
+ m.BDEV_ID = 0;
+ sendrec(driver_endpt, &m);
+ return r;
+ }
+
+ /* Close the device */
+ memset(&m, '\0', sizeof(message));
+ m.m_type = BDEV_CLOSE;
+ m.BDEV_MINOR = 0;
+ m.BDEV_ID = 0;
+ r = sendrec(driver_endpt, &m);
+ if (r != OK) {
+ log_debug(&log, "sendrec(BDEV_CLOSE) failed (r=%d)\n", r);
+ return r;
+ }
+
+ return bufsize;
+}
+
+int
+fb_edid_read(int minor, struct edid_info *info)
+{
+
+ int r;
+ uint8_t buffer[128];
+ endpoint_t endpt;
+
+ if (info == NULL || minor < 0 || minor >= FB_DEV_NR ||
+ edid_providers[minor][0] == '\0') {
+ return EINVAL;
+ }
+
+ log_debug(&log, "Contacting %s to get EDID.\n", edid_providers[minor]);
+
+ /* Look up the endpoint that corresponds to the label */
+ endpt = 0;
+ r = ds_retrieve_label_endpt(edid_providers[minor], &endpt);
+ if (r != 0 || endpt == 0) {
+ log_warn(&log, "Couldn't find endpoint for label '%s'\n", edid_providers[minor]);
+ return r;
+ }
+
+ /* Perform the request and put the resulting EDID into the buffer. */
+ memset(buffer, 0x00, 128);
+ r = do_read(endpt, buffer, 128);
+ if (r < 0) {
+ log_debug(&log, "Failed to read EDID\n");
+ return r;
+ }
+
+ /* parse and validate EDID */
+ r = edid_parse(buffer, info);
+ if (r != 0) {
+ log_warn(&log, "Invalid EDID data in buffer.\n");
+ return r;
+ }
+
+ log_debug(&log, "EDID Retrieved and Parsed OK\n");
+
+ return OK;
+}
+
--- /dev/null
+#ifndef __EDID_H
+#define __EDID_H
+
+#include <stdint.h>
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+#include <dev/videomode/edidreg.h>
+
+int fb_edid_args_parse(void);
+int fb_edid_read(int minor, struct edid_info *info);
+
+#endif /* __EDID_H */
PRIVCTL # 4
;
ipc
- SYSTEM pm rs ds vm vfs tda19988
+ SYSTEM pm rs ds vm vfs cat24c256 tda19988
;
};
BOARD_NAME=`eepromread -i | sed -n 's/^BOARD_NAME : \(.*\)$/\1/p'`
case "${BOARD_NAME}" in
+
A335BONE)
echo "Detected BeagleBone"
echo -n "Starting i2c device drivers: "
- test -e /dev/eepromb1s50 || (cd /dev && MAKEDEV eepromb1s50)
+
+ # start EEPROM driver for reading board info
+ test -e /dev/eepromb1s50 || \
+ (cd /dev && MAKEDEV eepromb1s50)
up cat24c256 -dev /dev/eepromb1s50 \
-label cat24c256.1.50 \
-args 'bus=1 address=0x50'
up tps65217 -label tps65217.1.24 \
-args 'bus=1 address=0x24'
+ # check for the presence of a display
+ eepromread -f /dev/i2c-2 -n > /dev/null 2>&1
+ RESULT=$?
+ if [ $RESULT -eq 0 ]
+ then
+ # start eeprom driver for reading EDID.
+ test -e /dev/eepromb2s50 || \
+ (cd /dev && MAKEDEV eepromb2s50)
+ up cat24c256 -dev /dev/eepromb2s50 \
+ -label cat24c256.2.50 \
+ -args 'bus=2 address=0x50'
+
+ # start frame buffer
+ #up fb -dev /dev/fb0 -args edid.0=cat24c256.2.50
+ # fb hasn't been ported to AM335X yet.
+ fi
+
;;
+
A335BNLT)
echo "Detected BeagleBone Black"
echo -n "Starting i2c device drivers: "
- test -e /dev/eepromb1s50 || (cd /dev && MAKEDEV eepromb1s50)
+
+ # start EEPROM driver for reading board info
+ test -e /dev/eepromb1s50 || \
+ (cd /dev && MAKEDEV eepromb1s50)
up cat24c256 -dev /dev/eepromb1s50 \
- -label cat24c256.1.50 -args 'bus=1 address=0x50'
+ -label cat24c256.1.50 \
+ -args 'bus=1 address=0x50'
# Start TPS65217 driver for power management.
up tps65217 -label tps65217.1.24 \
# Start TDA19988 driver for reading EDID.
up tda19988 -label tda19988.1.3470 -args \
'cec_bus=1 cec_address=0x34 hdmi_bus=1 hdmi_address=0x70'
+
+ # start frame buffer
+ #up fb -dev /dev/fb0 -args edid.0=tda19988.1.3470
+ # fb hasn't been ported to AM335X yet.
+
;;
+
UNKNOWN)
echo "Unable to detect board -- assuming BeagleBoard-xM"
echo -n "Starting i2c device drivers: "
# Set the system time to the time in the TPS65950's RTC
readclock
+ # check for the presence of a display
+ eepromread -f /dev/i2c-3 -n > /dev/null 2>&1
+ RESULT=$?
+ if [ $RESULT -eq 0 ]
+ then
+ # start eeprom driver for reading edid
+ test -e /dev/eepromb3s50 || \
+ (cd /dev && MAKEDEV eepromb3s50)
+ up cat24c256 -dev /dev/eepromb3s50 \
+ -label cat24c256.3.50 \
+ -args 'bus=3 address=0x50'
+
+ # start frame buffer
+ up fb -dev /dev/fb0 -args edid.0=cat24c256.3.50
+ fi
+
;;
esac