diff options
Diffstat (limited to 'target/linux/xburst/patches-2.6.35/051-fb.patch')
-rw-r--r-- | target/linux/xburst/patches-2.6.35/051-fb.patch | 971 |
1 files changed, 971 insertions, 0 deletions
diff --git a/target/linux/xburst/patches-2.6.35/051-fb.patch b/target/linux/xburst/patches-2.6.35/051-fb.patch new file mode 100644 index 0000000000..e021abf057 --- /dev/null +++ b/target/linux/xburst/patches-2.6.35/051-fb.patch @@ -0,0 +1,971 @@ +From 91ead9db8aabb54f4867e5a7ed4782dcca2273f5 Mon Sep 17 00:00:00 2001 +From: Lars-Peter Clausen <lars@metafoo.de> +Date: Sat, 17 Jul 2010 11:14:34 +0000 +Subject: [PATCH] FBDEV: JZ4740: Add framebuffer driver + +Add support for the LCD controller on JZ4740 SoCs. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +Cc: Andrew Morton <akpm@linux-foundation.org> +Cc: linux-fbdev@vger.kernel.org +Cc: linux-mips@linux-mips.org +Cc: linux-kernel@vger.kernel.org +Patchwork: https://patchwork.linux-mips.org/patch/1470/ +Signed-off-by: Ralf Baechle <ralf@linux-mips.org> +--- + arch/mips/include/asm/mach-jz4740/jz4740_fb.h | 67 ++ + drivers/video/Kconfig | 9 + + drivers/video/Makefile | 1 + + drivers/video/jz4740_fb.c | 847 +++++++++++++++++++++++++ + 4 files changed, 924 insertions(+), 0 deletions(-) + create mode 100644 arch/mips/include/asm/mach-jz4740/jz4740_fb.h + create mode 100644 drivers/video/jz4740_fb.c + +--- /dev/null ++++ b/arch/mips/include/asm/mach-jz4740/jz4740_fb.h +@@ -0,0 +1,67 @@ ++/* ++ * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de> ++ * ++ * 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. ++ * ++ * 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., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef __ASM_MACH_JZ4740_JZ4740_FB_H__ ++#define __ASM_MACH_JZ4740_JZ4740_FB_H__ ++ ++#include <linux/fb.h> ++ ++enum jz4740_fb_lcd_type { ++ JZ_LCD_TYPE_GENERIC_16_BIT = 0, ++ JZ_LCD_TYPE_GENERIC_18_BIT = 0 | (1 << 4), ++ JZ_LCD_TYPE_SPECIAL_TFT_1 = 1, ++ JZ_LCD_TYPE_SPECIAL_TFT_2 = 2, ++ JZ_LCD_TYPE_SPECIAL_TFT_3 = 3, ++ JZ_LCD_TYPE_NON_INTERLACED_CCIR656 = 5, ++ JZ_LCD_TYPE_INTERLACED_CCIR656 = 7, ++ JZ_LCD_TYPE_SINGLE_COLOR_STN = 8, ++ JZ_LCD_TYPE_SINGLE_MONOCHROME_STN = 9, ++ JZ_LCD_TYPE_DUAL_COLOR_STN = 10, ++ JZ_LCD_TYPE_DUAL_MONOCHROME_STN = 11, ++ JZ_LCD_TYPE_8BIT_SERIAL = 12, ++}; ++ ++#define JZ4740_FB_SPECIAL_TFT_CONFIG(start, stop) (((start) << 16) | (stop)) ++ ++/* ++* width: width of the lcd display in mm ++* height: height of the lcd display in mm ++* num_modes: size of modes ++* modes: list of valid video modes ++* bpp: bits per pixel for the lcd ++* lcd_type: lcd type ++*/ ++ ++struct jz4740_fb_platform_data { ++ unsigned int width; ++ unsigned int height; ++ ++ size_t num_modes; ++ struct fb_videomode *modes; ++ ++ unsigned int bpp; ++ enum jz4740_fb_lcd_type lcd_type; ++ ++ struct { ++ uint32_t spl; ++ uint32_t cls; ++ uint32_t ps; ++ uint32_t rev; ++ } special_tft_config; ++ ++ unsigned pixclk_falling_edge:1; ++ unsigned date_enable_active_low:1; ++}; ++ ++#endif +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -2229,6 +2229,15 @@ config FB_BROADSHEET + and could also have been called by other names when coupled with + a bridge adapter. + ++config FB_JZ4740 ++ tristate "JZ4740 LCD framebuffer support" ++ depends on FB ++ select FB_SYS_FILLRECT ++ select FB_SYS_COPYAREA ++ select FB_SYS_IMAGEBLIT ++ help ++ Framebuffer support for the JZ4740 SoC. ++ + source "drivers/video/omap/Kconfig" + source "drivers/video/omap2/Kconfig" + +--- a/drivers/video/Makefile ++++ b/drivers/video/Makefile +@@ -131,6 +131,7 @@ obj-$(CONFIG_FB_CARMINE) += car + obj-$(CONFIG_FB_MB862XX) += mb862xx/ + obj-$(CONFIG_FB_MSM) += msm/ + obj-$(CONFIG_FB_NUC900) += nuc900fb.o ++obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o + + # Platform or fallback drivers go here + obj-$(CONFIG_FB_UVESA) += uvesafb.o +--- /dev/null ++++ b/drivers/video/jz4740_fb.c +@@ -0,0 +1,847 @@ ++/* ++ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> ++ * JZ4740 SoC LCD framebuffer driver ++ * ++ * 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. ++ * ++ * 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., ++ * 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/platform_device.h> ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++ ++#include <linux/console.h> ++#include <linux/fb.h> ++ ++#include <linux/dma-mapping.h> ++ ++#include <asm/mach-jz4740/jz4740_fb.h> ++#include <asm/mach-jz4740/gpio.h> ++ ++#define JZ_REG_LCD_CFG 0x00 ++#define JZ_REG_LCD_VSYNC 0x04 ++#define JZ_REG_LCD_HSYNC 0x08 ++#define JZ_REG_LCD_VAT 0x0C ++#define JZ_REG_LCD_DAH 0x10 ++#define JZ_REG_LCD_DAV 0x14 ++#define JZ_REG_LCD_PS 0x18 ++#define JZ_REG_LCD_CLS 0x1C ++#define JZ_REG_LCD_SPL 0x20 ++#define JZ_REG_LCD_REV 0x24 ++#define JZ_REG_LCD_CTRL 0x30 ++#define JZ_REG_LCD_STATE 0x34 ++#define JZ_REG_LCD_IID 0x38 ++#define JZ_REG_LCD_DA0 0x40 ++#define JZ_REG_LCD_SA0 0x44 ++#define JZ_REG_LCD_FID0 0x48 ++#define JZ_REG_LCD_CMD0 0x4C ++#define JZ_REG_LCD_DA1 0x50 ++#define JZ_REG_LCD_SA1 0x54 ++#define JZ_REG_LCD_FID1 0x58 ++#define JZ_REG_LCD_CMD1 0x5C ++ ++#define JZ_LCD_CFG_SLCD BIT(31) ++#define JZ_LCD_CFG_PS_DISABLE BIT(23) ++#define JZ_LCD_CFG_CLS_DISABLE BIT(22) ++#define JZ_LCD_CFG_SPL_DISABLE BIT(21) ++#define JZ_LCD_CFG_REV_DISABLE BIT(20) ++#define JZ_LCD_CFG_HSYNCM BIT(19) ++#define JZ_LCD_CFG_PCLKM BIT(18) ++#define JZ_LCD_CFG_INV BIT(17) ++#define JZ_LCD_CFG_SYNC_DIR BIT(16) ++#define JZ_LCD_CFG_PS_POLARITY BIT(15) ++#define JZ_LCD_CFG_CLS_POLARITY BIT(14) ++#define JZ_LCD_CFG_SPL_POLARITY BIT(13) ++#define JZ_LCD_CFG_REV_POLARITY BIT(12) ++#define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11) ++#define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10) ++#define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9) ++#define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8) ++#define JZ_LCD_CFG_18_BIT BIT(7) ++#define JZ_LCD_CFG_PDW (BIT(5) | BIT(4)) ++#define JZ_LCD_CFG_MODE_MASK 0xf ++ ++#define JZ_LCD_CTRL_BURST_4 (0x0 << 28) ++#define JZ_LCD_CTRL_BURST_8 (0x1 << 28) ++#define JZ_LCD_CTRL_BURST_16 (0x2 << 28) ++#define JZ_LCD_CTRL_RGB555 BIT(27) ++#define JZ_LCD_CTRL_OFUP BIT(26) ++#define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24) ++#define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24) ++#define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24) ++#define JZ_LCD_CTRL_PDD_MASK (0xff << 16) ++#define JZ_LCD_CTRL_EOF_IRQ BIT(13) ++#define JZ_LCD_CTRL_SOF_IRQ BIT(12) ++#define JZ_LCD_CTRL_OFU_IRQ BIT(11) ++#define JZ_LCD_CTRL_IFU0_IRQ BIT(10) ++#define JZ_LCD_CTRL_IFU1_IRQ BIT(9) ++#define JZ_LCD_CTRL_DD_IRQ BIT(8) ++#define JZ_LCD_CTRL_QDD_IRQ BIT(7) ++#define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6) ++#define JZ_LCD_CTRL_LSB_FISRT BIT(5) ++#define JZ_LCD_CTRL_DISABLE BIT(4) ++#define JZ_LCD_CTRL_ENABLE BIT(3) ++#define JZ_LCD_CTRL_BPP_1 0x0 ++#define JZ_LCD_CTRL_BPP_2 0x1 ++#define JZ_LCD_CTRL_BPP_4 0x2 ++#define JZ_LCD_CTRL_BPP_8 0x3 ++#define JZ_LCD_CTRL_BPP_15_16 0x4 ++#define JZ_LCD_CTRL_BPP_18_24 0x5 ++ ++#define JZ_LCD_CMD_SOF_IRQ BIT(15) ++#define JZ_LCD_CMD_EOF_IRQ BIT(16) ++#define JZ_LCD_CMD_ENABLE_PAL BIT(12) ++ ++#define JZ_LCD_SYNC_MASK 0x3ff ++ ++#define JZ_LCD_STATE_DISABLED BIT(0) ++ ++struct jzfb_framedesc { ++ uint32_t next; ++ uint32_t addr; ++ uint32_t id; ++ uint32_t cmd; ++} __packed; ++ ++struct jzfb { ++ struct fb_info *fb; ++ struct platform_device *pdev; ++ void __iomem *base; ++ struct resource *mem; ++ struct jz4740_fb_platform_data *pdata; ++ ++ size_t vidmem_size; ++ void *vidmem; ++ dma_addr_t vidmem_phys; ++ struct jzfb_framedesc *framedesc; ++ dma_addr_t framedesc_phys; ++ ++ struct clk *ldclk; ++ struct clk *lpclk; ++ ++ unsigned is_enabled:1; ++ struct mutex lock; ++ ++ uint32_t pseudo_palette[16]; ++}; ++ ++static const struct fb_fix_screeninfo jzfb_fix __devinitdata = { ++ .id = "JZ4740 FB", ++ .type = FB_TYPE_PACKED_PIXELS, ++ .visual = FB_VISUAL_TRUECOLOR, ++ .xpanstep = 0, ++ .ypanstep = 0, ++ .ywrapstep = 0, ++ .accel = FB_ACCEL_NONE, ++}; ++ ++static const struct jz_gpio_bulk_request jz_lcd_ctrl_pins[] = { ++ JZ_GPIO_BULK_PIN(LCD_PCLK), ++ JZ_GPIO_BULK_PIN(LCD_HSYNC), ++ JZ_GPIO_BULK_PIN(LCD_VSYNC), ++ JZ_GPIO_BULK_PIN(LCD_DE), ++ JZ_GPIO_BULK_PIN(LCD_PS), ++ JZ_GPIO_BULK_PIN(LCD_REV), ++ JZ_GPIO_BULK_PIN(LCD_CLS), ++ JZ_GPIO_BULK_PIN(LCD_SPL), ++}; ++ ++static const struct jz_gpio_bulk_request jz_lcd_data_pins[] = { ++ JZ_GPIO_BULK_PIN(LCD_DATA0), ++ JZ_GPIO_BULK_PIN(LCD_DATA1), ++ JZ_GPIO_BULK_PIN(LCD_DATA2), ++ JZ_GPIO_BULK_PIN(LCD_DATA3), ++ JZ_GPIO_BULK_PIN(LCD_DATA4), ++ JZ_GPIO_BULK_PIN(LCD_DATA5), ++ JZ_GPIO_BULK_PIN(LCD_DATA6), ++ JZ_GPIO_BULK_PIN(LCD_DATA7), ++ JZ_GPIO_BULK_PIN(LCD_DATA8), ++ JZ_GPIO_BULK_PIN(LCD_DATA9), ++ JZ_GPIO_BULK_PIN(LCD_DATA10), ++ JZ_GPIO_BULK_PIN(LCD_DATA11), ++ JZ_GPIO_BULK_PIN(LCD_DATA12), ++ JZ_GPIO_BULK_PIN(LCD_DATA13), ++ JZ_GPIO_BULK_PIN(LCD_DATA14), ++ JZ_GPIO_BULK_PIN(LCD_DATA15), ++ JZ_GPIO_BULK_PIN(LCD_DATA16), ++ JZ_GPIO_BULK_PIN(LCD_DATA17), ++}; ++ ++static unsigned int jzfb_num_ctrl_pins(struct jzfb *jzfb) ++{ ++ unsigned int num; ++ ++ switch (jzfb->pdata->lcd_type) { ++ case JZ_LCD_TYPE_GENERIC_16_BIT: ++ num = 4; ++ break; ++ case JZ_LCD_TYPE_GENERIC_18_BIT: ++ num = 4; ++ break; ++ case JZ_LCD_TYPE_8BIT_SERIAL: ++ num = 3; ++ break; ++ case JZ_LCD_TYPE_SPECIAL_TFT_1: ++ case JZ_LCD_TYPE_SPECIAL_TFT_2: ++ case JZ_LCD_TYPE_SPECIAL_TFT_3: ++ num = 8; ++ break; ++ default: ++ num = 0; ++ break; ++ } ++ return num; ++} ++ ++static unsigned int jzfb_num_data_pins(struct jzfb *jzfb) ++{ ++ unsigned int num; ++ ++ switch (jzfb->pdata->lcd_type) { ++ case JZ_LCD_TYPE_GENERIC_16_BIT: ++ num = 16; ++ break; ++ case JZ_LCD_TYPE_GENERIC_18_BIT: ++ num = 18; ++ break; ++ case JZ_LCD_TYPE_8BIT_SERIAL: ++ num = 8; ++ break; ++ case JZ_LCD_TYPE_SPECIAL_TFT_1: ++ case JZ_LCD_TYPE_SPECIAL_TFT_2: ++ case JZ_LCD_TYPE_SPECIAL_TFT_3: ++ if (jzfb->pdata->bpp == 18) ++ num = 18; ++ else ++ num = 16; ++ break; ++ default: ++ num = 0; ++ break; ++ } ++ return num; ++} ++ ++/* Based on CNVT_TOHW macro from skeletonfb.c */ ++static inline uint32_t jzfb_convert_color_to_hw(unsigned val, ++ struct fb_bitfield *bf) ++{ ++ return (((val << bf->length) + 0x7FFF - val) >> 16) << bf->offset; ++} ++ ++static int jzfb_setcolreg(unsigned regno, unsigned red, unsigned green, ++ unsigned blue, unsigned transp, struct fb_info *fb) ++{ ++ uint32_t color; ++ ++ if (regno >= 16) ++ return -EINVAL; ++ ++ color = jzfb_convert_color_to_hw(red, &fb->var.red); ++ color |= jzfb_convert_color_to_hw(green, &fb->var.green); ++ color |= jzfb_convert_color_to_hw(blue, &fb->var.blue); ++ color |= jzfb_convert_color_to_hw(transp, &fb->var.transp); ++ ++ ((uint32_t *)(fb->pseudo_palette))[regno] = color; ++ ++ return 0; ++} ++ ++static int jzfb_get_controller_bpp(struct jzfb *jzfb) ++{ ++ switch (jzfb->pdata->bpp) { ++ case 18: ++ case 24: ++ return 32; ++ case 15: ++ return 16; ++ default: ++ return jzfb->pdata->bpp; ++ } ++} ++ ++static struct fb_videomode *jzfb_get_mode(struct jzfb *jzfb, ++ struct fb_var_screeninfo *var) ++{ ++ size_t i; ++ struct fb_videomode *mode = jzfb->pdata->modes; ++ ++ for (i = 0; i < jzfb->pdata->num_modes; ++i, ++mode) { ++ if (mode->xres == var->xres && mode->yres == var->yres) ++ return mode; ++ } ++ ++ return NULL; ++} ++ ++static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb) ++{ ++ struct jzfb *jzfb = fb->par; ++ struct fb_videomode *mode; ++ ++ if (var->bits_per_pixel != jzfb_get_controller_bpp(jzfb) && ++ var->bits_per_pixel != jzfb->pdata->bpp) ++ return -EINVAL; ++ ++ mode = jzfb_get_mode(jzfb, var); ++ if (mode == NULL) ++ return -EINVAL; ++ ++ fb_videomode_to_var(var, mode); ++ ++ switch (jzfb->pdata->bpp) { ++ case 8: ++ break; ++ case 15: ++ var->red.offset = 10; ++ var->red.length = 5; ++ var->green.offset = 6; ++ var->green.length = 5; ++ var->blue.offset = 0; ++ var->blue.length = 5; ++ break; ++ case 16: ++ var->red.offset = 11; ++ var->red.length = 5; ++ var->green.offset = 5; ++ var->green.length = 6; ++ var->blue.offset = 0; ++ var->blue.length = 5; ++ break; ++ case 18: ++ var->red.offset = 16; ++ var->red.length = 6; ++ var->green.offset = 8; ++ var->green.length = 6; ++ var->blue.offset = 0; ++ var->blue.length = 6; ++ var->bits_per_pixel = 32; ++ break; ++ case 32: ++ case 24: ++ var->transp.offset = 24; ++ var->transp.length = 8; ++ var->red.offset = 16; ++ var->red.length = 8; ++ var->green.offset = 8; ++ var->green.length = 8; ++ var->blue.offset = 0; ++ var->blue.length = 8; ++ var->bits_per_pixel = 32; ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int jzfb_set_par(struct fb_info *info) ++{ ++ struct jzfb *jzfb = info->par; ++ struct jz4740_fb_platform_data *pdata = jzfb->pdata; ++ struct fb_var_screeninfo *var = &info->var; ++ struct fb_videomode *mode; ++ uint16_t hds, vds; ++ uint16_t hde, vde; ++ uint16_t ht, vt; ++ uint32_t ctrl; ++ uint32_t cfg; ++ unsigned long rate; ++ ++ mode = jzfb_get_mode(jzfb, var); ++ if (mode == NULL) ++ return -EINVAL; ++ ++ if (mode == info->mode) ++ return 0; ++ ++ info->mode = mode; ++ ++ hds = mode->hsync_len + mode->left_margin; ++ hde = hds + mode->xres; ++ ht = hde + mode->right_margin; ++ ++ vds = mode->vsync_len + mode->upper_margin; ++ vde = vds + mode->yres; ++ vt = vde + mode->lower_margin; ++ ++ ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16; ++ ++ switch (pdata->bpp) { ++ case 1: ++ ctrl |= JZ_LCD_CTRL_BPP_1; ++ break; ++ case 2: ++ ctrl |= JZ_LCD_CTRL_BPP_2; ++ break; ++ case 4: ++ ctrl |= JZ_LCD_CTRL_BPP_4; ++ break; ++ case 8: ++ ctrl |= JZ_LCD_CTRL_BPP_8; ++ break; ++ case 15: ++ ctrl |= JZ_LCD_CTRL_RGB555; /* Falltrough */ ++ case 16: ++ ctrl |= JZ_LCD_CTRL_BPP_15_16; ++ break; ++ case 18: ++ case 24: ++ case 32: ++ ctrl |= JZ_LCD_CTRL_BPP_18_24; ++ break; ++ default: ++ break; ++ } ++ ++ cfg = pdata->lcd_type & 0xf; ++ ++ if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT)) ++ cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW; ++ ++ if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT)) ++ cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW; ++ ++ if (pdata->pixclk_falling_edge) ++ cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE; ++ ++ if (pdata->date_enable_active_low) ++ cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW; ++ ++ if (pdata->lcd_type == JZ_LCD_TYPE_GENERIC_18_BIT) ++ cfg |= JZ_LCD_CFG_18_BIT; ++ ++ if (mode->pixclock) { ++ rate = PICOS2KHZ(mode->pixclock) * 1000; ++ mode->refresh = rate / vt / ht; ++ } else { ++ if (pdata->lcd_type == JZ_LCD_TYPE_8BIT_SERIAL) ++ rate = mode->refresh * (vt + 2 * mode->xres) * ht; ++ else ++ rate = mode->refresh * vt * ht; ++ ++ mode->pixclock = KHZ2PICOS(rate / 1000); ++ } ++ ++ mutex_lock(&jzfb->lock); ++ if (!jzfb->is_enabled) ++ clk_enable(jzfb->ldclk); ++ else ++ ctrl |= JZ_LCD_CTRL_ENABLE; ++ ++ switch (pdata->lcd_type) { ++ case JZ_LCD_TYPE_SPECIAL_TFT_1: ++ case JZ_LCD_TYPE_SPECIAL_TFT_2: ++ case JZ_LCD_TYPE_SPECIAL_TFT_3: ++ writel(pdata->special_tft_config.spl, jzfb->base + JZ_REG_LCD_SPL); ++ writel(pdata->special_tft_config.cls, jzfb->base + JZ_REG_LCD_CLS); ++ writel(pdata->special_tft_config.ps, jzfb->base + JZ_REG_LCD_PS); ++ writel(pdata->special_tft_config.ps, jzfb->base + JZ_REG_LCD_REV); ++ break; ++ default: ++ cfg |= JZ_LCD_CFG_PS_DISABLE; ++ cfg |= JZ_LCD_CFG_CLS_DISABLE; ++ cfg |= JZ_LCD_CFG_SPL_DISABLE; ++ cfg |= JZ_LCD_CFG_REV_DISABLE; ++ break; ++ } ++ ++ writel(mode->hsync_len, jzfb->base + JZ_REG_LCD_HSYNC); ++ writel(mode->vsync_len, jzfb->base + JZ_REG_LCD_VSYNC); ++ ++ writel((ht << 16) | vt, jzfb->base + JZ_REG_LCD_VAT); ++ ++ writel((hds << 16) | hde, jzfb->base + JZ_REG_LCD_DAH); ++ writel((vds << 16) | vde, jzfb->base + JZ_REG_LCD_DAV); ++ ++ writel(cfg, jzfb->base + JZ_REG_LCD_CFG); ++ ++ writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); ++ ++ if (!jzfb->is_enabled) ++ clk_disable(jzfb->ldclk); ++ ++ mutex_unlock(&jzfb->lock); ++ ++ clk_set_rate(jzfb->lpclk, rate); ++ clk_set_rate(jzfb->ldclk, rate * 3); ++ ++ return 0; ++} ++ ++static void jzfb_enable(struct jzfb *jzfb) ++{ ++ uint32_t ctrl; ++ ++ clk_enable(jzfb->ldclk); ++ ++ jz_gpio_bulk_resume(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); ++ jz_gpio_bulk_resume(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); ++ ++ writel(0, jzfb->base + JZ_REG_LCD_STATE); ++ ++ writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); ++ ++ ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); ++ ctrl |= JZ_LCD_CTRL_ENABLE; ++ ctrl &= ~JZ_LCD_CTRL_DISABLE; ++ writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); ++} ++ ++static void jzfb_disable(struct jzfb *jzfb) ++{ ++ uint32_t ctrl; ++ ++ ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); ++ ctrl |= JZ_LCD_CTRL_DISABLE; ++ writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); ++ do { ++ ctrl = readl(jzfb->base + JZ_REG_LCD_STATE); ++ } while (!(ctrl & JZ_LCD_STATE_DISABLED)); ++ ++ jz_gpio_bulk_suspend(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); ++ jz_gpio_bulk_suspend(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); ++ ++ clk_disable(jzfb->ldclk); ++} ++ ++static int jzfb_blank(int blank_mode, struct fb_info *info) ++{ ++ struct jzfb *jzfb = info->par; ++ ++ switch (blank_mode) { ++ case FB_BLANK_UNBLANK: ++ mutex_lock(&jzfb->lock); ++ if (jzfb->is_enabled) { ++ mutex_unlock(&jzfb->lock); ++ return 0; ++ } ++ ++ jzfb_enable(jzfb); ++ jzfb->is_enabled = 1; ++ ++ mutex_unlock(&jzfb->lock); ++ break; ++ default: ++ mutex_lock(&jzfb->lock); ++ if (!jzfb->is_enabled) { ++ mutex_unlock(&jzfb->lock); ++ return 0; ++ } ++ ++ jzfb_disable(jzfb); ++ jzfb->is_enabled = 0; ++ ++ mutex_unlock(&jzfb->lock); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int jzfb_alloc_devmem(struct jzfb *jzfb) ++{ ++ int max_videosize = 0; ++ struct fb_videomode *mode = jzfb->pdata->modes; ++ void *page; ++ int i; ++ ++ for (i = 0; i < jzfb->pdata->num_modes; ++mode, ++i) { ++ if (max_videosize < mode->xres * mode->yres) ++ max_videosize = mode->xres * mode->yres; ++ } ++ ++ max_videosize *= jzfb_get_controller_bpp(jzfb) >> 3; ++ ++ jzfb->framedesc = dma_alloc_coherent(&jzfb->pdev->dev, ++ sizeof(*jzfb->framedesc), ++ &jzfb->framedesc_phys, GFP_KERNEL); ++ ++ if (!jzfb->framedesc) ++ return -ENOMEM; ++ ++ jzfb->vidmem_size = PAGE_ALIGN(max_videosize); ++ jzfb->vidmem = dma_alloc_coherent(&jzfb->pdev->dev, ++ jzfb->vidmem_size, ++ &jzfb->vidmem_phys, GFP_KERNEL); ++ ++ if (!jzfb->vidmem) ++ goto err_free_framedesc; ++ ++ for (page = jzfb->vidmem; ++ page < jzfb->vidmem + PAGE_ALIGN(jzfb->vidmem_size); ++ page += PAGE_SIZE) { ++ SetPageReserved(virt_to_page(page)); ++ } ++ ++ jzfb->framedesc->next = jzfb->framedesc_phys; ++ jzfb->framedesc->addr = jzfb->vidmem_phys; ++ jzfb->framedesc->id = 0xdeafbead; ++ jzfb->framedesc->cmd = 0; ++ jzfb->framedesc->cmd |= max_videosize / 4; ++ ++ return 0; ++ ++err_free_framedesc: ++ dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), ++ jzfb->framedesc, jzfb->framedesc_phys); ++ return -ENOMEM; ++} ++ ++static void jzfb_free_devmem(struct jzfb *jzfb) ++{ ++ dma_free_coherent(&jzfb->pdev->dev, jzfb->vidmem_size, ++ jzfb->vidmem, jzfb->vidmem_phys); ++ dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), ++ jzfb->framedesc, jzfb->framedesc_phys); ++} ++ ++static struct fb_ops jzfb_ops = { ++ .owner = THIS_MODULE, ++ .fb_check_var = jzfb_check_var, ++ .fb_set_par = jzfb_set_par, ++ .fb_blank = jzfb_blank, ++ .fb_fillrect = sys_fillrect, ++ .fb_copyarea = sys_copyarea, ++ .fb_imageblit = sys_imageblit, ++ .fb_setcolreg = jzfb_setcolreg, ++}; ++ ++static int __devinit jzfb_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct jzfb *jzfb; ++ struct fb_info *fb; ++ struct jz4740_fb_platform_data *pdata = pdev->dev.platform_data; ++ struct resource *mem; ++ ++ if (!pdata) { ++ dev_err(&pdev->dev, "Missing platform data\n"); ++ return -ENXIO; ++ } ++ ++ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!mem) { ++ dev_err(&pdev->dev, "Failed to get register memory resource\n"); ++ return -ENXIO; ++ } ++ ++ mem = request_mem_region(mem->start, resource_size(mem), pdev->name); ++ if (!mem) { ++ dev_err(&pdev->dev, "Failed to request register memory region\n"); ++ return -EBUSY; ++ } ++ ++ fb = framebuffer_alloc(sizeof(struct jzfb), &pdev->dev); ++ if (!fb) { ++ dev_err(&pdev->dev, "Failed to allocate framebuffer device\n"); ++ ret = -ENOMEM; ++ goto err_release_mem_region; ++ } ++ ++ fb->fbops = &jzfb_ops; ++ fb->flags = FBINFO_DEFAULT; ++ ++ jzfb = fb->par; ++ jzfb->pdev = pdev; ++ jzfb->pdata = pdata; ++ jzfb->mem = mem; ++ ++ jzfb->ldclk = clk_get(&pdev->dev, "lcd"); ++ if (IS_ERR(jzfb->ldclk)) { ++ ret = PTR_ERR(jzfb->ldclk); ++ dev_err(&pdev->dev, "Failed to get lcd clock: %d\n", ret); ++ goto err_framebuffer_release; ++ } ++ ++ jzfb->lpclk = clk_get(&pdev->dev, "lcd_pclk"); ++ if (IS_ERR(jzfb->lpclk)) { ++ ret = PTR_ERR(jzfb->lpclk); ++ dev_err(&pdev->dev, "Failed to get lcd pixel clock: %d\n", ret); ++ goto err_put_ldclk; ++ } ++ ++ jzfb->base = ioremap(mem->start, resource_size(mem)); ++ if (!jzfb->base) { ++ dev_err(&pdev->dev, "Failed to ioremap register memory region\n"); ++ ret = -EBUSY; ++ goto err_put_lpclk; ++ } ++ ++ platform_set_drvdata(pdev, jzfb); ++ ++ mutex_init(&jzfb->lock); ++ ++ fb_videomode_to_modelist(pdata->modes, pdata->num_modes, ++ &fb->modelist); ++ fb_videomode_to_var(&fb->var, pdata->modes); ++ fb->var.bits_per_pixel = pdata->bpp; ++ jzfb_check_var(&fb->var, fb); ++ ++ ret = jzfb_alloc_devmem(jzfb); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to allocate video memory\n"); ++ goto err_iounmap; ++ } ++ ++ fb->fix = jzfb_fix; ++ fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8; ++ fb->fix.mmio_start = mem->start; ++ fb->fix.mmio_len = resource_size(mem); ++ fb->fix.smem_start = jzfb->vidmem_phys; ++ fb->fix.smem_len = fb->fix.line_length * fb->var.yres; ++ fb->screen_base = jzfb->vidmem; ++ fb->pseudo_palette = jzfb->pseudo_palette; ++ ++ fb_alloc_cmap(&fb->cmap, 256, 0); ++ ++ clk_enable(jzfb->ldclk); ++ jzfb->is_enabled = 1; ++ ++ writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); ++ ++ fb->mode = NULL; ++ jzfb_set_par(fb); ++ ++ jz_gpio_bulk_request(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); ++ jz_gpio_bulk_request(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); ++ ++ ret = register_framebuffer(fb); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret); ++ goto err_free_devmem; ++ } ++ ++ jzfb->fb = fb; ++ ++ return 0; ++ ++err_free_devmem: ++ jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); ++ jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); ++ ++ fb_dealloc_cmap(&fb->cmap); ++ jzfb_free_devmem(jzfb); ++err_iounmap: ++ iounmap(jzfb->base); ++err_put_lpclk: ++ clk_put(jzfb->lpclk); ++err_put_ldclk: ++ clk_put(jzfb->ldclk); ++err_framebuffer_release: ++ framebuffer_release(fb); ++err_release_mem_region: ++ release_mem_region(mem->start, resource_size(mem)); ++ return ret; ++} ++ ++static int __devexit jzfb_remove(struct platform_device *pdev) ++{ ++ struct jzfb *jzfb = platform_get_drvdata(pdev); ++ ++ jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb); ++ ++ jz_gpio_bulk_free(jz_lcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); ++ jz_gpio_bulk_free(jz_lcd_data_pins, jzfb_num_data_pins(jzfb)); ++ ++ iounmap(jzfb->base); ++ release_mem_region(jzfb->mem->start, resource_size(jzfb->mem)); ++ ++ fb_dealloc_cmap(&jzfb->fb->cmap); ++ jzfb_free_devmem(jzfb); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ clk_put(jzfb->lpclk); ++ clk_put(jzfb->ldclk); ++ ++ framebuffer_release(jzfb->fb); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int jzfb_suspend(struct device *dev) ++{ ++ struct jzfb *jzfb = dev_get_drvdata(dev); ++ ++ acquire_console_sem(); ++ fb_set_suspend(jzfb->fb, 1); ++ release_console_sem(); ++ ++ mutex_lock(&jzfb->lock); ++ if (jzfb->is_enabled) ++ jzfb_disable(jzfb); ++ mutex_unlock(&jzfb->lock); ++ ++ return 0; ++} ++ ++static int jzfb_resume(struct device *dev) ++{ ++ struct jzfb *jzfb = dev_get_drvdata(dev); ++ clk_enable(jzfb->ldclk); ++ ++ mutex_lock(&jzfb->lock); ++ if (jzfb->is_enabled) ++ jzfb_enable(jzfb); ++ mutex_unlock(&jzfb->lock); ++ ++ acquire_console_sem(); ++ fb_set_suspend(jzfb->fb, 0); ++ release_console_sem(); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops jzfb_pm_ops = { ++ .suspend = jzfb_suspend, ++ .resume = jzfb_resume, ++ .poweroff = jzfb_suspend, ++ .restore = jzfb_resume, ++}; ++ ++#define JZFB_PM_OPS (&jzfb_pm_ops) ++ ++#else ++#define JZFB_PM_OPS NULL ++#endif ++ ++static struct platform_driver jzfb_driver = { ++ .probe = jzfb_probe, ++ .remove = __devexit_p(jzfb_remove), ++ .driver = { ++ .name = "jz4740-fb", ++ .pm = JZFB_PM_OPS, ++ }, ++}; ++ ++static int __init jzfb_init(void) ++{ ++ return platform_driver_register(&jzfb_driver); ++} ++module_init(jzfb_init); ++ ++static void __exit jzfb_exit(void) ++{ ++ platform_driver_unregister(&jzfb_driver); ++} ++module_exit(jzfb_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); ++MODULE_DESCRIPTION("JZ4740 SoC LCD framebuffer driver"); ++MODULE_ALIAS("platform:jz4740-fb"); |