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 1PJQR6-0001eI-BM for barebox@lists.infradead.org; Fri, 19 Nov 2010 12:51:34 +0000 From: Juergen Beisert Date: Fri, 19 Nov 2010 13:51:02 +0100 Message-Id: <1290171063-28870-11-git-send-email-jbe@pengutronix.de> In-Reply-To: <1290171063-28870-1-git-send-email-jbe@pengutronix.de> References: <1290171063-28870-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 10/11] Add a video driver for S3C2440 bases platforms To: barebox@lists.infradead.org Cc: Juergen Beisert From: Juergen Beisert Signed-off-by: Juergen Beisert --- arch/arm/mach-s3c24xx/include/mach/fb.h | 40 +++ drivers/video/Kconfig | 6 + drivers/video/Makefile | 1 + drivers/video/s3c.c | 474 +++++++++++++++++++++++++++++++ 4 files changed, 521 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-s3c24xx/include/mach/fb.h create mode 100644 drivers/video/s3c.c diff --git a/arch/arm/mach-s3c24xx/include/mach/fb.h b/arch/arm/mach-s3c24xx/include/mach/fb.h new file mode 100644 index 0000000..eec6164 --- /dev/null +++ b/arch/arm/mach-s3c24xx/include/mach/fb.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Juergen Beisert + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#ifndef __MACH_FB_H_ +# define __MACH_FB_H_ + +#include + +struct s3c_fb_platform_data { + + const struct fb_videomode *mode_list; + unsigned mode_cnt; + + int passive_display; /**< enable support for STN or CSTN displays */ + + void *framebuffer; /**< force framebuffer base address */ + unsigned size; /**< force framebuffer size */ + + /** hook to enable backlight and stuff */ + void (*enable)(int); +}; + +#endif /* __MACH_FB_H_ */ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index b600444..f43c100 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -30,4 +30,10 @@ config DRIVER_VIDEO_IMX_IPU Add support for the IPU framebuffer device found on i.MX31 and i.MX35 CPUs. +config S3C_VIDEO + bool "S3C244x framebuffer driver" + depends on ARCH_S3C24xx + help + Add support for the S3C244x LCD controller. + endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 179f0c4..4287fc8 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_VIDEO) += fb.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/s3c.c b/drivers/video/s3c.c new file mode 100644 index 0000000..c831ebd --- /dev/null +++ b/drivers/video/s3c.c @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2010 Juergen Beisert + * + * This driver is based on a patch found in the web: + * (C) Copyright 2006 by OpenMoko, Inc. + * Author: Harald Welte + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LCDCON1 0x00 +# define PNRMODE(x) (((x) & 3) << 5) +# define BPPMODE(x) (((x) & 0xf) << 1) +# define SET_CLKVAL(x) (((x) & 0x3ff) << 8) +# define GET_CLKVAL(x) (((x) >> 8) & 0x3ff) +# define ENVID (1 << 0) + +#define LCDCON2 0x04 +# define SET_VBPD(x) (((x) & 0xff) << 24) +# define SET_LINEVAL(x) (((x) & 0x3ff) << 14) +# define SET_VFPD(x) (((x) & 0xff) << 6) +# define SET_VSPW(x) ((x) & 0x3f) + +#define LCDCON3 0x08 +# define SET_HBPD(x) (((x) & 0x7f) << 19) +# define SET_HOZVAL(x) (((x) & 0x7ff) << 8) +# define SET_HFPD(x) ((x) & 0xff) + +#define LCDCON4 0x0c +# define SET_HSPW(x) ((x) & 0xff) + +#define LCDCON5 0x10 +# define BPP24BL (1 << 12) +# define FRM565 (1 << 11) +# define INV_CLK (1 << 10) +# define INV_HS (1 << 9) +# define INV_VS (1 << 8) +# define INV_DTA (1 << 7) +# define INV_DE (1 << 6) +# define INV_PWREN (1 << 5) +# define INV_LEND (1 << 4) /* FIXME */ +# define ENA_PWREN (1 << 3) +# define ENA_LEND (1 << 2) /* FIXME */ +# define BSWP (1 << 1) +# define HWSWP (1 << 0) + +#define LCDSADDR1 0x14 +# define SET_LCDBANK(x) (((x) & 0x1ff) << 21) +# define GET_LCDBANK(x) (((x) >> 21) & 0x1ff) +# define SET_LCDBASEU(x) ((x) & 0x1fffff) +# define GET_LCDBASEU(x) ((x) & 0x1fffff) + +#define LCDSADDR2 0x18 +# define SET_LCDBASEL(x) ((x) & 0x1fffff) +# define GET_LCDBASEL(x) ((x) & 0x1fffff) + +#define LCDSADDR3 0x1c +# define SET_OFFSIZE(x) (((x) & 0x7ff) << 11) +# define GET_OFFSIZE(x) (((x) >> 11) & 0x7ff) +# define SET_PAGE_WIDTH(x) ((x) & 0x3ff) +# define GET_PAGE_WIDTH(x) ((x) & 0x3ff) + +#define RED_LUT 0x20 +#define GREEN_LUT 0x24 +#define BLUE_LUT 0x28 + +#define DITHMODE 0x4c + +#define TPAL 0x50 + +#define LCDINTPND 0x54 + +#define LCDSRCPND 0x58 + +#define LCDINTMSK 0x5c +# define FIWSEL (1 << 2) + +#define TCONSEL 0x60 + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define TRANSP 3 + +struct s3cfb_host { + struct fb_host fb_data; + struct device_d *hw_dev; + struct s3c_fb_platform_data *pdata; /* shortcut to hw_dev->platform_data */ + void __iomem *base; +}; + +#define fb_info_to_s3cfb_host(x) ((struct s3cfb_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 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, + } +}; + +/** + * @param fb_info Framebuffer information + */ +static void s3cfb_enable_controller(struct fb_info *fb_info) +{ + struct s3cfb_host *fbh = fb_info_to_s3cfb_host(fb_info); + struct s3c_fb_platform_data *pdata = fbh->pdata; + uint32_t con1; + + con1 = readl(fbh->base + LCDCON1); + + con1 |= ENVID; + + writel(con1, fbh->base + LCDCON1); + + if (pdata->enable) + pdata->enable(1); +} + +/** + * @param fb_info Framebuffer information + */ +static void s3cfb_disable_controller(struct fb_info *fb_info) +{ + struct s3cfb_host *fbh = fb_info_to_s3cfb_host(fb_info); + struct s3c_fb_platform_data *pdata = fbh->pdata; + uint32_t con1; + + if (pdata->enable) + pdata->enable(0); + + con1 = readl(fbh->base + LCDCON1); + + con1 &= ~ENVID; + + writel(con1, fbh->base + LCDCON1); +} + +/** + * Framebuffer memory management + * @param fb_info Framebuffer information + * @param size Requested framebuffer size in bytes + * @return 0 on success + * + * If the user forces a fixed memory location, try to map the framebuffer into + * it. If it does not match, report an error. If no fixed memory location + * exists do memory allocation in a dynamic manner. + */ +static int s3cfb_memory_mmgt(struct fb_info *fb_info, unsigned size) +{ + struct s3cfb_host *fbh = fb_info_to_s3cfb_host(fb_info); + struct s3c_fb_platform_data *pdata = fbh->pdata; + + if (pdata->framebuffer != NULL) { + /* fixed memory location */ + if (pdata->size < size) + return -EINVAL; + fb_info->fb_dev.map_base = (resource_size_t)pdata->framebuffer; + fb_info->fb_dev.size = size; + } else { + /* dynamic memory location */ + if ((fb_info->fb_dev.size < size) && + (fb_info->fb_dev.size != 0)) { + free((void*)fb_info->fb_dev.map_base); + fb_info->fb_dev.map_base = 0; + fb_info->fb_dev.size = 0; + } + if (fb_info->fb_dev.size == 0) { + /* allocate more space on platform request */ + if (pdata->size > size) + size = pdata->size; + fb_info->fb_dev.map_base = + (resource_size_t)xzalloc(size); + fb_info->fb_dev.size = size; + } + } + + return 0; +} + +/** + * Prepare the video hardware for a specified video mode + * @param fb_info Framebuffer information + * @param mode The video mode description to setup + * @return 0 on success + */ +static int s3cfb_initialize_mode(struct fb_info *fb_info, + const struct fb_videomode *mode) +{ + struct s3cfb_host *fbh = fb_info_to_s3cfb_host(fb_info); + struct s3c_fb_platform_data *pdata = fbh->pdata; + unsigned size, hclk, div; + int rc; + uint32_t con1, con2, con3, con4, con5 = 0; + + if (pdata->passive_display != 0) { + dev_err(fbh->hw_dev, + "Passive displays are currently not supported\n"); + return -EINVAL; + } + + /* + * we need at least this amount of memory for the framebuffer + */ + size = mode->xres * mode->yres * (fb_info->bits_per_pixel >> 3); + + rc = s3cfb_memory_mmgt(fb_info, size); + if (rc != 0) { + dev_err(fbh->hw_dev, "Cannot allocate framebuffer memory\n"); + return rc; + } + + /* its useful to enable the unit's clock */ + s3c244x_mod_clock(CLK_LCDC, 1); + + /* ensure video output is _off_ */ + writel(0x00000000, fbh->base + LCDCON1); + + hclk = s3c24xx_get_hclk() / 1000U; /* hclk in kHz */ + div = hclk / PICOS2KHZ(mode->pixclock); + if (div < 3) + div = 3; + /* pixel clock is: (hclk) / ((div + 1) * 2) */ + div += 1; + div >>= 1; + div -= 1; + + con1 = PNRMODE(3) | SET_CLKVAL(div); /* PNRMODE=3 is TFT */ + + switch (fb_info->bits_per_pixel) { +#if 0 + /** @todo S3C2440: Add CLUT based videomodes */ + case 1: + con1 |= BPPMODE(8); + break; + case 2: + con1 |= BPPMODE(9); + break; + case 4: + con1 |= BPPMODE(10); + break; + case 8: + con1 |= BPPMODE(11); + break; +#endif + case 16: + con1 |= BPPMODE(12); + con5 |= FRM565; + 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: + con1 |= BPPMODE(13); + /** @todo S3C2440: Define the correct byte order + * for 24 bpp in memory (BPP24BL) + */ + con5 |= BPP24BL; + 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; + default: + dev_err(fbh->hw_dev, "Invalid bits per pixel value: %u\n", + fb_info->bits_per_pixel); + return -EINVAL; + } + + /* 'normal' in register description means positive logic */ + if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT)) + con5 |= INV_HS; + if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT)) + con5 |= INV_VS; + if (!(mode->sync & FB_SYNC_DE_HIGH_ACT)) + con5 |= INV_DE; + if (mode->sync & FB_SYNC_CLK_INVERT) + con5 |= INV_CLK; /* display should latch at the rising edge */ + if (mode->sync & FB_SYNC_SWAP_RGB) + con5 |= HWSWP; + + /** @todo S3C2440: LCD power enable via platform data */ + + /* vertical timing */ + con2 = SET_VBPD(mode->upper_margin - 1) | + SET_LINEVAL(mode->yres - 1) | + SET_VFPD(mode->lower_margin - 1) | + SET_VSPW(mode->vsync_len - 1); + + /* horizontal timing */ + con3 = SET_HBPD(mode->left_margin - 1) | + SET_HOZVAL(mode->xres - 1) | + SET_HFPD(mode->right_margin - 1); + con4 = SET_HSPW(mode->hsync_len - 1); + + /* basic timing setup */ + writel(con1, fbh->base + LCDCON1); + writel(con2, fbh->base + LCDCON2); + writel(con3, fbh->base + LCDCON3); + writel(con4, fbh->base + LCDCON4); + writel(con5, fbh->base + LCDCON5); + + /* framebuffer memory setup */ + writel(((uint32_t)fb_info->fb_dev.map_base) >> 1, + fbh->base + LCDSADDR1); + + writel(SET_LCDBASEL(((uint32_t)fb_info->fb_dev.map_base + size) >> 1), + fbh->base + LCDSADDR2); + + writel(SET_OFFSIZE(0) | + SET_PAGE_WIDTH((mode->xres * fb_info->bits_per_pixel) >> 4), + fbh->base + LCDSADDR3); + + writel(FIWSEL, fbh->base + LCDINTMSK); + + return 0; +} + +/** + * Print some information about the current hardware state + * @param hw_dev S3C video device + */ +#ifdef CONFIG_VIDEO_INFO_VERBOSE +static void s3cfb_info(struct device_d *hw_dev) +{ + uint32_t con1, addr1, addr2, addr3; + + con1 = readl(hw_dev->map_base + LCDCON1); + addr1 = readl(hw_dev->map_base + LCDSADDR1); + addr2 = readl(hw_dev->map_base + LCDSADDR2); + addr3 = readl(hw_dev->map_base + LCDSADDR3); + + printf(" Video hardware info:\n"); + printf(" Video clock is running at %u Hz\n", + s3c24xx_get_hclk() / ((GET_CLKVAL(con1) + 1) * 2)); + printf(" Video memory bank starts at 0x%08X\n", + GET_LCDBANK(addr1) << 22); + printf(" Video memory bank offset: 0x%08X\n", + GET_LCDBASEU(addr1)); + printf(" Video memory end: 0x%08X\n", + GET_LCDBASEU(addr2)); + printf(" Virtual screen offset size: %u half words\n", + GET_OFFSIZE(addr3)); + printf(" Virtual screen page width: %u half words\n", + GET_PAGE_WIDTH(addr3)); +} +#endif + +/* + * There is only one video hardware instance available. + * It makes no sense to dynamically allocate this data + */ +static struct s3cfb_host host_data = { + .fb_data.fb_mode = s3cfb_initialize_mode, + .fb_data.fb_enable = s3cfb_enable_controller, + .fb_data.fb_disable = s3cfb_disable_controller, +}; + +static int s3cfb_probe(struct device_d *hw_dev) +{ + struct s3c_fb_platform_data *pdata = hw_dev->platform_data; + struct device_d *fb_dev; + + if (pdata == NULL) { + dev_err(hw_dev, "Framebuffer driver missing platform data"); + return -ENODEV; + } + + /* enable unit's clock */ + s3c244x_mod_clock(CLK_LCDC, 1); + + writel(0, hw_dev->map_base + LCDCON1); + + /** @todo S3C2440: LCD power disable via platform data */ + writel(0, hw_dev->map_base + LCDCON5); + + /* disable it again until user request */ + s3c244x_mod_clock(CLK_LCDC, 0); + + /* 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_cnt; + + fb_dev = register_framebuffer(&host_data.fb_data); + if (fb_dev == NULL) { + dev_err(hw_dev, "Failed to register framebuffer\n"); + return -EINVAL; + } + + return 0; +} + +static struct driver_d s3cfb_driver = { + .name = "s3cfb", + .probe = s3cfb_probe, +#ifdef CONFIG_VIDEO_INFO_VERBOSE + .info = s3cfb_info, +#endif +}; + +static int s3cfb_init(void) +{ + return register_driver(&s3cfb_driver); +} + +device_initcall(s3cfb_init); + +/** + * The S3C244x LCD controller supports passive (CSTN/STN) and active (TFT) + * LC displays + * + * The driver itself currently supports only active TFT LC displays in the + * follwing manner: + * + * * True colours + * - 16 bpp + * - 24 bpp + */ -- 1.7.2.3 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox