From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from metis.ext.pengutronix.de ([2001:6f8:1178:4:290:27ff:fe1d:cc33]) by canuck.infradead.org with esmtps (Exim 4.72 #1 (Red Hat Linux)) id 1PAhl3-0007mf-KX for barebox@lists.infradead.org; Tue, 26 Oct 2010 11:32:11 +0000 From: Juergen Beisert Date: Tue, 26 Oct 2010 13:31:45 +0200 Message-Id: <1288092708-5187-10-git-send-email-jbe@pengutronix.de> In-Reply-To: <1288092708-5187-1-git-send-email-jbe@pengutronix.de> References: <1288092708-5187-1-git-send-email-jbe@pengutronix.de> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: barebox-bounces@lists.infradead.org Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: [PATCH 09/12] STM378x: Add video driver for this platform To: barebox@lists.infradead.org Signed-off-by: Juergen Beisert --- arch/arm/mach-stm/include/mach/fb.h | 38 +++ drivers/video/Kconfig | 7 + drivers/video/Makefile | 1 + drivers/video/stm.c | 561 +++++++++++++++++++++++++++++++++++ 4 files changed, 607 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-stm/include/mach/fb.h create mode 100644 drivers/video/stm.c diff --git a/arch/arm/mach-stm/include/mach/fb.h b/arch/arm/mach-stm/include/mach/fb.h new file mode 100644 index 0000000..6596ef2 --- /dev/null +++ b/arch/arm/mach-stm/include/mach/fb.h @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __MACH_FB_H +# define __MACH_FB_H + +#include + +#define STMLCDIF_8BIT 1 /** pixel data bus to the display is of 8 bit width */ +#define STMLCDIF_16BIT 0 /** pixel data bus to the display is of 16 bit width */ +#define STMLCDIF_18BIT 2 /** pixel data bus to the display is of 18 bit width */ +#define STMLCDIF_24BIT 3 /** pixel data bus to the display is of 24 bit width */ + +struct imx_fb_videomode { + struct fb_videomode *mode_list; + unsigned mode_count; + void *framebuffer; /**< force fixed framebuffer address if != NULL */ + unsigned size; /**< force fixed size if != NULL */ + + unsigned dotclk_delay; /**< refer manual HW_LCDIF_VDCTRL4 register */ + unsigned ld_intf_width; /**< refer STMLCDIF_* macros */ +}; + +#endif /* __MACH_FB_H */ + diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 5a5edd2..6cda478 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -45,4 +45,11 @@ config S3C_VIDEO help Add support for the S3C244x LCD controller. +config DRIVER_VIDEO_STM + bool "i.MX23/28 framebuffer driver" + depends on ARCH_STM + help + Say 'Y' here to enable framebuffer and spash screen support for + i.MX23 and i.MX28 based systems. + endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 4287fc8..0ddb81e 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_VIDEO) += fb.o +obj-$(CONFIG_DRIVER_VIDEO_STM) += stm.o obj-$(CONFIG_DRIVER_VIDEO_IMX) += imx.o obj-$(CONFIG_DRIVER_VIDEO_IMX_IPU) += imx-ipu-fb.o obj-$(CONFIG_S3C_VIDEO) += s3c.o diff --git a/drivers/video/stm.c b/drivers/video/stm.c new file mode 100644 index 0000000..5f5d60a --- /dev/null +++ b/drivers/video/stm.c @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2010 Juergen Beisert, Pengutronix + * + * This code is based on: + * Author: Vitaly Wool + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/** + * @file + * @brief LCDIF driver for i.MX23 and i.MX28 (i.MX23 untested yet) + * + * The LCDIF support four modes of operation + * - MPU interface (to drive smart displays) -> not supported yet + * - VSYNC interface (like MPU interface plus Vsync) -> not supported yet + * - Dotclock interface (to drive LC displays with RGB data and sync signals) + * - DVI (to drive ITU-R BT656) -> not supported yet + * + * This driver depends on a correct setup of the pins used for this purpose + * (platform specific). + * + * For the developer: Don't forget to set the data bus width to the display + * in the imx_fb_videomode structure. You will else end up with ugly colours. + * If you fight against jitter you can vary the clock delay. This is a feature + * of the i.MX28 and you can vary it between 2 ns ... 8 ns in 2 ns steps. Give + * the required value in the imx_fb_videomode structure. + */ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HW_LCDIF_CTRL 0x00 +# define CTRL_SFTRST (1 << 31) +# define CTRL_CLKGATE (1 << 30) +# define CTRL_BYPASS_COUNT (1 << 19) +# define CTRL_VSYNC_MODE (1 << 18) +# define CTRL_DOTCLK_MODE (1 << 17) +# define CTRL_DATA_SELECT (1 << 16) +# define SET_BUS_WIDTH(x) (((x) & 0x3) << 10) +# define SET_WORD_LENGTH(x) (((x) & 0x3) << 8) +# define GET_WORD_LENGTH(x) (((x) >> 8) & 0x3) +# define CTRL_MASTER (1 << 5) +# define CTRL_DF16 (1 << 3) +# define CTRL_DF18 (1 << 2) +# define CTRL_DF24 (1 << 1) +# define CTRL_RUN (1 << 0) + +#define HW_LCDIF_CTRL1 0x10 +# define SET_BYTE_PACKAGING(x) (((x) & 0xf) << 16) +# define GET_BYTE_PACKAGING(x) (((x) >> 16) & 0xf) + +#define HW_LCDIF_CTRL2 0x20 + +#define HW_LCDIF_TRANSFER_COUNT 0x30 +# define SET_VCOUNT(x) (((x) & 0xffff) << 16) +# define SET_HCOUNT(x) ((x) & 0xffff) + +#define HW_LCDIF_CUR_BUF 0x40 + +#define HW_LCDIF_NEXT_BUF 0x50 + +#define HW_LCDIF_TIMING 0x60 +# define SET_CMD_HOLD(x) (((x) & 0xff) << 24) +# define SET_CMD_SETUP(x) (((x) & 0xff) << 16) +# define SET_DATA_HOLD(x) (((x) & 0xff) << 8) +# define SET_DATA_SETUP(x) ((x) & 0xff)) + +#define HW_LCDIF_VDCTRL0 0x70 +# define VDCTRL0_ENABLE_PRESENT (1 << 28) +# define VDCTRL0_VSYNC_POL (1 << 27) /* 0 = low active, 1 = high active */ +# define VDCTRL0_HSYNC_POL (1 << 26) /* 0 = low active, 1 = high active */ +# define VDCTRL0_DOTCLK_POL (1 << 25) /* 0 = output at falling edge, capturing at rising edge */ +# define VDCTRL0_ENABLE_POL (1 << 24) /* 0 = low active, 1 = high active */ +# define VDCTRL0_VSYNC_PERIOD_UNIT (1 << 21) +# define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT (1 << 20) +# define VDCTRL0_HALF_LINE (1 << 19) +# define VDCTRL0_HALF_LINE_MODE (1 << 18) +# define SET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff) + +#define HW_LCDIF_VDCTRL1 0x80 + +#define HW_LCDIF_VDCTRL2 0x90 +# define SET_HSYNC_PULSE_WIDTH(x) (((x) & 0x3fff) << 18) +# define SET_HSYNC_PERIOD(x) ((x) & 0x3ffff) + +#define HW_LCDIF_VDCTRL3 0xa0 +# define VDCTRL3_MUX_SYNC_SIGNALS (1 << 29) +# define VDCTRL3_VSYNC_ONLY (1 << 28) +# define SET_HOR_WAIT_CNT(x) (((x) & 0xfff) << 16) +# define SET_VERT_WAIT_CNT(x) ((x) & 0xffff) + +#define HW_LCDIF_VDCTRL4 0xb0 +# define SET_DOTCLK_DLY(x) (((x) & 0x7) << 29) +# define VDCTRL4_SYNC_SIGNALS_ON (1 << 18) +# define SET_DOTCLK_H_VALID_DATA_CNT(x) ((x) & 0x3ffff) + +#define HW_LCDIF_DVICTRL0 0xc0 +#define HW_LCDIF_DVICTRL1 0xd0 +#define HW_LCDIF_DVICTRL2 0xe0 +#define HW_LCDIF_DVICTRL3 0xf0 +#define HW_LCDIF_DVICTRL4 0x100 +#define HW_LCDIF_DATA 0x180 + +#define HW_LCDIF_DEBUG0 0x1d0 +# define DEBUG_HSYNC (1 < 26) +# define DEBUG_VSYNC (1 < 25) + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define TRANSP 3 + +struct imxfb_host { + struct fb_host fb_data; + struct device_d *hw_dev; + void __iomem *base; +}; + +#define fb_info_to_imxfb_host(x) ((struct imxfb_host*)((x)->host)) + +/* the RGB565 true colour mode */ +static const struct fb_bitfield def_rgb565[] = { + [RED] = { + .offset = 11, + .length = 5, + }, + [GREEN] = { + .offset = 5, + .length = 6, + }, + [BLUE] = { + .offset = 0, + .length = 5, + }, + [TRANSP] = { /* no support for transparency */ + .length = 0, + } +}; + +/* the RGB666 true colour mode */ +static const struct fb_bitfield def_rgb666[] = { + [RED] = { + .offset = 16, + .length = 6, + }, + [GREEN] = { + .offset = 8, + .length = 6, + }, + [BLUE] = { + .offset = 0, + .length = 6, + }, + [TRANSP] = { /* no support for transparency */ + .length = 0, + } +}; + +/* the RGB888 true colour mode */ +static const struct fb_bitfield def_rgb888[] = { + [RED] = { + .offset = 16, + .length = 8, + }, + [GREEN] = { + .offset = 8, + .length = 8, + }, + [BLUE] = { + .offset = 0, + .length = 8, + }, + [TRANSP] = { /* no support for transparency */ + .length = 0, + } +}; + +/** + * Just calculate the amount of required bytes per line + * @param ppl Used pixel per line + * @param bpp Bits per pixel + * @return Byte count + */ +static inline unsigned calc_line_length(unsigned ppl, unsigned bpp) +{ + return (ppl * bpp) >> 3; +} + +/** + * Prepare the video hardware for a specified video mode + * @param fb_info Framebuffer information + * @param mode The video mode description to initialize + * @return 0 on success + * + * Dotclock mode: + * One line of pixels or one frame in the i.MX28 is defined to: + * @verbatim + * |<---------------------- one line period -------------------------------->| + * |<- HSync length ->| + * |<----- Start of line --->| + * |<-------- active line data ------>| + * + * |<------------------------ frame period --------------------------------->| + * |<- VSync length ->| + * |<--- Start of 1. line -->| + * |<---------- active lines -------->| + * @endverbatim + * Based on the values from struct fb_videomode: + * - "one line period" = left_margin + xres + right_margin + hsync_len + * - "HSync length" = hsync_len + * - "Start of line" = hsync_len + left_margin + * - "active line data" = xres + */ +static int stmfb_initialize_mode(struct fb_info *fb_info, + const struct fb_videomode *mode) +{ + struct imxfb_host *fbh = fb_info_to_imxfb_host(fb_info); + struct imx_fb_videomode *pdata = fbh->hw_dev->platform_data; + uint32_t reg; + unsigned size; + + pr_debug("%s called\n", __func__); + /* + * we need at least this amount of memory for the framebuffer + */ + size = calc_line_length(mode->xres, fb_info->bits_per_pixel) * mode->yres; + if (fb_info->fb_dev->size != 0) { + if (size > fb_info->fb_dev->size) { + pr_err("Cannot initialize video mode '%s': Its too large. " + "Required bytes are %u, available only %u\n", + mode->name, size, fb_info->fb_dev->size); + return -EINVAL; + } + } else + fb_info->fb_dev->size = size; + + /* + * if no framebuffer memory was specified yet, allocate one, + * and allocate more memory, on user request + */ + if (fb_info->fb_dev->map_base == 0U) + fb_info->fb_dev->map_base = (resource_size_t)xzalloc(fb_info->fb_dev->size); + + /* TODO HCLK must be active at this point of time! */ + + size = imx_set_lcdifclk(PICOS2KHZ(mode->pixclock)); + if (size == 0) { + pr_debug("Unable to set a valid pixel clock\n"); + return -EINVAL; + } + + /* + * bring the controller out of reset and configure it into DOTCLOCK mode + */ + reg = CTRL_BYPASS_COUNT | /* always in DOTCLOCK mode */ + CTRL_DOTCLK_MODE; + writel(reg, fbh->base + HW_LCDIF_CTRL); + + /* master mode only */ + reg |= CTRL_MASTER; + + /* + * Configure videomode and interface mode + */ + reg |= SET_BUS_WIDTH(pdata->ld_intf_width); + switch (fb_info->bits_per_pixel) { + case 8: + reg |= SET_WORD_LENGTH(1); + /* TODO refer manual page 2046 */ + pr_warning("8 bpp mode not supported yet\n"); + break; + case 16: + pr_debug("Setting up an RGB565 mode\n"); + reg |= SET_WORD_LENGTH(0) | CTRL_DF16; /* we assume RGB565 */ + writel(SET_BYTE_PACKAGING(0xf), fbh->base + HW_LCDIF_CTRL1); + fb_info->red = def_rgb565[RED]; + fb_info->green = def_rgb565[GREEN]; + fb_info->blue = def_rgb565[BLUE]; + fb_info->transp = def_rgb565[TRANSP]; + break; + case 24: + case 32: + pr_debug("Setting up an RGB888/666 mode\n"); + reg |= SET_WORD_LENGTH(3); + switch (pdata->ld_intf_width) { + case STMLCDIF_8BIT: + pr_debug("Unsupported LCD bus width mapping\n"); + break; + case STMLCDIF_16BIT: + case STMLCDIF_18BIT: + /* 24 bit to 18 bit mapping */ + reg |= CTRL_DF24; /* ignore the upper 2 bits in each colour component */ + fb_info->red = def_rgb666[RED]; + fb_info->green = def_rgb666[GREEN]; + fb_info->blue = def_rgb666[BLUE]; + fb_info->transp = def_rgb666[TRANSP]; + break; + case STMLCDIF_24BIT: + /* real 24 bit */ + fb_info->red = def_rgb888[RED]; + fb_info->green = def_rgb888[GREEN]; + fb_info->blue = def_rgb888[BLUE]; + fb_info->transp = def_rgb888[TRANSP]; + break; + } + /* do not use packed pixels = one pixel per word instead */ + writel(SET_BYTE_PACKAGING(0x7), fbh->base + HW_LCDIF_CTRL1); + break; + default: + pr_debug("Unhandled colour depth of %u\n", fb_info->bits_per_pixel); + return -EINVAL; + } + writel(reg, fbh->base + HW_LCDIF_CTRL); + pr_debug("Setting up CTRL to %08X\n", reg); + + writel(SET_VCOUNT(mode->yres) | + SET_HCOUNT(mode->xres), fbh->base + HW_LCDIF_TRANSFER_COUNT); + + reg = VDCTRL0_ENABLE_PRESENT | /* always in DOTCLOCK mode */ + VDCTRL0_VSYNC_PERIOD_UNIT | + VDCTRL0_VSYNC_PULSE_WIDTH_UNIT; + if (mode->sync & FB_SYNC_HOR_HIGH_ACT) + reg |= VDCTRL0_HSYNC_POL; + if (mode->sync & FB_SYNC_VERT_HIGH_ACT) + reg |= VDCTRL0_VSYNC_POL; + if (mode->sync & FB_SYNC_DE_HIGH_ACT) + reg |= VDCTRL0_ENABLE_POL; + if (mode->sync & FB_SYNC_CLK_INVERT) + reg |= VDCTRL0_DOTCLK_POL; + + reg |= SET_VSYNC_PULSE_WIDTH(mode->vsync_len); + writel(reg, fbh->base + HW_LCDIF_VDCTRL0); + pr_debug("Setting up VDCTRL0 to %08X\n", reg); + + /* frame length in lines */ + writel(mode->upper_margin + mode->vsync_len + mode->lower_margin + mode->yres, + fbh->base + HW_LCDIF_VDCTRL1); + + /* line length in units of clocks or pixels */ + writel(SET_HSYNC_PULSE_WIDTH(mode->hsync_len) | + SET_HSYNC_PERIOD(mode->left_margin + mode->hsync_len + mode->right_margin + mode->xres), + fbh->base + HW_LCDIF_VDCTRL2); + + writel(SET_HOR_WAIT_CNT(mode->left_margin + mode->hsync_len) | + SET_VERT_WAIT_CNT(mode->upper_margin + mode->vsync_len), + fbh->base + HW_LCDIF_VDCTRL3); + + writel(SET_DOTCLK_DLY(pdata->dotclk_delay) | + SET_DOTCLK_H_VALID_DATA_CNT(mode->xres), + fbh->base + HW_LCDIF_VDCTRL4); + + writel(fb_info->fb_dev->map_base, fbh->base + HW_LCDIF_CUR_BUF); + /* always show one framebuffer only */ + writel(fb_info->fb_dev->map_base, fbh->base + HW_LCDIF_NEXT_BUF); + + return 0; +} + +/** + * @param fb_info Framebuffer information + */ +static void stmfb_enable_controller(struct fb_info *fb_info) +{ + struct imxfb_host *fbh = fb_info_to_imxfb_host(fb_info); + uint32_t reg, last_reg; + unsigned loop, edges; + + pr_debug("%s called\n", __func__); + + /* if it was disabled, re-enable the mode again */ + reg = readl(fbh->base + HW_LCDIF_CTRL); + reg |= CTRL_DOTCLK_MODE; + writel(reg, fbh->base + HW_LCDIF_CTRL); + + /* enable the SYNC signals first, then the DMA enginge */ + reg = readl(fbh->base + HW_LCDIF_VDCTRL4); + reg |= VDCTRL4_SYNC_SIGNALS_ON; + writel(reg, fbh->base + HW_LCDIF_VDCTRL4); + + /* + * Give the attached LC display or monitor a chance to sync into + * our signals. + * Wait for at least 2 VSYNCs = four VSYNC edges + */ + edges = 4; + + while (edges != 0) { + loop = 800; + last_reg = readl(fbh->base + HW_LCDIF_DEBUG0) & DEBUG_VSYNC; + do { + reg = readl(fbh->base + HW_LCDIF_DEBUG0) & DEBUG_VSYNC; + if (reg != last_reg); + break; + last_reg = reg; + loop--; + } while (loop != 0); + edges--; + } + + reg = readl(fbh->base + HW_LCDIF_CTRL); + reg |= CTRL_RUN; + writel(reg, fbh->base + HW_LCDIF_CTRL); +} + +/** + * @param fb_info Framebuffer information + */ +static void stmfb_disable_controller(struct fb_info *fb_info) +{ + struct imxfb_host *fbh = fb_info_to_imxfb_host(fb_info); + unsigned loop; + uint32_t reg; + + pr_debug("%s called\n", __func__); + /* + * Even if we disable the controller here, it will still continue + * until its FIFOs are running out of data + */ + reg = readl(fbh->base + HW_LCDIF_CTRL); + reg &= ~CTRL_DOTCLK_MODE; + writel(reg, fbh->base + HW_LCDIF_CTRL); + + loop = 1000; + while (loop) { + reg = readl(fbh->base + HW_LCDIF_CTRL); + if (!(reg & CTRL_RUN)) + break; + loop--; + } + + reg = readl(fbh->base + HW_LCDIF_VDCTRL4); + reg &= ~VDCTRL4_SYNC_SIGNALS_ON; + writel(reg, fbh->base + HW_LCDIF_VDCTRL4); +} + +/** + * Print some information about the current hardware state + * @param hw_dev STM video device + */ +static void stmfb_info(struct device_d *hw_dev) +{ + uint32_t reg; + + printf(" STM video hardware:\n"); + printf(" Pixel clock: %u kHz\n", imx_get_lcdifclk()); + + reg = readl(hw_dev->map_base + HW_LCDIF_CTRL); + switch (GET_WORD_LENGTH(reg)) { + case 0: + printf(" 16 bpp mode with %s colour scheme\n", + reg & CTRL_DF16 ? "RGB565" : "ARGB1555"); + switch (GET_BYTE_PACKAGING(readl(hw_dev->map_base + HW_LCDIF_CTRL1))) { + case 0xf: + printf(" One pixel per halfword\n"); + break; + case 0x3: + printf(" One pixel per word\n"); + break; + default: + printf("Unknown pixel packaging: %u\n", + GET_BYTE_PACKAGING(readl(hw_dev->map_base + HW_LCDIF_CTRL1))); + } + break; + case 1: + case 2: + printf("Unsupported bpp mode yet!\n"); + break; + case 3: + printf(" 24 bpp mode with %s colour scheme\n", + reg & CTRL_DF24 ? "RGB888" : "RGB666"); + switch (GET_BYTE_PACKAGING(readl(hw_dev->map_base + HW_LCDIF_CTRL1))) { + case 0x7: + printf(" One pixel per word (xRGB xRGB xRGB ...)\n"); + break; + case 0xf: + printf("Packed pixel format per word (RGBR GBRG BRGB ...)\n"); + break; + default: + printf("Unknown pixel packaging: %u\n", + GET_BYTE_PACKAGING(readl(hw_dev->map_base + HW_LCDIF_CTRL1))); + } + break; + } +} + +/* + * There is only one video hardware instance available. + * It makes no sense to dynamically allocate this data + */ +static struct imxfb_host host_data = { + .fb_data.fb_mode = stmfb_initialize_mode, + .fb_data.fb_enable = stmfb_enable_controller, + .fb_data.fb_disable = stmfb_disable_controller, + .fb_data.bits_per_pixel = 16, +}; + +static int stmfb_probe(struct device_d *hw_dev) +{ + struct imx_fb_videomode *pdata = hw_dev->platform_data; + struct device_d *fb_dev; + + if (pdata == NULL) { + pr_debug("STMFB: No platformdata. Giving up\n"); + return -ENODEV; + } + + /* add runtime hardware info */ + host_data.hw_dev = hw_dev; + host_data.base = (void*)hw_dev->map_base; + + /* add runtime video info */ + host_data.fb_data.mode = pdata->mode_list; + host_data.fb_data.mode_cnt = pdata->mode_count; + + fb_dev = register_framebuffer(&host_data.fb_data, pdata->framebuffer, + pdata->size); + if (fb_dev == NULL) { + pr_err("STMFB: Failed to register framebuffer\n"); + return -EINVAL; + } + + return 0; +} + +static struct driver_d stmfb_driver = { + .name = "stmfb", + .probe = stmfb_probe, + .info = stmfb_info, +}; + +static int stmfb_init(void) +{ + return register_driver(&stmfb_driver); +} + +device_initcall(stmfb_init); -- 1.7.2.3 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox