diff options
Diffstat (limited to 'u-boot/drivers/video')
27 files changed, 13480 insertions, 0 deletions
diff --git a/u-boot/drivers/video/Makefile b/u-boot/drivers/video/Makefile new file mode 100644 index 0000000..5448ff8 --- /dev/null +++ b/u-boot/drivers/video/Makefile @@ -0,0 +1,62 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# 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 +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libvideo.o + +COBJS-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o +COBJS-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o +COBJS-$(CONFIG_CFB_CONSOLE) += cfb_console.o +COBJS-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o +COBJS-$(CONFIG_S6E63D6) += s6e63d6.o +COBJS-$(CONFIG_VIDEO_AMBA) += amba.o +COBJS-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o +COBJS-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o +COBJS-$(CONFIG_VIDEO_MB86R0xGDC) += mb86r0xgdc.o videomodes.o +COBJS-$(CONFIG_VIDEO_MX3) += mx3fb.o +COBJS-$(CONFIG_VIDEO_MX5) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o +COBJS-$(CONFIG_VIDEO_SED13806) += sed13806.o +COBJS-$(CONFIG_SED156X) += sed156x.o +COBJS-$(CONFIG_VIDEO_SM501) += sm501.o +COBJS-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o +COBJS-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o +COBJS-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/u-boot/drivers/video/amba.c b/u-boot/drivers/video/amba.c new file mode 100644 index 0000000..ffa1c39 --- /dev/null +++ b/u-boot/drivers/video/amba.c @@ -0,0 +1,79 @@ +/* + * Driver for AMBA PrimeCell CLCD + * + * Copyright (C) 2009 Alessandro Rubini + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#include <common.h> +#include <asm/io.h> +#include <lcd.h> +#include <amba_clcd.h> + +/* These variables are required by lcd.c -- although it sets them by itself */ +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; +void *lcd_base; +void *lcd_console_address; +short console_col; +short console_row; + +/* + * To use this driver you need to provide the following in board files: + * a panel_info definition + * an lcd_enable function (can't define a weak default with current code) + */ + +/* There is nothing to do with color registers, we use true color */ +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ + return; +} + +/* Low level initialization of the logic cell: depends on panel_info */ +void lcd_ctrl_init(void *lcdbase) +{ + struct clcd_config *config; + struct clcd_registers *regs; + u32 cntl; + + config = panel_info.priv; + regs = config->address; + cntl = config->cntl & ~CNTL_LCDEN; + + /* Lazily, just copy the registers over: first control with disable */ + writel(cntl, ®s->cntl); + + writel(config->tim0, ®s->tim0); + writel(config->tim1, ®s->tim1); + writel(config->tim2, ®s->tim2); + writel(config->tim3, ®s->tim3); + writel((u32)lcdbase, ®s->ubas); + /* finally, enable */ + writel(cntl | CNTL_LCDEN, ®s->cntl); +} + +/* This is trivial, and copied from atmel_lcdfb.c */ +ulong calc_fbsize(void) +{ + return ((panel_info.vl_col * panel_info.vl_row * + NBITS(panel_info.vl_bpix)) / 8) + PAGE_SIZE; +} diff --git a/u-boot/drivers/video/ati_ids.h b/u-boot/drivers/video/ati_ids.h new file mode 100644 index 0000000..3e72a7d --- /dev/null +++ b/u-boot/drivers/video/ati_ids.h @@ -0,0 +1,211 @@ +/* + * ATI PCI IDs from XFree86, kept here to make sync'ing with + * XFree much simpler. Currently, this list is only used by + * radeonfb + */ + +#define PCI_CHIP_RV380_3150 0x3150 +#define PCI_CHIP_RV380_3151 0x3151 +#define PCI_CHIP_RV380_3152 0x3152 +#define PCI_CHIP_RV380_3153 0x3153 +#define PCI_CHIP_RV380_3154 0x3154 +#define PCI_CHIP_RV380_3156 0x3156 +#define PCI_CHIP_RV380_3E50 0x3E50 +#define PCI_CHIP_RV380_3E51 0x3E51 +#define PCI_CHIP_RV380_3E52 0x3E52 +#define PCI_CHIP_RV380_3E53 0x3E53 +#define PCI_CHIP_RV380_3E54 0x3E54 +#define PCI_CHIP_RV380_3E56 0x3E56 +#define PCI_CHIP_RS100_4136 0x4136 +#define PCI_CHIP_RS200_4137 0x4137 +#define PCI_CHIP_R300_AD 0x4144 +#define PCI_CHIP_R300_AE 0x4145 +#define PCI_CHIP_R300_AF 0x4146 +#define PCI_CHIP_R300_AG 0x4147 +#define PCI_CHIP_R350_AH 0x4148 +#define PCI_CHIP_R350_AI 0x4149 +#define PCI_CHIP_R350_AJ 0x414A +#define PCI_CHIP_R350_AK 0x414B +#define PCI_CHIP_RV350_AP 0x4150 +#define PCI_CHIP_RV350_AQ 0x4151 +#define PCI_CHIP_RV360_AR 0x4152 +#define PCI_CHIP_RV350_AS 0x4153 +#define PCI_CHIP_RV350_AT 0x4154 +#define PCI_CHIP_RV350_AV 0x4156 +#define PCI_CHIP_MACH32 0x4158 +#define PCI_CHIP_RS250_4237 0x4237 +#define PCI_CHIP_R200_BB 0x4242 +#define PCI_CHIP_R200_BC 0x4243 +#define PCI_CHIP_RS100_4336 0x4336 +#define PCI_CHIP_RS200_4337 0x4337 +#define PCI_CHIP_MACH64CT 0x4354 +#define PCI_CHIP_MACH64CX 0x4358 +#define PCI_CHIP_RS250_4437 0x4437 +#define PCI_CHIP_MACH64ET 0x4554 +#define PCI_CHIP_MACH64GB 0x4742 +#define PCI_CHIP_MACH64GD 0x4744 +#define PCI_CHIP_MACH64GI 0x4749 +#define PCI_CHIP_MACH64GL 0x474C +#define PCI_CHIP_MACH64GM 0x474D +#define PCI_CHIP_MACH64GN 0x474E +#define PCI_CHIP_MACH64GO 0x474F +#define PCI_CHIP_MACH64GP 0x4750 +#define PCI_CHIP_MACH64GQ 0x4751 +#define PCI_CHIP_MACH64GR 0x4752 +#define PCI_CHIP_MACH64GS 0x4753 +#define PCI_CHIP_MACH64GT 0x4754 +#define PCI_CHIP_MACH64GU 0x4755 +#define PCI_CHIP_MACH64GV 0x4756 +#define PCI_CHIP_MACH64GW 0x4757 +#define PCI_CHIP_MACH64GX 0x4758 +#define PCI_CHIP_MACH64GY 0x4759 +#define PCI_CHIP_MACH64GZ 0x475A +#define PCI_CHIP_RV250_Id 0x4964 +#define PCI_CHIP_RV250_Ie 0x4965 +#define PCI_CHIP_RV250_If 0x4966 +#define PCI_CHIP_RV250_Ig 0x4967 +#define PCI_CHIP_R420_JH 0x4A48 +#define PCI_CHIP_R420_JI 0x4A49 +#define PCI_CHIP_R420_JJ 0x4A4A +#define PCI_CHIP_R420_JK 0x4A4B +#define PCI_CHIP_R420_JL 0x4A4C +#define PCI_CHIP_R420_JM 0x4A4D +#define PCI_CHIP_R420_JN 0x4A4E +#define PCI_CHIP_R420_JP 0x4A50 +#define PCI_CHIP_MACH64LB 0x4C42 +#define PCI_CHIP_MACH64LD 0x4C44 +#define PCI_CHIP_RAGE128LE 0x4C45 +#define PCI_CHIP_RAGE128LF 0x4C46 +#define PCI_CHIP_MACH64LG 0x4C47 +#define PCI_CHIP_MACH64LI 0x4C49 +#define PCI_CHIP_MACH64LM 0x4C4D +#define PCI_CHIP_MACH64LN 0x4C4E +#define PCI_CHIP_MACH64LP 0x4C50 +#define PCI_CHIP_MACH64LQ 0x4C51 +#define PCI_CHIP_MACH64LR 0x4C52 +#define PCI_CHIP_MACH64LS 0x4C53 +#define PCI_CHIP_MACH64LT 0x4C54 +#define PCI_CHIP_RADEON_LW 0x4C57 +#define PCI_CHIP_RADEON_LX 0x4C58 +#define PCI_CHIP_RADEON_LY 0x4C59 +#define PCI_CHIP_RADEON_LZ 0x4C5A +#define PCI_CHIP_RV250_Ld 0x4C64 +#define PCI_CHIP_RV250_Le 0x4C65 +#define PCI_CHIP_RV250_Lf 0x4C66 +#define PCI_CHIP_RV250_Lg 0x4C67 +#define PCI_CHIP_RV250_Ln 0x4C6E +#define PCI_CHIP_RAGE128MF 0x4D46 +#define PCI_CHIP_RAGE128ML 0x4D4C +#define PCI_CHIP_R300_ND 0x4E44 +#define PCI_CHIP_R300_NE 0x4E45 +#define PCI_CHIP_R300_NF 0x4E46 +#define PCI_CHIP_R300_NG 0x4E47 +#define PCI_CHIP_R350_NH 0x4E48 +#define PCI_CHIP_R350_NI 0x4E49 +#define PCI_CHIP_R360_NJ 0x4E4A +#define PCI_CHIP_R350_NK 0x4E4B +#define PCI_CHIP_RV350_NP 0x4E50 +#define PCI_CHIP_RV350_NQ 0x4E51 +#define PCI_CHIP_RV350_NR 0x4E52 +#define PCI_CHIP_RV350_NS 0x4E53 +#define PCI_CHIP_RV350_NT 0x4E54 +#define PCI_CHIP_RV350_NV 0x4E56 +#define PCI_CHIP_RAGE128PA 0x5041 +#define PCI_CHIP_RAGE128PB 0x5042 +#define PCI_CHIP_RAGE128PC 0x5043 +#define PCI_CHIP_RAGE128PD 0x5044 +#define PCI_CHIP_RAGE128PE 0x5045 +#define PCI_CHIP_RAGE128PF 0x5046 +#define PCI_CHIP_RAGE128PG 0x5047 +#define PCI_CHIP_RAGE128PH 0x5048 +#define PCI_CHIP_RAGE128PI 0x5049 +#define PCI_CHIP_RAGE128PJ 0x504A +#define PCI_CHIP_RAGE128PK 0x504B +#define PCI_CHIP_RAGE128PL 0x504C +#define PCI_CHIP_RAGE128PM 0x504D +#define PCI_CHIP_RAGE128PN 0x504E +#define PCI_CHIP_RAGE128PO 0x504F +#define PCI_CHIP_RAGE128PP 0x5050 +#define PCI_CHIP_RAGE128PQ 0x5051 +#define PCI_CHIP_RAGE128PR 0x5052 +#define PCI_CHIP_RAGE128PS 0x5053 +#define PCI_CHIP_RAGE128PT 0x5054 +#define PCI_CHIP_RAGE128PU 0x5055 +#define PCI_CHIP_RAGE128PV 0x5056 +#define PCI_CHIP_RAGE128PW 0x5057 +#define PCI_CHIP_RAGE128PX 0x5058 +#define PCI_CHIP_RADEON_QD 0x5144 +#define PCI_CHIP_RADEON_QE 0x5145 +#define PCI_CHIP_RADEON_QF 0x5146 +#define PCI_CHIP_RADEON_QG 0x5147 +#define PCI_CHIP_R200_QH 0x5148 +#define PCI_CHIP_R200_QI 0x5149 +#define PCI_CHIP_R200_QJ 0x514A +#define PCI_CHIP_R200_QK 0x514B +#define PCI_CHIP_R200_QL 0x514C +#define PCI_CHIP_R200_QM 0x514D +#define PCI_CHIP_R200_QN 0x514E +#define PCI_CHIP_R200_QO 0x514F +#define PCI_CHIP_RV200_QW 0x5157 +#define PCI_CHIP_RV200_QX 0x5158 +#define PCI_CHIP_RV100_QY 0x5159 +#define PCI_CHIP_RV100_QZ 0x515A +#define PCI_CHIP_RN50 0x515E +#define PCI_CHIP_RAGE128RE 0x5245 +#define PCI_CHIP_RAGE128RF 0x5246 +#define PCI_CHIP_RAGE128RG 0x5247 +#define PCI_CHIP_RAGE128RK 0x524B +#define PCI_CHIP_RAGE128RL 0x524C +#define PCI_CHIP_RAGE128SE 0x5345 +#define PCI_CHIP_RAGE128SF 0x5346 +#define PCI_CHIP_RAGE128SG 0x5347 +#define PCI_CHIP_RAGE128SH 0x5348 +#define PCI_CHIP_RAGE128SK 0x534B +#define PCI_CHIP_RAGE128SL 0x534C +#define PCI_CHIP_RAGE128SM 0x534D +#define PCI_CHIP_RAGE128SN 0x534E +#define PCI_CHIP_RAGE128TF 0x5446 +#define PCI_CHIP_RAGE128TL 0x544C +#define PCI_CHIP_RAGE128TR 0x5452 +#define PCI_CHIP_RAGE128TS 0x5453 +#define PCI_CHIP_RAGE128TT 0x5454 +#define PCI_CHIP_RAGE128TU 0x5455 +#define PCI_CHIP_RV370_5460 0x5460 +#define PCI_CHIP_RV370_5461 0x5461 +#define PCI_CHIP_RV370_5462 0x5462 +#define PCI_CHIP_RV370_5463 0x5463 +#define PCI_CHIP_RV370_5464 0x5464 +#define PCI_CHIP_RV370_5465 0x5465 +#define PCI_CHIP_RV370_5466 0x5466 +#define PCI_CHIP_RV370_5467 0x5467 +#define PCI_CHIP_R423_UH 0x5548 +#define PCI_CHIP_R423_UI 0x5549 +#define PCI_CHIP_R423_UJ 0x554A +#define PCI_CHIP_R423_UK 0x554B +#define PCI_CHIP_R423_UQ 0x5551 +#define PCI_CHIP_R423_UR 0x5552 +#define PCI_CHIP_R423_UT 0x5554 +#define PCI_CHIP_MACH64VT 0x5654 +#define PCI_CHIP_MACH64VU 0x5655 +#define PCI_CHIP_MACH64VV 0x5656 +#define PCI_CHIP_RS300_5834 0x5834 +#define PCI_CHIP_RS300_5835 0x5835 +#define PCI_CHIP_RS300_5836 0x5836 +#define PCI_CHIP_RS300_5837 0x5837 +#define PCI_CHIP_RV370_5B60 0x5B60 +#define PCI_CHIP_RV370_5B61 0x5B61 +#define PCI_CHIP_RV370_5B62 0x5B62 +#define PCI_CHIP_RV370_5B63 0x5B63 +#define PCI_CHIP_RV370_5B64 0x5B64 +#define PCI_CHIP_RV370_5B65 0x5B65 +#define PCI_CHIP_RV370_5B66 0x5B66 +#define PCI_CHIP_RV370_5B67 0x5B67 +#define PCI_CHIP_RV280_5960 0x5960 +#define PCI_CHIP_RV280_5961 0x5961 +#define PCI_CHIP_RV280_5962 0x5962 +#define PCI_CHIP_RV280_5964 0x5964 +#define PCI_CHIP_RV280_5C61 0x5C61 +#define PCI_CHIP_RV280_5C63 0x5C63 +#define PCI_CHIP_R423_5D57 0x5D57 +#define PCI_CHIP_RS350_7834 0x7834 +#define PCI_CHIP_RS350_7835 0x7835 diff --git a/u-boot/drivers/video/ati_radeon_fb.c b/u-boot/drivers/video/ati_radeon_fb.c new file mode 100644 index 0000000..4a9bd07 --- /dev/null +++ b/u-boot/drivers/video/ati_radeon_fb.c @@ -0,0 +1,781 @@ +/* + * ATI Radeon Video card Framebuffer driver. + * + * Copyright 2007 Freescale Semiconductor, Inc. + * Zhang Wei <wei.zhang@freescale.com> + * Jason Jin <jason.jin@freescale.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + * + * Some codes of this file is partly ported from Linux kernel + * ATI video framebuffer driver. + * + * Now the driver is tested on below ATI chips: + * 9200 + * X300 + * X700 + * + */ + +#include <common.h> + +#include <command.h> +#include <pci.h> +#include <asm/processor.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <malloc.h> +#include <video_fb.h> +#include "videomodes.h" + +#include <radeon.h> +#include "ati_ids.h" +#include "ati_radeon_fb.h" + +#undef DEBUG + +#ifdef DEBUG +#define DPRINT(x...) printf(x) +#else +#define DPRINT(x...) do{}while(0) +#endif + +#ifndef min_t +#define min_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) +#endif + +#define MAX_MAPPED_VRAM (2048*2048*4) +#define MIN_MAPPED_VRAM (1024*768*1) + +#define RADEON_BUFFER_ALIGN 0x00000fff +#define SURF_UPPER_BOUND(x,y,bpp) (((((x) * (((y) + 15) & ~15) * (bpp)/8) + RADEON_BUFFER_ALIGN) \ + & ~RADEON_BUFFER_ALIGN) - 1) +#define RADEON_CRT_PITCH(width, bpp) ((((width) * (bpp) + ((bpp) * 8 - 1)) / ((bpp) * 8)) | \ + ((((width) * (bpp) + ((bpp) * 8 - 1)) / ((bpp) * 8)) << 16)) + +#define CRTC_H_TOTAL_DISP_VAL(htotal, hdisp) \ + (((((htotal) / 8) - 1) & 0x3ff) | (((((hdisp) / 8) - 1) & 0x1ff) << 16)) +#define CRTC_HSYNC_STRT_WID_VAL(hsync_srtr, hsync_wid) \ + (((hsync_srtr) & 0x1fff) | (((hsync_wid) & 0x3f) << 16)) +#define CRTC_V_TOTAL_DISP_VAL(vtotal, vdisp) \ + ((((vtotal) - 1) & 0xffff) | (((vdisp) - 1) << 16)) +#define CRTC_VSYNC_STRT_WID_VAL(vsync_srtr, vsync_wid) \ + ((((vsync_srtr) - 1) & 0xfff) | (((vsync_wid) & 0x1f) << 16)) + +/*#define PCI_VENDOR_ID_ATI*/ +#define PCI_CHIP_RV280_5960 0x5960 +#define PCI_CHIP_RV280_5961 0x5961 +#define PCI_CHIP_RV280_5962 0x5962 +#define PCI_CHIP_RV280_5964 0x5964 +#define PCI_CHIP_RV280_5C63 0x5C63 +#define PCI_CHIP_RV370_5B60 0x5B60 +#define PCI_CHIP_RV380_5657 0x5657 +#define PCI_CHIP_R420_554d 0x554d + +static struct pci_device_id ati_radeon_pci_ids[] = { + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5960}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5961}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5962}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5964}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV280_5C63}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV370_5B60}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_RV380_5657}, + {PCI_VENDOR_ID_ATI, PCI_CHIP_R420_554d}, + {0, 0} +}; + +static u16 ati_radeon_id_family_table[][2] = { + {PCI_CHIP_RV280_5960, CHIP_FAMILY_RV280}, + {PCI_CHIP_RV280_5961, CHIP_FAMILY_RV280}, + {PCI_CHIP_RV280_5962, CHIP_FAMILY_RV280}, + {PCI_CHIP_RV280_5964, CHIP_FAMILY_RV280}, + {PCI_CHIP_RV280_5C63, CHIP_FAMILY_RV280}, + {PCI_CHIP_RV370_5B60, CHIP_FAMILY_RV380}, + {PCI_CHIP_RV380_5657, CHIP_FAMILY_RV380}, + {PCI_CHIP_R420_554d, CHIP_FAMILY_R420}, + {0, 0} +}; + +u16 get_radeon_id_family(u16 device) +{ + int i; + for (i=0; ati_radeon_id_family_table[0][i]; i+=2) + if (ati_radeon_id_family_table[0][i] == device) + return ati_radeon_id_family_table[0][i + 1]; + return 0; +} + +struct radeonfb_info *rinfo; + +static void radeon_identify_vram(struct radeonfb_info *rinfo) +{ + u32 tmp; + + /* framebuffer size */ + if ((rinfo->family == CHIP_FAMILY_RS100) || + (rinfo->family == CHIP_FAMILY_RS200) || + (rinfo->family == CHIP_FAMILY_RS300)) { + u32 tom = INREG(NB_TOM); + tmp = ((((tom >> 16) - (tom & 0xffff) + 1) << 6) * 1024); + + radeon_fifo_wait(6); + OUTREG(MC_FB_LOCATION, tom); + OUTREG(DISPLAY_BASE_ADDR, (tom & 0xffff) << 16); + OUTREG(CRTC2_DISPLAY_BASE_ADDR, (tom & 0xffff) << 16); + OUTREG(OV0_BASE_ADDR, (tom & 0xffff) << 16); + + /* This is supposed to fix the crtc2 noise problem. */ + OUTREG(GRPH2_BUFFER_CNTL, INREG(GRPH2_BUFFER_CNTL) & ~0x7f0000); + + if ((rinfo->family == CHIP_FAMILY_RS100) || + (rinfo->family == CHIP_FAMILY_RS200)) { + /* This is to workaround the asic bug for RMX, some versions + of BIOS dosen't have this register initialized correctly. + */ + OUTREGP(CRTC_MORE_CNTL, CRTC_H_CUTOFF_ACTIVE_EN, + ~CRTC_H_CUTOFF_ACTIVE_EN); + } + } else { + tmp = INREG(CONFIG_MEMSIZE); + } + + /* mem size is bits [28:0], mask off the rest */ + rinfo->video_ram = tmp & CONFIG_MEMSIZE_MASK; + + /* + * Hack to get around some busted production M6's + * reporting no ram + */ + if (rinfo->video_ram == 0) { + switch (rinfo->pdev.device) { + case PCI_CHIP_RADEON_LY: + case PCI_CHIP_RADEON_LZ: + rinfo->video_ram = 8192 * 1024; + break; + default: + break; + } + } + + /* + * Now try to identify VRAM type + */ + if ((rinfo->family >= CHIP_FAMILY_R300) || + (INREG(MEM_SDRAM_MODE_REG) & (1<<30))) + rinfo->vram_ddr = 1; + else + rinfo->vram_ddr = 0; + + tmp = INREG(MEM_CNTL); + if (IS_R300_VARIANT(rinfo)) { + tmp &= R300_MEM_NUM_CHANNELS_MASK; + switch (tmp) { + case 0: rinfo->vram_width = 64; break; + case 1: rinfo->vram_width = 128; break; + case 2: rinfo->vram_width = 256; break; + default: rinfo->vram_width = 128; break; + } + } else if ((rinfo->family == CHIP_FAMILY_RV100) || + (rinfo->family == CHIP_FAMILY_RS100) || + (rinfo->family == CHIP_FAMILY_RS200)){ + if (tmp & RV100_MEM_HALF_MODE) + rinfo->vram_width = 32; + else + rinfo->vram_width = 64; + } else { + if (tmp & MEM_NUM_CHANNELS_MASK) + rinfo->vram_width = 128; + else + rinfo->vram_width = 64; + } + + /* This may not be correct, as some cards can have half of channel disabled + * ToDo: identify these cases + */ + + DPRINT("radeonfb: Found %dk of %s %d bits wide videoram\n", + rinfo->video_ram / 1024, + rinfo->vram_ddr ? "DDR" : "SDRAM", + rinfo->vram_width); + +} + +static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *mode) +{ + int i; + + radeon_fifo_wait(20); + +#if 0 + /* Workaround from XFree */ + if (rinfo->is_mobility) { + /* A temporal workaround for the occational blanking on certain laptop + * panels. This appears to related to the PLL divider registers + * (fail to lock?). It occurs even when all dividers are the same + * with their old settings. In this case we really don't need to + * fiddle with PLL registers. By doing this we can avoid the blanking + * problem with some panels. + */ + if ((mode->ppll_ref_div == (INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK)) && + (mode->ppll_div_3 == (INPLL(PPLL_DIV_3) & + (PPLL_POST3_DIV_MASK | PPLL_FB3_DIV_MASK)))) { + /* We still have to force a switch to selected PPLL div thanks to + * an XFree86 driver bug which will switch it away in some cases + * even when using UseFDev */ + OUTREGP(CLOCK_CNTL_INDEX, + mode->clk_cntl_index & PPLL_DIV_SEL_MASK, + ~PPLL_DIV_SEL_MASK); + radeon_pll_errata_after_index(rinfo); + radeon_pll_errata_after_data(rinfo); + return; + } + } +#endif + if(rinfo->pdev.device == PCI_CHIP_RV370_5B60) return; + + /* Swich VCKL clock input to CPUCLK so it stays fed while PPLL updates*/ + OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_CPUCLK, ~VCLK_SRC_SEL_MASK); + + /* Reset PPLL & enable atomic update */ + OUTPLLP(PPLL_CNTL, + PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN, + ~(PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN)); + + /* Switch to selected PPLL divider */ + OUTREGP(CLOCK_CNTL_INDEX, + mode->clk_cntl_index & PPLL_DIV_SEL_MASK, + ~PPLL_DIV_SEL_MASK); + + /* Set PPLL ref. div */ + if (rinfo->family == CHIP_FAMILY_R300 || + rinfo->family == CHIP_FAMILY_RS300 || + rinfo->family == CHIP_FAMILY_R350 || + rinfo->family == CHIP_FAMILY_RV350) { + if (mode->ppll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) { + /* When restoring console mode, use saved PPLL_REF_DIV + * setting. + */ + OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, 0); + } else { + /* R300 uses ref_div_acc field as real ref divider */ + OUTPLLP(PPLL_REF_DIV, + (mode->ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), + ~R300_PPLL_REF_DIV_ACC_MASK); + } + } else + OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, ~PPLL_REF_DIV_MASK); + + /* Set PPLL divider 3 & post divider*/ + OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_FB3_DIV_MASK); + OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_POST3_DIV_MASK); + + /* Write update */ + while (INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R) + ; + OUTPLLP(PPLL_REF_DIV, PPLL_ATOMIC_UPDATE_W, ~PPLL_ATOMIC_UPDATE_W); + + /* Wait read update complete */ + /* FIXME: Certain revisions of R300 can't recover here. Not sure of + the cause yet, but this workaround will mask the problem for now. + Other chips usually will pass at the very first test, so the + workaround shouldn't have any effect on them. */ + for (i = 0; (i < 10000 && INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R); i++) + ; + + OUTPLL(HTOTAL_CNTL, 0); + + /* Clear reset & atomic update */ + OUTPLLP(PPLL_CNTL, 0, + ~(PPLL_RESET | PPLL_SLEEP | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN)); + + /* We may want some locking ... oh well */ + udelay(5000); + + /* Switch back VCLK source to PPLL */ + OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_PPLLCLK, ~VCLK_SRC_SEL_MASK); +} + +typedef struct { + u16 reg; + u32 val; +} reg_val; + +#if 0 /* unused ? -> scheduled for removal */ +/* these common regs are cleared before mode setting so they do not + * interfere with anything + */ +static reg_val common_regs[] = { + { OVR_CLR, 0 }, + { OVR_WID_LEFT_RIGHT, 0 }, + { OVR_WID_TOP_BOTTOM, 0 }, + { OV0_SCALE_CNTL, 0 }, + { SUBPIC_CNTL, 0 }, + { VIPH_CONTROL, 0 }, + { I2C_CNTL_1, 0 }, + { GEN_INT_CNTL, 0 }, + { CAP0_TRIG_CNTL, 0 }, + { CAP1_TRIG_CNTL, 0 }, +}; +#endif /* 0 */ + +void radeon_setmode(void) +{ + struct radeon_regs *mode = malloc(sizeof(struct radeon_regs)); + + mode->crtc_gen_cntl = 0x03000200; + mode->crtc_ext_cntl = 0x00008048; + mode->dac_cntl = 0xff002100; + mode->crtc_h_total_disp = 0x4f0063; + mode->crtc_h_sync_strt_wid = 0x8c02a2; + mode->crtc_v_total_disp = 0x01df020c; + mode->crtc_v_sync_strt_wid = 0x8201ea; + mode->crtc_pitch = 0x00500050; + + OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl); + OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl, + ~(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS)); + OUTREGP(DAC_CNTL, mode->dac_cntl, DAC_RANGE_CNTL | DAC_BLANKING); + OUTREG(CRTC_H_TOTAL_DISP, mode->crtc_h_total_disp); + OUTREG(CRTC_H_SYNC_STRT_WID, mode->crtc_h_sync_strt_wid); + OUTREG(CRTC_V_TOTAL_DISP, mode->crtc_v_total_disp); + OUTREG(CRTC_V_SYNC_STRT_WID, mode->crtc_v_sync_strt_wid); + OUTREG(CRTC_OFFSET, 0); + OUTREG(CRTC_OFFSET_CNTL, 0); + OUTREG(CRTC_PITCH, mode->crtc_pitch); + + mode->clk_cntl_index = 0x300; + mode->ppll_ref_div = 0xc; + mode->ppll_div_3 = 0x00030059; + + radeon_write_pll_regs(rinfo, mode); +} + +static void set_pal(void) +{ + int idx, val = 0; + + for (idx = 0; idx < 256; idx++) { + OUTREG8(PALETTE_INDEX, idx); + OUTREG(PALETTE_DATA, val); + val += 0x00010101; + } +} + +void radeon_setmode_9200(int vesa_idx, int bpp) +{ + struct radeon_regs *mode = malloc(sizeof(struct radeon_regs)); + + mode->crtc_gen_cntl = CRTC_EN | CRTC_EXT_DISP_EN; + mode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN | CRTC_CRT_ON; + mode->dac_cntl = DAC_MASK_ALL | DAC_VGA_ADR_EN | DAC_8BIT_EN; + mode->crtc_offset_cntl = CRTC_OFFSET_CNTL__CRTC_TILE_EN; + + switch (bpp) { + case 24: + mode->crtc_gen_cntl |= 0x6 << 8; /* x888 */ +#if defined(__BIG_ENDIAN) + mode->surface_cntl = NONSURF_AP0_SWP_32BPP | NONSURF_AP1_SWP_32BPP; + mode->surf_info[0] = NONSURF_AP0_SWP_32BPP | NONSURF_AP1_SWP_32BPP; +#endif + break; + case 16: + mode->crtc_gen_cntl |= 0x4 << 8; /* 565 */ +#if defined(__BIG_ENDIAN) + mode->surface_cntl = NONSURF_AP0_SWP_16BPP | NONSURF_AP1_SWP_16BPP; + mode->surf_info[0] = NONSURF_AP0_SWP_16BPP | NONSURF_AP1_SWP_16BPP; +#endif + break; + default: + mode->crtc_gen_cntl |= 0x2 << 8; /* palette */ + mode->surface_cntl = 0x00000000; + break; + } + + switch (vesa_idx) { + case RES_MODE_1280x1024: + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1688,1280); + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(1066,1024); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(1025,3); +#if defined(CONFIG_RADEON_VREFRESH_75HZ) + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1288,18); + mode->ppll_div_3 = 0x00010078; +#else /* default @ 60 Hz */ + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1320,14); + mode->ppll_div_3 = 0x00010060; +#endif + /* + * for this mode pitch expands to the same value for 32, 16 and 8 bpp, + * so we set it here once only. + */ + mode->crtc_pitch = RADEON_CRT_PITCH(1280,32); + switch (bpp) { + case 24: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1280 * 4 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1280,1024,32); + break; + case 16: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1280 * 2 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1280,1024,16); + break; + default: /* 8 bpp */ + mode->surf_info[0] = R200_SURF_TILE_COLOR_MACRO | (1280 * 1 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1280,1024,8); + break; + } + break; + case RES_MODE_1024x768: +#if defined(CONFIG_RADEON_VREFRESH_75HZ) + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1312,1024); + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1032,12); + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(800,768); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(769,3); + mode->ppll_div_3 = 0x0002008c; +#else /* @ 60 Hz */ + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1344,1024); + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(1040,17) | CRTC_H_SYNC_POL; + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(806,768); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(771,6) | CRTC_V_SYNC_POL; + mode->ppll_div_3 = 0x00020074; +#endif + /* also same pitch value for 32, 16 and 8 bpp */ + mode->crtc_pitch = RADEON_CRT_PITCH(1024,32); + switch (bpp) { + case 24: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1024 * 4 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,768,32); + break; + case 16: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (1024 * 2 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,768,16); + break; + default: /* 8 bpp */ + mode->surf_info[0] = R200_SURF_TILE_COLOR_MACRO | (1024 * 1 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,768,8); + break; + } + break; + case RES_MODE_800x600: + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(1056,800); +#if defined(CONFIG_RADEON_VREFRESH_75HZ) + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(808,10); + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(625,600); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(601,3); + mode->ppll_div_3 = 0x000300b0; +#else /* @ 60 Hz */ + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(832,16); + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(628,600); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(601,4); + mode->ppll_div_3 = 0x0003008e; +#endif + switch (bpp) { + case 24: + mode->crtc_pitch = RADEON_CRT_PITCH(832,32); + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (832 * 4 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(832,600,32); + break; + case 16: + mode->crtc_pitch = RADEON_CRT_PITCH(896,16); + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (896 * 2 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(896,600,16); + break; + default: /* 8 bpp */ + mode->crtc_pitch = RADEON_CRT_PITCH(1024,8); + mode->surf_info[0] = R200_SURF_TILE_COLOR_MACRO | (1024 * 1 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(1024,600,8); + break; + } + break; + default: /* RES_MODE_640x480 */ +#if defined(CONFIG_RADEON_VREFRESH_75HZ) + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(840,640); + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(648,8) | CRTC_H_SYNC_POL; + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(500,480); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(481,3) | CRTC_V_SYNC_POL; + mode->ppll_div_3 = 0x00030070; +#else /* @ 60 Hz */ + mode->crtc_h_total_disp = CRTC_H_TOTAL_DISP_VAL(800,640); + mode->crtc_h_sync_strt_wid = CRTC_HSYNC_STRT_WID_VAL(674,12) | CRTC_H_SYNC_POL; + mode->crtc_v_total_disp = CRTC_V_TOTAL_DISP_VAL(525,480); + mode->crtc_v_sync_strt_wid = CRTC_VSYNC_STRT_WID_VAL(491,2) | CRTC_V_SYNC_POL; + mode->ppll_div_3 = 0x00030059; +#endif + /* also same pitch value for 32, 16 and 8 bpp */ + mode->crtc_pitch = RADEON_CRT_PITCH(640,32); + switch (bpp) { + case 24: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (640 * 4 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(640,480,32); + break; + case 16: + mode->surf_info[0] |= R200_SURF_TILE_COLOR_MACRO | (640 * 2 / 16); + mode->surf_upper_bound[0] = SURF_UPPER_BOUND(640,480,16); + break; + default: /* 8 bpp */ + mode->crtc_offset_cntl = 0x00000000; + break; + } + break; + } + + OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl | CRTC_DISP_REQ_EN_B); + OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl, + (CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS)); + OUTREGP(DAC_CNTL, mode->dac_cntl, DAC_RANGE_CNTL | DAC_BLANKING); + OUTREG(CRTC_H_TOTAL_DISP, mode->crtc_h_total_disp); + OUTREG(CRTC_H_SYNC_STRT_WID, mode->crtc_h_sync_strt_wid); + OUTREG(CRTC_V_TOTAL_DISP, mode->crtc_v_total_disp); + OUTREG(CRTC_V_SYNC_STRT_WID, mode->crtc_v_sync_strt_wid); + OUTREG(CRTC_OFFSET, 0); + OUTREG(CRTC_OFFSET_CNTL, mode->crtc_offset_cntl); + OUTREG(CRTC_PITCH, mode->crtc_pitch); + OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl); + + mode->clk_cntl_index = 0x300; + mode->ppll_ref_div = 0xc; + + radeon_write_pll_regs(rinfo, mode); + + OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl, + ~(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS)); + OUTREG(SURFACE0_INFO, mode->surf_info[0]); + OUTREG(SURFACE0_LOWER_BOUND, 0); + OUTREG(SURFACE0_UPPER_BOUND, mode->surf_upper_bound[0]); + OUTREG(SURFACE_CNTL, mode->surface_cntl); + + if (bpp > 8) + set_pal(); + + free(mode); +} + +#include "../bios_emulator/include/biosemu.h" +extern int BootVideoCardBIOS(pci_dev_t pcidev, BE_VGAInfo ** pVGAInfo, int cleanUp); + +int radeon_probe(struct radeonfb_info *rinfo) +{ + pci_dev_t pdev; + u16 did; + + pdev = pci_find_devices(ati_radeon_pci_ids, 0); + + if (pdev != -1) { + pci_read_config_word(pdev, PCI_DEVICE_ID, &did); + printf("ATI Radeon video card (%04x, %04x) found @(%d:%d:%d)\n", + PCI_VENDOR_ID_ATI, did, (pdev >> 16) & 0xff, + (pdev >> 11) & 0x1f, (pdev >> 8) & 0x7); + + strcpy(rinfo->name, "ATI Radeon"); + rinfo->pdev.vendor = PCI_VENDOR_ID_ATI; + rinfo->pdev.device = did; + rinfo->family = get_radeon_id_family(rinfo->pdev.device); + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, + &rinfo->fb_base_bus); + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, + &rinfo->mmio_base_bus); + rinfo->fb_base_bus &= 0xfffff000; + rinfo->mmio_base_bus &= ~0x04; + + rinfo->mmio_base = pci_bus_to_virt(pdev, rinfo->mmio_base_bus, + PCI_REGION_MEM, 0, MAP_NOCACHE); + DPRINT("rinfo->mmio_base = 0x%p bus=0x%x\n", + rinfo->mmio_base, rinfo->mmio_base_bus); + rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16; + DPRINT("rinfo->fb_local_base = 0x%x\n",rinfo->fb_local_base); + /* PostBIOS with x86 emulater */ + if (!BootVideoCardBIOS(pdev, NULL, 0)) + return -1; + + /* + * Check for errata + * (These will be added in the future for the chipfamily + * R300, RV200, RS200, RV100, RS100.) + */ + + /* Get VRAM size and type */ + radeon_identify_vram(rinfo); + + rinfo->mapped_vram = min_t(unsigned long, MAX_MAPPED_VRAM, + rinfo->video_ram); + rinfo->fb_base = pci_bus_to_virt(pdev, rinfo->fb_base_bus, + PCI_REGION_MEM, 0, MAP_NOCACHE); + DPRINT("Radeon: framebuffer base address 0x%08x, " + "bus address 0x%08x\n" + "MMIO base address 0x%08x, bus address 0x%08x, " + "framebuffer local base 0x%08x.\n ", + (u32)rinfo->fb_base, rinfo->fb_base_bus, + (u32)rinfo->mmio_base, rinfo->mmio_base_bus, + rinfo->fb_local_base); + return 0; + } + return -1; +} + +/* + * The Graphic Device + */ +GraphicDevice ctfb; + +#define CURSOR_SIZE 0x1000 /* in KByte for HW Cursor */ +#define PATTERN_ADR (pGD->dprBase + CURSOR_SIZE) /* pattern Memory after Cursor Memory */ +#define PATTERN_SIZE 8*8*4 /* 4 Bytes per Pixel 8 x 8 Pixel */ +#define ACCELMEMORY (CURSOR_SIZE + PATTERN_SIZE) /* reserved Memory for BITBlt and hw cursor */ + +void *video_hw_init(void) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + u32 *vm; + char *penv; + unsigned long t1, hsynch, vsynch; + int bits_per_pixel, i, tmp, vesa_idx = 0, videomode; + struct ctfb_res_modes *res_mode; + struct ctfb_res_modes var_mode; + + rinfo = malloc(sizeof(struct radeonfb_info)); + + printf("Video: "); + if(radeon_probe(rinfo)) { + printf("No radeon video card found!\n"); + return NULL; + } + + tmp = 0; + + videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE; + /* get video mode via environment */ + if ((penv = getenv ("videomode")) != NULL) { + /* deceide if it is a string */ + if (penv[0] <= '9') { + videomode = (int) simple_strtoul (penv, NULL, 16); + tmp = 1; + } + } else { + tmp = 1; + } + if (tmp) { + /* parameter are vesa modes */ + /* search params */ + for (i = 0; i < VESA_MODES_COUNT; i++) { + if (vesa_modes[i].vesanr == videomode) + break; + } + if (i == VESA_MODES_COUNT) { + printf ("no VESA Mode found, switching to mode 0x%x ", CONFIG_SYS_DEFAULT_VIDEO_MODE); + i = 0; + } + res_mode = (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i].resindex]; + bits_per_pixel = vesa_modes[i].bits_per_pixel; + vesa_idx = vesa_modes[i].resindex; + } else { + res_mode = (struct ctfb_res_modes *) &var_mode; + bits_per_pixel = video_get_params (res_mode, penv); + } + + /* calculate hsynch and vsynch freq (info only) */ + t1 = (res_mode->left_margin + res_mode->xres + + res_mode->right_margin + res_mode->hsync_len) / 8; + t1 *= 8; + t1 *= res_mode->pixclock; + t1 /= 1000; + hsynch = 1000000000L / t1; + t1 *= (res_mode->upper_margin + res_mode->yres + + res_mode->lower_margin + res_mode->vsync_len); + t1 /= 1000; + vsynch = 1000000000L / t1; + + /* fill in Graphic device struct */ + sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres, + res_mode->yres, bits_per_pixel, (hsynch / 1000), + (vsynch / 1000)); + printf ("%s\n", pGD->modeIdent); + pGD->winSizeX = res_mode->xres; + pGD->winSizeY = res_mode->yres; + pGD->plnSizeX = res_mode->xres; + pGD->plnSizeY = res_mode->yres; + + switch (bits_per_pixel) { + case 24: + pGD->gdfBytesPP = 4; + pGD->gdfIndex = GDF_32BIT_X888RGB; + if (res_mode->xres == 800) { + pGD->winSizeX = 832; + pGD->plnSizeX = 832; + } + break; + case 16: + pGD->gdfBytesPP = 2; + pGD->gdfIndex = GDF_16BIT_565RGB; + if (res_mode->xres == 800) { + pGD->winSizeX = 896; + pGD->plnSizeX = 896; + } + break; + default: + if (res_mode->xres == 800) { + pGD->winSizeX = 1024; + pGD->plnSizeX = 1024; + } + pGD->gdfBytesPP = 1; + pGD->gdfIndex = GDF__8BIT_INDEX; + break; + } + + pGD->isaBase = CONFIG_SYS_ISA_IO_BASE_ADDRESS; + pGD->pciBase = (unsigned int)rinfo->fb_base; + pGD->frameAdrs = (unsigned int)rinfo->fb_base; + pGD->memSize = 64 * 1024 * 1024; + + /* Cursor Start Address */ + pGD->dprBase = (pGD->winSizeX * pGD->winSizeY * pGD->gdfBytesPP) + + (unsigned int)rinfo->fb_base; + if ((pGD->dprBase & 0x0fff) != 0) { + /* allign it */ + pGD->dprBase &= 0xfffff000; + pGD->dprBase += 0x00001000; + } + DPRINT ("Cursor Start %x Pattern Start %x\n", pGD->dprBase, + PATTERN_ADR); + pGD->vprBase = (unsigned int)rinfo->fb_base; /* Dummy */ + pGD->cprBase = (unsigned int)rinfo->fb_base; /* Dummy */ + /* set up Hardware */ + + /* Clear video memory (only visible screen area) */ + i = pGD->winSizeX * pGD->winSizeY * pGD->gdfBytesPP / 4; + vm = (unsigned int *) pGD->pciBase; + while (i--) + *vm++ = 0; + /*SetDrawingEngine (bits_per_pixel);*/ + + if (rinfo->family == CHIP_FAMILY_RV280) + radeon_setmode_9200(vesa_idx, bits_per_pixel); + else + radeon_setmode(); + + return ((void *) pGD); +} + +void video_set_lut (unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + OUTREG(PALETTE_INDEX, index); + OUTREG(PALETTE_DATA, (r << 16) | (g << 8) | b); +} diff --git a/u-boot/drivers/video/ati_radeon_fb.h b/u-boot/drivers/video/ati_radeon_fb.h new file mode 100644 index 0000000..0659045 --- /dev/null +++ b/u-boot/drivers/video/ati_radeon_fb.h @@ -0,0 +1,282 @@ +#ifndef __ATI_RADEON_FB_H +#define __ATI_RADEON_FB_H + +/*************************************************************** + * Most of the definitions here are adapted right from XFree86 * + ***************************************************************/ + +/* + * Chip families. Must fit in the low 16 bits of a long word + */ +enum radeon_family { + CHIP_FAMILY_UNKNOW, + CHIP_FAMILY_LEGACY, + CHIP_FAMILY_RADEON, + CHIP_FAMILY_RV100, + CHIP_FAMILY_RS100, /* U1 (IGP320M) or A3 (IGP320)*/ + CHIP_FAMILY_RV200, + CHIP_FAMILY_RS200, /* U2 (IGP330M/340M/350M) or A4 (IGP330/340/345/350), + RS250 (IGP 7000) */ + CHIP_FAMILY_R200, + CHIP_FAMILY_RV250, + CHIP_FAMILY_RS300, /* Radeon 9000 IGP */ + CHIP_FAMILY_RV280, + CHIP_FAMILY_R300, + CHIP_FAMILY_R350, + CHIP_FAMILY_RV350, + CHIP_FAMILY_RV380, /* RV370/RV380/M22/M24 */ + CHIP_FAMILY_R420, /* R420/R423/M18 */ + CHIP_FAMILY_LAST, +}; + +#define IS_RV100_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_RV100) || \ + ((rinfo)->family == CHIP_FAMILY_RV200) || \ + ((rinfo)->family == CHIP_FAMILY_RS100) || \ + ((rinfo)->family == CHIP_FAMILY_RS200) || \ + ((rinfo)->family == CHIP_FAMILY_RV250) || \ + ((rinfo)->family == CHIP_FAMILY_RV280) || \ + ((rinfo)->family == CHIP_FAMILY_RS300)) + +#define IS_R300_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_R300) || \ + ((rinfo)->family == CHIP_FAMILY_RV350) || \ + ((rinfo)->family == CHIP_FAMILY_R350) || \ + ((rinfo)->family == CHIP_FAMILY_RV380) || \ + ((rinfo)->family == CHIP_FAMILY_R420)) + +struct radeonfb_info { + char name[20]; + + struct pci_device_id pdev; + u16 family; + + u32 fb_base_bus; + u32 mmio_base_bus; + + void *mmio_base; + void *fb_base; + + u32 video_ram; + u32 mapped_vram; + int vram_width; + int vram_ddr; + + u32 fb_local_base; +}; + +#define INREG8(addr) readb((rinfo->mmio_base)+addr) +#define OUTREG8(addr,val) writeb(val, (rinfo->mmio_base)+addr) +#define INREG16(addr) readw((rinfo->mmio_base)+addr) +#define OUTREG16(addr,val) writew(val, (rinfo->mmio_base)+addr) +#define INREG(addr) readl((rinfo->mmio_base)+addr) +#define OUTREG(addr,val) writel(val, (rinfo->mmio_base)+addr) + +static inline void _OUTREGP(struct radeonfb_info *rinfo, u32 addr, + u32 val, u32 mask) +{ + unsigned int tmp; + + tmp = INREG(addr); + tmp &= (mask); + tmp |= (val); + OUTREG(addr, tmp); +} + +#define OUTREGP(addr,val,mask) _OUTREGP(rinfo, addr, val,mask) + +/* + * 2D Engine helper routines + */ +static inline void radeon_engine_flush (struct radeonfb_info *rinfo) +{ + int i; + + /* initiate flush */ + OUTREGP(RB2D_DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL, + ~RB2D_DC_FLUSH_ALL); + + for (i=0; i < 2000000; i++) { + if (!(INREG(RB2D_DSTCACHE_CTLSTAT) & RB2D_DC_BUSY)) + return; + udelay(1); + } + printf("radeonfb: Flush Timeout !\n"); +} + +static inline void _radeon_fifo_wait(struct radeonfb_info *rinfo, int entries) +{ + int i; + + for (i=0; i<2000000; i++) { + if ((INREG(RBBM_STATUS) & 0x7f) >= entries) + return; + udelay(1); + } + printf("radeonfb: FIFO Timeout !\n"); +} + +static inline void _radeon_engine_idle(struct radeonfb_info *rinfo) +{ + int i; + + /* ensure FIFO is empty before waiting for idle */ + _radeon_fifo_wait (rinfo, 64); + + for (i=0; i<2000000; i++) { + if (((INREG(RBBM_STATUS) & GUI_ACTIVE)) == 0) { + radeon_engine_flush (rinfo); + return; + } + udelay(1); + } + printf("radeonfb: Idle Timeout !\n"); +} + +#define radeon_engine_idle() _radeon_engine_idle(rinfo) +#define radeon_fifo_wait(entries) _radeon_fifo_wait(rinfo,entries) +#define radeon_msleep(ms) _radeon_msleep(rinfo,ms) + +/* + * This structure contains the various registers manipulated by this + * driver for setting or restoring a mode. It's mostly copied from + * XFree's RADEONSaveRec structure. A few chip settings might still be + * tweaked without beeing reflected or saved in these registers though + */ +struct radeon_regs { + /* Common registers */ + u32 ovr_clr; + u32 ovr_wid_left_right; + u32 ovr_wid_top_bottom; + u32 ov0_scale_cntl; + u32 mpp_tb_config; + u32 mpp_gp_config; + u32 subpic_cntl; + u32 viph_control; + u32 i2c_cntl_1; + u32 gen_int_cntl; + u32 cap0_trig_cntl; + u32 cap1_trig_cntl; + u32 bus_cntl; + u32 surface_cntl; + u32 bios_5_scratch; + + /* Other registers to save for VT switches or driver load/unload */ + u32 dp_datatype; + u32 rbbm_soft_reset; + u32 clock_cntl_index; + u32 amcgpio_en_reg; + u32 amcgpio_mask; + + /* Surface/tiling registers */ + u32 surf_lower_bound[8]; + u32 surf_upper_bound[8]; + u32 surf_info[8]; + + /* CRTC registers */ + u32 crtc_gen_cntl; + u32 crtc_ext_cntl; + u32 dac_cntl; + u32 crtc_h_total_disp; + u32 crtc_h_sync_strt_wid; + u32 crtc_v_total_disp; + u32 crtc_v_sync_strt_wid; + u32 crtc_offset; + u32 crtc_offset_cntl; + u32 crtc_pitch; + u32 disp_merge_cntl; + u32 grph_buffer_cntl; + u32 crtc_more_cntl; + + /* CRTC2 registers */ + u32 crtc2_gen_cntl; + u32 dac2_cntl; + u32 disp_output_cntl; + u32 disp_hw_debug; + u32 disp2_merge_cntl; + u32 grph2_buffer_cntl; + u32 crtc2_h_total_disp; + u32 crtc2_h_sync_strt_wid; + u32 crtc2_v_total_disp; + u32 crtc2_v_sync_strt_wid; + u32 crtc2_offset; + u32 crtc2_offset_cntl; + u32 crtc2_pitch; + + /* Flat panel regs */ + u32 fp_crtc_h_total_disp; + u32 fp_crtc_v_total_disp; + u32 fp_gen_cntl; + u32 fp2_gen_cntl; + u32 fp_h_sync_strt_wid; + u32 fp2_h_sync_strt_wid; + u32 fp_horz_stretch; + u32 fp_panel_cntl; + u32 fp_v_sync_strt_wid; + u32 fp2_v_sync_strt_wid; + u32 fp_vert_stretch; + u32 lvds_gen_cntl; + u32 lvds_pll_cntl; + u32 tmds_crc; + u32 tmds_transmitter_cntl; + + /* Computed values for PLL */ + u32 dot_clock_freq; + int feedback_div; + int post_div; + + /* PLL registers */ + u32 ppll_div_3; + u32 ppll_ref_div; + u32 vclk_ecp_cntl; + u32 clk_cntl_index; + + /* Computed values for PLL2 */ + u32 dot_clock_freq_2; + int feedback_div_2; + int post_div_2; + + /* PLL2 registers */ + u32 p2pll_ref_div; + u32 p2pll_div_0; + u32 htotal_cntl2; + + /* Palette */ + int palette_valid; +}; + +static inline u32 __INPLL(struct radeonfb_info *rinfo, u32 addr) +{ + u32 data; + + OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000003f); + /* radeon_pll_errata_after_index(rinfo); */ + data = INREG(CLOCK_CNTL_DATA); + /* radeon_pll_errata_after_data(rinfo); */ + return data; +} + +static inline void __OUTPLL(struct radeonfb_info *rinfo, unsigned int index, + u32 val) +{ + + OUTREG8(CLOCK_CNTL_INDEX, (index & 0x0000003f) | 0x00000080); + /* radeon_pll_errata_after_index(rinfo); */ + OUTREG(CLOCK_CNTL_DATA, val); + /* radeon_pll_errata_after_data(rinfo); */ +} + +static inline void __OUTPLLP(struct radeonfb_info *rinfo, unsigned int index, + u32 val, u32 mask) +{ + unsigned int tmp; + + tmp = __INPLL(rinfo, index); + tmp &= (mask); + tmp |= (val); + __OUTPLL(rinfo, index, tmp); +} + +#define INPLL(addr) __INPLL(rinfo, addr) +#define OUTPLL(index, val) __OUTPLL(rinfo, index, val) +#define OUTPLLP(index, val, mask) __OUTPLLP(rinfo, index, val, mask) + +#endif diff --git a/u-boot/drivers/video/atmel_lcdfb.c b/u-boot/drivers/video/atmel_lcdfb.c new file mode 100644 index 0000000..c02ffd8 --- /dev/null +++ b/u-boot/drivers/video/atmel_lcdfb.c @@ -0,0 +1,164 @@ +/* + * Driver for AT91/AT32 LCD Controller + * + * Copyright (C) 2007 Atmel Corporation + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/gpio.h> +#include <asm/arch/clk.h> +#include <lcd.h> +#include <atmel_lcdc.h> + +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; + +void *lcd_base; /* Start of framebuffer memory */ +void *lcd_console_address; /* Start of console buffer */ + +short console_col; +short console_row; + +/* configurable parameters */ +#define ATMEL_LCDC_CVAL_DEFAULT 0xc8 +#define ATMEL_LCDC_DMA_BURST_LEN 8 +#ifndef ATMEL_LCDC_GUARD_TIME +#define ATMEL_LCDC_GUARD_TIME 1 +#endif + +#if defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91CAP9) +#define ATMEL_LCDC_FIFO_SIZE 2048 +#else +#define ATMEL_LCDC_FIFO_SIZE 512 +#endif + +#define lcdc_readl(mmio, reg) __raw_readl((mmio)+(reg)) +#define lcdc_writel(mmio, reg, val) __raw_writel((val), (mmio)+(reg)) + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ +#if defined(CONFIG_ATMEL_LCD_BGR555) + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno), + (red >> 3) | ((green & 0xf8) << 2) | ((blue & 0xf8) << 7)); +#else + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno), + (blue >> 3) | ((green & 0xfc) << 3) | ((red & 0xf8) << 8)); +#endif +} + +void lcd_ctrl_init(void *lcdbase) +{ + unsigned long value; + + /* Turn off the LCD controller and the DMA controller */ + lcdc_writel(panel_info.mmio, ATMEL_LCDC_PWRCON, + ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET); + + /* Wait for the LCDC core to become idle */ + while (lcdc_readl(panel_info.mmio, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY) + udelay(10); + + lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMACON, 0); + + /* Reset LCDC DMA */ + lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST); + + /* ...set frame size and burst length = 8 words (?) */ + value = (panel_info.vl_col * panel_info.vl_row * + NBITS(panel_info.vl_bpix)) / 32; + value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMAFRMCFG, value); + + /* Set pixel clock */ + value = get_lcdc_clk_rate(0) / panel_info.vl_clk; + if (get_lcdc_clk_rate(0) % panel_info.vl_clk) + value++; + value = (value / 2) - 1; + + if (!value) { + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS); + } else + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDCON1, + value << ATMEL_LCDC_CLKVAL_OFFSET); + + /* Initialize control register 2 */ +#ifdef CONFIG_AVR32 + value = ATMEL_LCDC_MEMOR_BIG | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE; +#else + value = ATMEL_LCDC_MEMOR_LITTLE | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE; +#endif + if (panel_info.vl_tft) + value |= ATMEL_LCDC_DISTYPE_TFT; + + value |= panel_info.vl_sync; + value |= (panel_info.vl_bpix << 5); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDCON2, value); + + /* Vertical timing */ + value = (panel_info.vl_vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET; + value |= panel_info.vl_upper_margin << ATMEL_LCDC_VBP_OFFSET; + value |= panel_info.vl_lower_margin; + lcdc_writel(panel_info.mmio, ATMEL_LCDC_TIM1, value); + + /* Horizontal timing */ + value = (panel_info.vl_right_margin - 1) << ATMEL_LCDC_HFP_OFFSET; + value |= (panel_info.vl_hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET; + value |= (panel_info.vl_left_margin - 1); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_TIM2, value); + + /* Display size */ + value = (panel_info.vl_col - 1) << ATMEL_LCDC_HOZVAL_OFFSET; + value |= panel_info.vl_row - 1; + lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDFRMCFG, value); + + /* FIFO Threshold: Use formula from data sheet */ + value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_FIFO, value); + + /* Toggle LCD_MODE every frame */ + lcdc_writel(panel_info.mmio, ATMEL_LCDC_MVAL, 0); + + /* Disable all interrupts */ + lcdc_writel(panel_info.mmio, ATMEL_LCDC_IDR, ~0UL); + + /* Set contrast */ + value = ATMEL_LCDC_PS_DIV8 | + ATMEL_LCDC_ENA_PWMENABLE; + if (!panel_info.vl_cont_pol_low) + value |= ATMEL_LCDC_POL_POSITIVE; + lcdc_writel(panel_info.mmio, ATMEL_LCDC_CONTRAST_CTR, value); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT); + + /* Set framebuffer DMA base address and pixel offset */ + lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMABADDR1, (u_long)lcdbase); + + lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMAEN); + lcdc_writel(panel_info.mmio, ATMEL_LCDC_PWRCON, + (ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR); +} + +ulong calc_fbsize(void) +{ + return ((panel_info.vl_col * panel_info.vl_row * + NBITS(panel_info.vl_bpix)) / 8) + PAGE_SIZE; +} diff --git a/u-boot/drivers/video/bus_vcxk.c b/u-boot/drivers/video/bus_vcxk.c new file mode 100644 index 0000000..67a59a7 --- /dev/null +++ b/u-boot/drivers/video/bus_vcxk.c @@ -0,0 +1,459 @@ +/* + * (C) Copyright 2005-2009 + * Jens Scharsig @ BuS Elektronik GmbH & Co. KG, <esw@bus-elektronik.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#include <common.h> +#include <bmp_layout.h> +#include <asm/io.h> + +vu_char *vcxk_bws = ((vu_char *) (CONFIG_SYS_VCXK_BASE)); +vu_short *vcxk_bws_word = ((vu_short *)(CONFIG_SYS_VCXK_BASE)); +vu_long *vcxk_bws_long = ((vu_long *) (CONFIG_SYS_VCXK_BASE)); + +#ifdef CONFIG_AT91RM9200 + #include <asm/arch/hardware.h> + #include <asm/arch/at91_pio.h> + + #ifndef VCBITMASK + #define VCBITMASK(bitno) (0x0001 << (bitno % 16)) + #endif +#ifndef CONFIG_AT91_LEGACY +at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE; +#define VCXK_INIT_PIN(PORT, PIN, DDR, I0O1) \ + do { \ + writel(PIN, &pio->PORT.per); \ + writel(PIN, &pio->PORT.DDR); \ + writel(PIN, &pio->PORT.mddr); \ + if (!I0O1) \ + writel(PIN, &pio->PORT.puer); \ + } while (0); + +#define VCXK_SET_PIN(PORT, PIN) writel(PIN, &pio->PORT.sodr); +#define VCXK_CLR_PIN(PORT, PIN) writel(PIN, &pio->PORT.codr); + +#define VCXK_ACKNOWLEDGE \ + (!(readl(&pio->CONFIG_SYS_VCXK_ACKNOWLEDGE_PORT.pdsr) & \ + CONFIG_SYS_VCXK_ACKNOWLEDGE_PIN)) +#else + #define VCXK_INIT_PIN(PORT, PIN, DDR, I0O1) \ + ((AT91PS_PIO) PORT)->PIO_PER = PIN; \ + ((AT91PS_PIO) PORT)->DDR = PIN; \ + ((AT91PS_PIO) PORT)->PIO_MDDR = PIN; \ + if (!I0O1) ((AT91PS_PIO) PORT)->PIO_PPUER = PIN; + + #define VCXK_SET_PIN(PORT, PIN) ((AT91PS_PIO) PORT)->PIO_SODR = PIN; + #define VCXK_CLR_PIN(PORT, PIN) ((AT91PS_PIO) PORT)->PIO_CODR = PIN; + + #define VCXK_ACKNOWLEDGE \ + (!(((AT91PS_PIO) CONFIG_SYS_VCXK_ACKNOWLEDGE_PORT)->\ + PIO_PDSR & CONFIG_SYS_VCXK_ACKNOWLEDGE_PIN)) +#endif +#elif defined(CONFIG_MCF52x2) + #include <asm/m5282.h> + #ifndef VCBITMASK + #define VCBITMASK(bitno) (0x8000 >> (bitno % 16)) + #endif + + #define VCXK_INIT_PIN(PORT, PIN, DDR, I0O1) \ + if (I0O1) DDR |= PIN; else DDR &= ~PIN; + + #define VCXK_SET_PIN(PORT, PIN) PORT |= PIN; + #define VCXK_CLR_PIN(PORT, PIN) PORT &= ~PIN; + + #define VCXK_ACKNOWLEDGE \ + (!(CONFIG_SYS_VCXK_ACKNOWLEDGE_PORT & \ + CONFIG_SYS_VCXK_ACKNOWLEDGE_PIN)) + +#else + #error no vcxk support for selected ARCH +#endif + +#define VCXK_DISABLE\ + VCXK_SET_PIN(CONFIG_SYS_VCXK_ENABLE_PORT, CONFIG_SYS_VCXK_ENABLE_PIN) +#define VCXK_ENABLE\ + VCXK_CLR_PIN(CONFIG_SYS_VCXK_ENABLE_PORT, CONFIG_SYS_VCXK_ENABLE_PIN) + +#ifndef CONFIG_SYS_VCXK_DOUBLEBUFFERED + #define VCXK_BWS(x, data) vcxk_bws[x] = data; + #define VCXK_BWS_WORD_SET(x, mask) vcxk_bws_word[x] |= mask; + #define VCXK_BWS_WORD_CLEAR(x, mask) vcxk_bws_word[x] &= ~mask; + #define VCXK_BWS_LONG(x, data) vcxk_bws_long[x] = data; +#else + u_char double_bws[16384]; + u_short *double_bws_word; + u_long *double_bws_long; + #define VCXK_BWS(x,data) \ + double_bws[x] = data; vcxk_bws[x] = data; + #define VCXK_BWS_WORD_SET(x,mask) \ + double_bws_word[x] |= mask; \ + vcxk_bws_word[x] = double_bws_word[x]; + #define VCXK_BWS_WORD_CLEAR(x,mask) \ + double_bws_word[x] &= ~mask; \ + vcxk_bws_word[x] = double_bws_word[x]; + #define VCXK_BWS_LONG(x,data) \ + double_bws_long[x] = data; vcxk_bws_long[x] = data; +#endif + +#define VC4K16_Bright1 vcxk_bws_word[0x20004 / 2] +#define VC4K16_Bright2 vcxk_bws_word[0x20006 / 2] +#define VC2K_Bright vcxk_bws[0x8000] +#define VC8K_BrightH vcxk_bws[0xC000] +#define VC8K_BrightL vcxk_bws[0xC001] + +vu_char VC4K16; + +u_long display_width; +u_long display_height; +u_long display_bwidth; + +ulong search_vcxk_driver(void); +void vcxk_cls(void); +void vcxk_setbrightness(unsigned int side, short brightness); +int vcxk_request(void); +int vcxk_acknowledge_wait(void); +void vcxk_clear(void); + +/* + ****f* bus_vcxk/vcxk_init + * FUNCTION + * initialalize Video Controller + * PARAMETERS + * width visible display width in pixel + * height visible display height in pixel + *** + */ + +int vcxk_init(unsigned long width, unsigned long height) +{ +#ifdef CONFIG_SYS_VCXK_RESET_PORT + VCXK_INIT_PIN(CONFIG_SYS_VCXK_RESET_PORT, + CONFIG_SYS_VCXK_RESET_PIN, CONFIG_SYS_VCXK_RESET_DDR, 1) + VCXK_SET_PIN(CONFIG_SYS_VCXK_RESET_PORT, CONFIG_SYS_VCXK_RESET_PIN); +#endif + +#ifdef CONFIG_SYS_VCXK_DOUBLEBUFFERED + double_bws_word = (u_short *)double_bws; + double_bws_long = (u_long *)double_bws; + debug("%lx %lx %lx \n", double_bws, double_bws_word, double_bws_long); +#endif + display_width = width; + display_height = height; +#if (CONFIG_SYS_VCXK_DEFAULT_LINEALIGN == 4) + display_bwidth = ((width + 31) / 8) & ~0x3; +#elif (CONFIG_SYS_VCXK_DEFAULT_LINEALIGN == 2) + display_bwidth = ((width + 15) / 8) & ~0x1; +#else + #error CONFIG_SYS_VCXK_DEFAULT_LINEALIGN is invalid +#endif + debug("linesize ((%d + 15) / 8 & ~0x1) = %d\n", + display_width, display_bwidth); + +#ifdef CONFIG_SYS_VCXK_AUTODETECT + VC4K16 = 0; + vcxk_bws_long[1] = 0x0; + vcxk_bws_long[1] = 0x55AAAA55; + vcxk_bws_long[5] = 0x0; + if (vcxk_bws_long[1] == 0x55AAAA55) + VC4K16 = 1; +#else + VC4K16 = 1; + debug("No autodetect: use vc4k\n"); +#endif + + VCXK_INIT_PIN(CONFIG_SYS_VCXK_INVERT_PORT, + CONFIG_SYS_VCXK_INVERT_PIN, CONFIG_SYS_VCXK_INVERT_DDR, 1) + VCXK_SET_PIN(CONFIG_SYS_VCXK_INVERT_PORT, CONFIG_SYS_VCXK_INVERT_PIN) + + VCXK_SET_PIN(CONFIG_SYS_VCXK_REQUEST_PORT, CONFIG_SYS_VCXK_REQUEST_PIN); + VCXK_INIT_PIN(CONFIG_SYS_VCXK_REQUEST_PORT, + CONFIG_SYS_VCXK_REQUEST_PIN, CONFIG_SYS_VCXK_REQUEST_DDR, 1) + + VCXK_INIT_PIN(CONFIG_SYS_VCXK_ACKNOWLEDGE_PORT, + CONFIG_SYS_VCXK_ACKNOWLEDGE_PIN, + CONFIG_SYS_VCXK_ACKNOWLEDGE_DDR, 0) + + VCXK_DISABLE; + VCXK_INIT_PIN(CONFIG_SYS_VCXK_ENABLE_PORT, + CONFIG_SYS_VCXK_ENABLE_PIN, CONFIG_SYS_VCXK_ENABLE_DDR, 1) + + vcxk_cls(); + vcxk_cls(); /* clear second/hidden page */ + + vcxk_setbrightness(3, 1000); + VCXK_ENABLE; + return 1; +} + +/* + ****f* bus_vcxk/vcxk_setpixel + * FUNCTION + * set the pixel[x,y] with the given color + * PARAMETER + * x pixel colum + * y pixel row + * color <0x40 off/black + * >0x40 on + *** + */ + +void vcxk_setpixel(int x, int y, unsigned long color) +{ + vu_short dataptr; + + if ((x < display_width) && (y < display_height)) { + dataptr = ((x / 16)) + (y * (display_bwidth >> 1)); + + color = ((color >> 16) & 0xFF) | + ((color >> 8) & 0xFF) | (color & 0xFF); + + if (color > 0x40) { + VCXK_BWS_WORD_SET(dataptr, VCBITMASK(x)); + } else { + VCXK_BWS_WORD_CLEAR(dataptr, VCBITMASK(x)); + } + } +} + +/* + ****f* bus_vcxk/vcxk_loadimage + * FUNCTION + * copies a binary image to display memory + *** + */ + +void vcxk_loadimage(ulong source) +{ + int cnt; + vcxk_acknowledge_wait(); + if (VC4K16) { + for (cnt = 0; cnt < (16384 / 4); cnt++) { + VCXK_BWS_LONG(cnt, (*(ulong *) source)); + source = source + 4; + } + } else { + for (cnt = 0; cnt < 16384; cnt++) { + VCXK_BWS_LONG(cnt*2, (*(vu_char *) source)); + source++; + } + } + vcxk_request(); +} + +/* + ****f* bus_vcxk/vcxk_cls + * FUNCTION + * clear the display + *** + */ + +void vcxk_cls(void) +{ + vcxk_acknowledge_wait(); + vcxk_clear(); + vcxk_request(); +} + +/* + ****f* bus_vcxk/vcxk_clear(void) + * FUNCTION + * clear the display memory + *** + */ + +void vcxk_clear(void) +{ + int cnt; + + for (cnt = 0; cnt < (16384 / 4); cnt++) { + VCXK_BWS_LONG(cnt, 0) + } +} + +/* + ****f* bus_vcxk/vcxk_setbrightness + * FUNCTION + * set the display brightness + * PARAMETER + * side 1 set front side brightness + * 2 set back side brightness + * 3 set brightness for both sides + * brightness 0..1000 + *** + */ + +void vcxk_setbrightness(unsigned int side, short brightness) +{ + if (VC4K16) { + if ((side == 0) || (side & 0x1)) + VC4K16_Bright1 = brightness + 23; + if ((side == 0) || (side & 0x2)) + VC4K16_Bright2 = brightness + 23; + } else { + VC2K_Bright = (brightness >> 4) + 2; + VC8K_BrightH = (brightness + 23) >> 8; + VC8K_BrightL = (brightness + 23) & 0xFF; + } +} + +/* + ****f* bus_vcxk/vcxk_request + * FUNCTION + * requests viewing of display memory + *** + */ + +int vcxk_request(void) +{ + VCXK_CLR_PIN(CONFIG_SYS_VCXK_REQUEST_PORT, + CONFIG_SYS_VCXK_REQUEST_PIN) + VCXK_SET_PIN(CONFIG_SYS_VCXK_REQUEST_PORT, + CONFIG_SYS_VCXK_REQUEST_PIN); + return 1; +} + +/* + ****f* bus_vcxk/vcxk_acknowledge_wait + * FUNCTION + * wait for acknowledge viewing requests + *** + */ + +int vcxk_acknowledge_wait(void) +{ + while (VCXK_ACKNOWLEDGE) + ; + return 1; +} + +/* + ****f* bus_vcxk/vcxk_draw_mono + * FUNCTION + * copies a monochrom bitmap (BMP-Format) from given memory + * PARAMETER + * dataptr pointer to bitmap + * x output bitmap @ columne + * y output bitmap @ row + *** + */ + +void vcxk_draw_mono(unsigned char *dataptr, unsigned long linewidth, + unsigned long cp_width, unsigned long cp_height) +{ + unsigned char *lineptr; + unsigned long xcnt, ycnt; + + for (ycnt = cp_height; ycnt > 0; ycnt--) { + lineptr = dataptr; + for (xcnt = 0; xcnt < cp_width; xcnt++) { + if ((*lineptr << (xcnt % 8)) & 0x80) + vcxk_setpixel(xcnt, ycnt - 1, 0xFFFFFF); + else + vcxk_setpixel(xcnt, ycnt-1, 0); + + if ((xcnt % 8) == 7) + lineptr++; + } /* endfor xcnt */ + dataptr = dataptr + linewidth; + } /* endfor ycnt */ +} + +/* + ****f* bus_vcxk/vcxk_display_bitmap + * FUNCTION + * copies a bitmap (BMP-Format) to the given position + * PARAMETER + * addr pointer to bitmap + * x output bitmap @ columne + * y output bitmap @ row + *** + */ + +int vcxk_display_bitmap(ulong addr, int x, int y) +{ + bmp_image_t *bmp; + unsigned long width; + unsigned long height; + unsigned long bpp; + unsigned long compression; + + unsigned long lw; + + unsigned long c_width; + unsigned long c_height; + unsigned char *dataptr; + + bmp = (bmp_image_t *) addr; + if ((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M')) { + compression = le32_to_cpu(bmp->header.compression); + width = le32_to_cpu(bmp->header.width); + height = le32_to_cpu(bmp->header.height); + bpp = le16_to_cpu(bmp->header.bit_count); + + dataptr = (unsigned char *) bmp + + le32_to_cpu(bmp->header.data_offset); + + if (display_width < (width + x)) + c_width = display_width - x; + else + c_width = width; + if (display_height < (height + y)) + c_height = display_height - y; + else + c_height = height; + + lw = (((width + 7) / 8) + 3) & ~0x3; + + if (c_height < height) + dataptr = dataptr + lw * (height - c_height); + switch (bpp) { + case 1: + vcxk_draw_mono(dataptr, lw, c_width, c_height); + break; + default: + printf("Error: %ld bit per pixel " + "not supported by VCxK\n", bpp); + return 0; + } + } else { + printf("Error: no valid bmp at %lx\n", (ulong) bmp); + return 0; + } + return 1; +} + +/* + ****f* bus_vcxk/video_display_bitmap + *** + */ + +int video_display_bitmap(ulong addr, int x, int y) +{ + vcxk_acknowledge_wait(); + if (vcxk_display_bitmap(addr, x, y)) { + vcxk_request(); + return 0; + } + return 1; +} + +/* EOF */ diff --git a/u-boot/drivers/video/cfb_console.c b/u-boot/drivers/video/cfb_console.c new file mode 100644 index 0000000..dd849c2 --- /dev/null +++ b/u-boot/drivers/video/cfb_console.c @@ -0,0 +1,1671 @@ +/* + * (C) Copyright 2002 ELTEC Elektronik AG + * Frank Gottschling <fgottschling@eltec.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * cfb_console.c + * + * Color Framebuffer Console driver for 8/15/16/24/32 bits per pixel. + * + * At the moment only the 8x16 font is tested and the font fore- and + * background color is limited to black/white/gray colors. The Linux + * logo can be placed in the upper left corner and additional board + * information strings (that normaly goes to serial port) can be drawed. + * + * The console driver can use the standard PC keyboard interface (i8042) + * for character input. Character output goes to a memory mapped video + * framebuffer with little or big-endian organisation. + * With environment setting 'console=serial' the console i/o can be + * forced to serial port. + + The driver uses graphic specific defines/parameters/functions: + + (for SMI LynxE graphic chip) + + CONFIG_VIDEO_SMI_LYNXEM - use graphic driver for SMI 710,712,810 + VIDEO_FB_LITTLE_ENDIAN - framebuffer organisation default: big endian + VIDEO_HW_RECTFILL - graphic driver supports hardware rectangle fill + VIDEO_HW_BITBLT - graphic driver supports hardware bit blt + + Console Parameters are set by graphic drivers global struct: + + VIDEO_VISIBLE_COLS - x resolution + VIDEO_VISIBLE_ROWS - y resolution + VIDEO_PIXEL_SIZE - storage size in byte per pixel + VIDEO_DATA_FORMAT - graphical data format GDF + VIDEO_FB_ADRS - start of video memory + + CONFIG_I8042_KBD - AT Keyboard driver for i8042 + VIDEO_KBD_INIT_FCT - init function for keyboard + VIDEO_TSTC_FCT - keyboard_tstc function + VIDEO_GETC_FCT - keyboard_getc function + + CONFIG_CONSOLE_CURSOR - on/off drawing cursor is done with delay + loop in VIDEO_TSTC_FCT (i8042) + CONFIG_SYS_CONSOLE_BLINK_COUNT - value for delay loop - blink rate + CONFIG_CONSOLE_TIME - display time/date in upper right corner, + needs CONFIG_CMD_DATE and CONFIG_CONSOLE_CURSOR + CONFIG_VIDEO_LOGO - display Linux Logo in upper left corner + CONFIG_VIDEO_BMP_LOGO - use bmp_logo instead of linux_logo + CONFIG_CONSOLE_EXTRA_INFO - display additional board information strings + that normaly goes to serial port. This define + requires a board specific function: + video_drawstring (VIDEO_INFO_X, + VIDEO_INFO_Y + i*VIDEO_FONT_HEIGHT, + info); + that fills a info buffer at i=row. + s.a: board/eltec/bab7xx. +CONFIG_VGA_AS_SINGLE_DEVICE - If set the framebuffer device will be initialised + as an output only device. The Keyboard driver + will not be set-up. This may be used, if you + have none or more than one Keyboard devices + (USB Keyboard, AT Keyboard). + +CONFIG_VIDEO_SW_CURSOR: - Draws a cursor after the last character. No + blinking is provided. Uses the macros CURSOR_SET + and CURSOR_OFF. +CONFIG_VIDEO_HW_CURSOR: - Uses the hardware cursor capability of the + graphic chip. Uses the macro CURSOR_SET. + ATTENTION: If booting an OS, the display driver + must disable the hardware register of the graphic + chip. Otherwise a blinking field is displayed +*/ + +#include <common.h> + +#include <malloc.h> + +/*****************************************************************************/ +/* Console device defines with SMI graphic */ +/* Any other graphic must change this section */ +/*****************************************************************************/ + +#ifdef CONFIG_VIDEO_SMI_LYNXEM + +#define VIDEO_FB_LITTLE_ENDIAN +#define VIDEO_HW_RECTFILL +#define VIDEO_HW_BITBLT +#endif + +/*****************************************************************************/ +/* Defines for the CT69000 driver */ +/*****************************************************************************/ +#ifdef CONFIG_VIDEO_CT69000 + +#define VIDEO_FB_LITTLE_ENDIAN +#define VIDEO_HW_RECTFILL +#define VIDEO_HW_BITBLT +#endif + +/*****************************************************************************/ +/* Defines for the SED13806 driver */ +/*****************************************************************************/ +#ifdef CONFIG_VIDEO_SED13806 + +#ifndef CONFIG_TOTAL5200 +#define VIDEO_FB_LITTLE_ENDIAN +#endif +#define VIDEO_HW_RECTFILL +#define VIDEO_HW_BITBLT +#endif + +/*****************************************************************************/ +/* Defines for the SED13806 driver */ +/*****************************************************************************/ +#ifdef CONFIG_VIDEO_SM501 + +#ifdef CONFIG_HH405 +#define VIDEO_FB_LITTLE_ENDIAN +#endif +#endif + +/*****************************************************************************/ +/* Defines for the MB862xx driver */ +/*****************************************************************************/ +#ifdef CONFIG_VIDEO_MB862xx + +#ifdef CONFIG_VIDEO_CORALP +#define VIDEO_FB_LITTLE_ENDIAN +#endif +#ifdef CONFIG_VIDEO_MB862xx_ACCEL +#define VIDEO_HW_RECTFILL +#define VIDEO_HW_BITBLT +#endif +#endif + +/*****************************************************************************/ +/* Include video_fb.h after definitions of VIDEO_HW_RECTFILL etc */ +/*****************************************************************************/ +#include <video_fb.h> + +/*****************************************************************************/ +/* some Macros */ +/*****************************************************************************/ +#define VIDEO_VISIBLE_COLS (pGD->winSizeX) +#define VIDEO_VISIBLE_ROWS (pGD->winSizeY) +#define VIDEO_PIXEL_SIZE (pGD->gdfBytesPP) +#define VIDEO_DATA_FORMAT (pGD->gdfIndex) +#define VIDEO_FB_ADRS (pGD->frameAdrs) + +/*****************************************************************************/ +/* Console device defines with i8042 keyboard controller */ +/* Any other keyboard controller must change this section */ +/*****************************************************************************/ + +#ifdef CONFIG_I8042_KBD +#include <i8042.h> + +#define VIDEO_KBD_INIT_FCT i8042_kbd_init() +#define VIDEO_TSTC_FCT i8042_tstc +#define VIDEO_GETC_FCT i8042_getc +#endif + +/*****************************************************************************/ +/* Console device */ +/*****************************************************************************/ + +#include <version.h> +#include <linux/types.h> +#include <stdio_dev.h> +#include <video_font.h> + +#if defined(CONFIG_CMD_DATE) +#include <rtc.h> +#endif + +#if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN) +#include <watchdog.h> +#include <bmp_layout.h> + +#ifdef CONFIG_SPLASH_SCREEN_ALIGN +#define BMP_ALIGN_CENTER 0x7FFF +#endif + +#endif + +/*****************************************************************************/ +/* Cursor definition: */ +/* CONFIG_CONSOLE_CURSOR: Uses a timer function (see drivers/input/i8042.c) */ +/* to let the cursor blink. Uses the macros */ +/* CURSOR_OFF and CURSOR_ON. */ +/* CONFIG_VIDEO_SW_CURSOR: Draws a cursor after the last character. No */ +/* blinking is provided. Uses the macros CURSOR_SET */ +/* and CURSOR_OFF. */ +/* CONFIG_VIDEO_HW_CURSOR: Uses the hardware cursor capability of the */ +/* graphic chip. Uses the macro CURSOR_SET. */ +/* ATTENTION: If booting an OS, the display driver */ +/* must disable the hardware register of the graphic */ +/* chip. Otherwise a blinking field is displayed */ +/*****************************************************************************/ +#if !defined(CONFIG_CONSOLE_CURSOR) && \ + !defined(CONFIG_VIDEO_SW_CURSOR) && \ + !defined(CONFIG_VIDEO_HW_CURSOR) +/* no Cursor defined */ +#define CURSOR_ON +#define CURSOR_OFF +#define CURSOR_SET +#endif + +#ifdef CONFIG_CONSOLE_CURSOR +#ifdef CURSOR_ON +#error only one of CONFIG_CONSOLE_CURSOR,CONFIG_VIDEO_SW_CURSOR,CONFIG_VIDEO_HW_CURSOR can be defined +#endif +void console_cursor (int state); +#define CURSOR_ON console_cursor(1) +#define CURSOR_OFF console_cursor(0) +#define CURSOR_SET +#ifndef CONFIG_I8042_KBD +#warning Cursor drawing on/off needs timer function s.a. drivers/input/i8042.c +#endif +#else +#ifdef CONFIG_CONSOLE_TIME +#error CONFIG_CONSOLE_CURSOR must be defined for CONFIG_CONSOLE_TIME +#endif +#endif /* CONFIG_CONSOLE_CURSOR */ + +#ifdef CONFIG_VIDEO_SW_CURSOR +#ifdef CURSOR_ON +#error only one of CONFIG_CONSOLE_CURSOR,CONFIG_VIDEO_SW_CURSOR,CONFIG_VIDEO_HW_CURSOR can be defined +#endif +#define CURSOR_ON +#define CURSOR_OFF video_putchar(console_col * VIDEO_FONT_WIDTH,\ + console_row * VIDEO_FONT_HEIGHT, ' ') +#define CURSOR_SET video_set_cursor() +#endif /* CONFIG_VIDEO_SW_CURSOR */ + + +#ifdef CONFIG_VIDEO_HW_CURSOR +#ifdef CURSOR_ON +#error only one of CONFIG_CONSOLE_CURSOR,CONFIG_VIDEO_SW_CURSOR,CONFIG_VIDEO_HW_CURSOR can be defined +#endif +#define CURSOR_ON +#define CURSOR_OFF +#define CURSOR_SET video_set_hw_cursor(console_col * VIDEO_FONT_WIDTH, \ + (console_row * VIDEO_FONT_HEIGHT) + video_logo_height) +#endif /* CONFIG_VIDEO_HW_CURSOR */ + +#ifdef CONFIG_VIDEO_LOGO +#ifdef CONFIG_VIDEO_BMP_LOGO +#include <bmp_logo.h> +#define VIDEO_LOGO_WIDTH BMP_LOGO_WIDTH +#define VIDEO_LOGO_HEIGHT BMP_LOGO_HEIGHT +#define VIDEO_LOGO_LUT_OFFSET BMP_LOGO_OFFSET +#define VIDEO_LOGO_COLORS BMP_LOGO_COLORS + +#else /* CONFIG_VIDEO_BMP_LOGO */ +#define LINUX_LOGO_WIDTH 80 +#define LINUX_LOGO_HEIGHT 80 +#define LINUX_LOGO_COLORS 214 +#define LINUX_LOGO_LUT_OFFSET 0x20 +#define __initdata +#include <linux_logo.h> +#define VIDEO_LOGO_WIDTH LINUX_LOGO_WIDTH +#define VIDEO_LOGO_HEIGHT LINUX_LOGO_HEIGHT +#define VIDEO_LOGO_LUT_OFFSET LINUX_LOGO_LUT_OFFSET +#define VIDEO_LOGO_COLORS LINUX_LOGO_COLORS +#endif /* CONFIG_VIDEO_BMP_LOGO */ +#define VIDEO_INFO_X (VIDEO_LOGO_WIDTH) +#define VIDEO_INFO_Y (VIDEO_FONT_HEIGHT/2) +#else /* CONFIG_VIDEO_LOGO */ +#define VIDEO_LOGO_WIDTH 0 +#define VIDEO_LOGO_HEIGHT 0 +#endif /* CONFIG_VIDEO_LOGO */ + +#define VIDEO_COLS VIDEO_VISIBLE_COLS +#define VIDEO_ROWS VIDEO_VISIBLE_ROWS +#define VIDEO_SIZE (VIDEO_ROWS*VIDEO_COLS*VIDEO_PIXEL_SIZE) +#define VIDEO_PIX_BLOCKS (VIDEO_SIZE >> 2) +#define VIDEO_LINE_LEN (VIDEO_COLS*VIDEO_PIXEL_SIZE) +#define VIDEO_BURST_LEN (VIDEO_COLS/8) + +#ifdef CONFIG_VIDEO_LOGO +#define CONSOLE_ROWS ((VIDEO_ROWS - video_logo_height) / VIDEO_FONT_HEIGHT) +#else +#define CONSOLE_ROWS (VIDEO_ROWS / VIDEO_FONT_HEIGHT) +#endif + +#define CONSOLE_COLS (VIDEO_COLS / VIDEO_FONT_WIDTH) +#define CONSOLE_ROW_SIZE (VIDEO_FONT_HEIGHT * VIDEO_LINE_LEN) +#define CONSOLE_ROW_FIRST (video_console_address) +#define CONSOLE_ROW_SECOND (video_console_address + CONSOLE_ROW_SIZE) +#define CONSOLE_ROW_LAST (video_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE) +#define CONSOLE_SIZE (CONSOLE_ROW_SIZE * CONSOLE_ROWS) +#define CONSOLE_SCROLL_SIZE (CONSOLE_SIZE - CONSOLE_ROW_SIZE) + +/* Macros */ +#ifdef VIDEO_FB_LITTLE_ENDIAN +#define SWAP16(x) ((((x) & 0x00ff) << 8) | ( (x) >> 8)) +#define SWAP32(x) ((((x) & 0x000000ff) << 24) | (((x) & 0x0000ff00) << 8)|\ + (((x) & 0x00ff0000) >> 8) | (((x) & 0xff000000) >> 24) ) +#define SHORTSWAP32(x) ((((x) & 0x000000ff) << 8) | (((x) & 0x0000ff00) >> 8)|\ + (((x) & 0x00ff0000) << 8) | (((x) & 0xff000000) >> 8) ) +#else +#define SWAP16(x) (x) +#define SWAP32(x) (x) +#if defined(VIDEO_FB_16BPP_WORD_SWAP) +#define SHORTSWAP32(x) ( ((x) >> 16) | ((x) << 16) ) +#else +#define SHORTSWAP32(x) (x) +#endif +#endif + +#if defined(DEBUG) || defined(DEBUG_CFB_CONSOLE) +#define PRINTD(x) printf(x) +#else +#define PRINTD(x) +#endif + + +#ifdef CONFIG_CONSOLE_EXTRA_INFO +extern void video_get_info_str ( /* setup a board string: type, speed, etc. */ + int line_number, /* location to place info string beside logo */ + char *info /* buffer for info string */ + ); + +#endif + +/* Locals */ +static GraphicDevice *pGD; /* Pointer to Graphic array */ + +static void *video_fb_address; /* frame buffer address */ +static void *video_console_address; /* console buffer start address */ + +static int video_logo_height = VIDEO_LOGO_HEIGHT; + +static int console_col = 0; /* cursor col */ +static int console_row = 0; /* cursor row */ + +static u32 eorx, fgx, bgx; /* color pats */ + +static const int video_font_draw_table8[] = { + 0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, + 0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff, + 0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff, + 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff }; + +static const int video_font_draw_table15[] = { + 0x00000000, 0x00007fff, 0x7fff0000, 0x7fff7fff }; + +static const int video_font_draw_table16[] = { + 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff }; + +static const int video_font_draw_table24[16][3] = { + { 0x00000000, 0x00000000, 0x00000000 }, + { 0x00000000, 0x00000000, 0x00ffffff }, + { 0x00000000, 0x0000ffff, 0xff000000 }, + { 0x00000000, 0x0000ffff, 0xffffffff }, + { 0x000000ff, 0xffff0000, 0x00000000 }, + { 0x000000ff, 0xffff0000, 0x00ffffff }, + { 0x000000ff, 0xffffffff, 0xff000000 }, + { 0x000000ff, 0xffffffff, 0xffffffff }, + { 0xffffff00, 0x00000000, 0x00000000 }, + { 0xffffff00, 0x00000000, 0x00ffffff }, + { 0xffffff00, 0x0000ffff, 0xff000000 }, + { 0xffffff00, 0x0000ffff, 0xffffffff }, + { 0xffffffff, 0xffff0000, 0x00000000 }, + { 0xffffffff, 0xffff0000, 0x00ffffff }, + { 0xffffffff, 0xffffffff, 0xff000000 }, + { 0xffffffff, 0xffffffff, 0xffffffff } }; + +static const int video_font_draw_table32[16][4] = { + { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00000000, 0x00000000, 0x00000000, 0x00ffffff }, + { 0x00000000, 0x00000000, 0x00ffffff, 0x00000000 }, + { 0x00000000, 0x00000000, 0x00ffffff, 0x00ffffff }, + { 0x00000000, 0x00ffffff, 0x00000000, 0x00000000 }, + { 0x00000000, 0x00ffffff, 0x00000000, 0x00ffffff }, + { 0x00000000, 0x00ffffff, 0x00ffffff, 0x00000000 }, + { 0x00000000, 0x00ffffff, 0x00ffffff, 0x00ffffff }, + { 0x00ffffff, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00ffffff, 0x00000000, 0x00000000, 0x00ffffff }, + { 0x00ffffff, 0x00000000, 0x00ffffff, 0x00000000 }, + { 0x00ffffff, 0x00000000, 0x00ffffff, 0x00ffffff }, + { 0x00ffffff, 0x00ffffff, 0x00000000, 0x00000000 }, + { 0x00ffffff, 0x00ffffff, 0x00000000, 0x00ffffff }, + { 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00000000 }, + { 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff } }; + + +/******************************************************************************/ + +static void video_drawchars (int xx, int yy, unsigned char *s, int count) +{ + u8 *cdat, *dest, *dest0; + int rows, offset, c; + + offset = yy * VIDEO_LINE_LEN + xx * VIDEO_PIXEL_SIZE; + dest0 = video_fb_address + offset; + + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + case GDF__8BIT_332RGB: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; + dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = (video_font_draw_table8[bits >> 4] & eorx) ^ bgx; + ((u32 *) dest)[1] = (video_font_draw_table8[bits & 15] & eorx) ^ bgx; + } + dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE; + s++; + } + break; + + case GDF_15BIT_555RGB: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; + dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = SHORTSWAP32 ((video_font_draw_table15 [bits >> 6] & eorx) ^ bgx); + ((u32 *) dest)[1] = SHORTSWAP32 ((video_font_draw_table15 [bits >> 4 & 3] & eorx) ^ bgx); + ((u32 *) dest)[2] = SHORTSWAP32 ((video_font_draw_table15 [bits >> 2 & 3] & eorx) ^ bgx); + ((u32 *) dest)[3] = SHORTSWAP32 ((video_font_draw_table15 [bits & 3] & eorx) ^ bgx); + } + dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE; + s++; + } + break; + + case GDF_16BIT_565RGB: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; + dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = SHORTSWAP32 ((video_font_draw_table16 [bits >> 6] & eorx) ^ bgx); + ((u32 *) dest)[1] = SHORTSWAP32 ((video_font_draw_table16 [bits >> 4 & 3] & eorx) ^ bgx); + ((u32 *) dest)[2] = SHORTSWAP32 ((video_font_draw_table16 [bits >> 2 & 3] & eorx) ^ bgx); + ((u32 *) dest)[3] = SHORTSWAP32 ((video_font_draw_table16 [bits & 3] & eorx) ^ bgx); + } + dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE; + s++; + } + break; + + case GDF_32BIT_X888RGB: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; + dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = SWAP32 ((video_font_draw_table32 [bits >> 4][0] & eorx) ^ bgx); + ((u32 *) dest)[1] = SWAP32 ((video_font_draw_table32 [bits >> 4][1] & eorx) ^ bgx); + ((u32 *) dest)[2] = SWAP32 ((video_font_draw_table32 [bits >> 4][2] & eorx) ^ bgx); + ((u32 *) dest)[3] = SWAP32 ((video_font_draw_table32 [bits >> 4][3] & eorx) ^ bgx); + ((u32 *) dest)[4] = SWAP32 ((video_font_draw_table32 [bits & 15][0] & eorx) ^ bgx); + ((u32 *) dest)[5] = SWAP32 ((video_font_draw_table32 [bits & 15][1] & eorx) ^ bgx); + ((u32 *) dest)[6] = SWAP32 ((video_font_draw_table32 [bits & 15][2] & eorx) ^ bgx); + ((u32 *) dest)[7] = SWAP32 ((video_font_draw_table32 [bits & 15][3] & eorx) ^ bgx); + } + dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE; + s++; + } + break; + + case GDF_24BIT_888RGB: + while (count--) { + c = *s; + cdat = video_fontdata + c * VIDEO_FONT_HEIGHT; + for (rows = VIDEO_FONT_HEIGHT, dest = dest0; + rows--; + dest += VIDEO_LINE_LEN) { + u8 bits = *cdat++; + + ((u32 *) dest)[0] = (video_font_draw_table24[bits >> 4][0] & eorx) ^ bgx; + ((u32 *) dest)[1] = (video_font_draw_table24[bits >> 4][1] & eorx) ^ bgx; + ((u32 *) dest)[2] = (video_font_draw_table24[bits >> 4][2] & eorx) ^ bgx; + ((u32 *) dest)[3] = (video_font_draw_table24[bits & 15][0] & eorx) ^ bgx; + ((u32 *) dest)[4] = (video_font_draw_table24[bits & 15][1] & eorx) ^ bgx; + ((u32 *) dest)[5] = (video_font_draw_table24[bits & 15][2] & eorx) ^ bgx; + } + dest0 += VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE; + s++; + } + break; + } +} + +/*****************************************************************************/ + +static inline void video_drawstring (int xx, int yy, unsigned char *s) +{ + video_drawchars (xx, yy, s, strlen ((char *)s)); +} + +/*****************************************************************************/ + +static void video_putchar (int xx, int yy, unsigned char c) +{ + video_drawchars (xx, yy + video_logo_height, &c, 1); +} + +/*****************************************************************************/ +#if defined(CONFIG_CONSOLE_CURSOR) || defined(CONFIG_VIDEO_SW_CURSOR) +static void video_set_cursor (void) +{ + /* swap drawing colors */ + eorx = fgx; + fgx = bgx; + bgx = eorx; + eorx = fgx ^ bgx; + /* draw cursor */ + video_putchar (console_col * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT, + ' '); + /* restore drawing colors */ + eorx = fgx; + fgx = bgx; + bgx = eorx; + eorx = fgx ^ bgx; +} +#endif +/*****************************************************************************/ +#ifdef CONFIG_CONSOLE_CURSOR +void console_cursor (int state) +{ + static int last_state = 0; + +#ifdef CONFIG_CONSOLE_TIME + struct rtc_time tm; + char info[16]; + + /* time update only if cursor is on (faster scroll) */ + if (state) { + rtc_get (&tm); + + sprintf (info, " %02d:%02d:%02d ", tm.tm_hour, tm.tm_min, + tm.tm_sec); + video_drawstring (VIDEO_VISIBLE_COLS - 10 * VIDEO_FONT_WIDTH, + VIDEO_INFO_Y, (uchar *)info); + + sprintf (info, "%02d.%02d.%04d", tm.tm_mday, tm.tm_mon, + tm.tm_year); + video_drawstring (VIDEO_VISIBLE_COLS - 10 * VIDEO_FONT_WIDTH, + VIDEO_INFO_Y + 1 * VIDEO_FONT_HEIGHT, (uchar *)info); + } +#endif + + if (state && (last_state != state)) { + video_set_cursor (); + } + + if (!state && (last_state != state)) { + /* clear cursor */ + video_putchar (console_col * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT, + ' '); + } + + last_state = state; +} +#endif + +/*****************************************************************************/ + +#ifndef VIDEO_HW_RECTFILL +static void memsetl (int *p, int c, int v) +{ + while (c--) + *(p++) = v; +} +#endif + +/*****************************************************************************/ + +#ifndef VIDEO_HW_BITBLT +static void memcpyl (int *d, int *s, int c) +{ + while (c--) + *(d++) = *(s++); +} +#endif + +/*****************************************************************************/ + +static void console_scrollup (void) +{ + /* copy up rows ignoring the first one */ + +#ifdef VIDEO_HW_BITBLT + video_hw_bitblt (VIDEO_PIXEL_SIZE, /* bytes per pixel */ + 0, /* source pos x */ + video_logo_height + VIDEO_FONT_HEIGHT, /* source pos y */ + 0, /* dest pos x */ + video_logo_height, /* dest pos y */ + VIDEO_VISIBLE_COLS, /* frame width */ + VIDEO_VISIBLE_ROWS - video_logo_height - VIDEO_FONT_HEIGHT /* frame height */ + ); +#else + memcpyl (CONSOLE_ROW_FIRST, CONSOLE_ROW_SECOND, + CONSOLE_SCROLL_SIZE >> 2); +#endif + + /* clear the last one */ +#ifdef VIDEO_HW_RECTFILL + video_hw_rectfill (VIDEO_PIXEL_SIZE, /* bytes per pixel */ + 0, /* dest pos x */ + VIDEO_VISIBLE_ROWS - VIDEO_FONT_HEIGHT, /* dest pos y */ + VIDEO_VISIBLE_COLS, /* frame width */ + VIDEO_FONT_HEIGHT, /* frame height */ + CONSOLE_BG_COL /* fill color */ + ); +#else + memsetl (CONSOLE_ROW_LAST, CONSOLE_ROW_SIZE >> 2, CONSOLE_BG_COL); +#endif +} + +/*****************************************************************************/ + +static void console_back (void) +{ + CURSOR_OFF; + console_col--; + + if (console_col < 0) { + console_col = CONSOLE_COLS - 1; + console_row--; + if (console_row < 0) + console_row = 0; + } + video_putchar (console_col * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT, + ' '); +} + +/*****************************************************************************/ + +static void console_newline (void) +{ + /* Check if last character in the line was just drawn. If so, cursor was + overwriten and need not to be cleared. Cursor clearing without this + check causes overwriting the 1st character of the line if line lenght + is >= CONSOLE_COLS + */ + if (console_col < CONSOLE_COLS) + CURSOR_OFF; + console_row++; + console_col = 0; + + /* Check if we need to scroll the terminal */ + if (console_row >= CONSOLE_ROWS) { + /* Scroll everything up */ + console_scrollup (); + + /* Decrement row number */ + console_row--; + } +} + +static void console_cr (void) +{ + CURSOR_OFF; + console_col = 0; +} + +/*****************************************************************************/ + +void video_putc (const char c) +{ + static int nl = 1; + + switch (c) { + case 13: /* back to first column */ + console_cr (); + break; + + case '\n': /* next line */ + if (console_col || (!console_col && nl)) + console_newline (); + nl = 1; + break; + + case 9: /* tab 8 */ + CURSOR_OFF; + console_col |= 0x0008; + console_col &= ~0x0007; + + if (console_col >= CONSOLE_COLS) + console_newline (); + break; + + case 8: /* backspace */ + console_back (); + break; + + default: /* draw the char */ + video_putchar (console_col * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT, + c); + console_col++; + + /* check for newline */ + if (console_col >= CONSOLE_COLS) { + console_newline (); + nl = 0; + } + } + CURSOR_SET; +} + + +/*****************************************************************************/ + +void video_puts (const char *s) +{ + int count = strlen (s); + + while (count--) + video_putc (*s++); +} + +/*****************************************************************************/ + +/* + * Do not enforce drivers (or board code) to provide empty + * video_set_lut() if they do not support 8 bpp format. + * Implement weak default function instead. + */ +void __video_set_lut (unsigned int index, unsigned char r, + unsigned char g, unsigned char b) +{ +} +void video_set_lut (unsigned int, unsigned char, unsigned char, unsigned char) + __attribute__((weak, alias("__video_set_lut"))); + +#if defined(CONFIG_CMD_BMP) || defined(CONFIG_SPLASH_SCREEN) + +#define FILL_8BIT_332RGB(r,g,b) { \ + *fb = ((r>>5)<<5) | ((g>>5)<<2) | (b>>6); \ + fb ++; \ +} + +#define FILL_15BIT_555RGB(r,g,b) { \ + *(unsigned short *)fb = SWAP16((unsigned short)(((r>>3)<<10) | ((g>>3)<<5) | (b>>3))); \ + fb += 2; \ +} + +#define FILL_16BIT_565RGB(r,g,b) { \ + *(unsigned short *)fb = SWAP16((unsigned short)((((r)>>3)<<11) | (((g)>>2)<<5) | ((b)>>3))); \ + fb += 2; \ +} + +#define FILL_32BIT_X888RGB(r,g,b) { \ + *(unsigned long *)fb = SWAP32((unsigned long)(((r<<16) | (g<<8) | b))); \ + fb += 4; \ +} + +#ifdef VIDEO_FB_LITTLE_ENDIAN +#define FILL_24BIT_888RGB(r,g,b) { \ + fb[0] = b; \ + fb[1] = g; \ + fb[2] = r; \ + fb += 3; \ +} +#else +#define FILL_24BIT_888RGB(r,g,b) { \ + fb[0] = r; \ + fb[1] = g; \ + fb[2] = b; \ + fb += 3; \ +} +#endif + +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) +static void inline fill_555rgb_pswap(uchar *fb, int x, + u8 r, u8 g, u8 b) +{ + ushort *dst = (ushort *)fb; + ushort color = (ushort)(((r >> 3) << 10) | + ((g >> 3) << 5) | + (b >> 3)); + if (x & 1) + *(--dst) = color; + else + *(++dst) = color; +} +#endif + +/* + * RLE8 bitmap support + */ + +#ifdef CONFIG_VIDEO_BMP_RLE8 +/* Pre-calculated color table entry */ +struct palette { + union { + unsigned short w; /* word */ + unsigned int dw; /* double word */ + } ce; /* color entry */ +}; + +/* + * Helper to draw encoded/unencoded run. + */ +static void draw_bitmap (uchar **fb, uchar *bm, struct palette *p, + int cnt, int enc) +{ + ulong addr = (ulong)*fb; + int *off; + int enc_off = 1; + int i; + + /* + * Setup offset of the color index in the bitmap. + * Color index of encoded run is at offset 1. + */ + off = enc ? &enc_off : &i; + + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + for (i = 0; i < cnt; i++) + *(unsigned char *)addr++ = bm[*off]; + break; + case GDF_15BIT_555RGB: + case GDF_16BIT_565RGB: + /* differences handled while pre-calculating palette */ + for (i = 0; i < cnt; i++) { + *(unsigned short *)addr = p[bm[*off]].ce.w; + addr += 2; + } + break; + case GDF_32BIT_X888RGB: + for (i = 0; i < cnt; i++) { + *(unsigned long *)addr = p[bm[*off]].ce.dw; + addr += 4; + } + break; + } + *fb = (uchar *)addr; /* return modified address */ +} + +static int display_rle8_bitmap (bmp_image_t *img, int xoff, int yoff, + int width, int height) +{ + unsigned char *bm; + unsigned char *fbp; + unsigned int cnt, runlen; + int decode = 1; + int x, y, bpp, i, ncolors; + struct palette p[256]; + bmp_color_table_entry_t cte; + int green_shift, red_off; + + x = 0; + y = __le32_to_cpu(img->header.height) - 1; + ncolors = __le32_to_cpu(img->header.colors_used); + bpp = VIDEO_PIXEL_SIZE; + fbp = (unsigned char *)((unsigned int)video_fb_address + + (((y + yoff) * VIDEO_COLS) + xoff) * bpp); + + bm = (uchar *)img + __le32_to_cpu(img->header.data_offset); + + /* pre-calculate and setup palette */ + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + for (i = 0; i < ncolors; i++) { + cte = img->color_table[i]; + video_set_lut (i, cte.red, cte.green, cte.blue); + } + break; + case GDF_15BIT_555RGB: + case GDF_16BIT_565RGB: + if (VIDEO_DATA_FORMAT == GDF_15BIT_555RGB) { + green_shift = 3; + red_off = 10; + } else { + green_shift = 2; + red_off = 11; + } + for (i = 0; i < ncolors; i++) { + cte = img->color_table[i]; + p[i].ce.w = SWAP16((unsigned short) + (((cte.red >> 3) << red_off) | + ((cte.green >> green_shift) << 5) | + cte.blue >> 3)); + } + break; + case GDF_32BIT_X888RGB: + for (i = 0; i < ncolors; i++) { + cte = img->color_table[i]; + p[i].ce.dw = SWAP32((cte.red << 16) | (cte.green << 8) | + cte.blue); + } + break; + default: + printf("RLE Bitmap unsupported in video mode 0x%x\n", + VIDEO_DATA_FORMAT); + return -1; + } + + while (decode) { + switch (bm[0]) { + case 0: + switch (bm[1]) { + case 0: + /* scan line end marker */ + bm += 2; + x = 0; + y--; + fbp = (unsigned char *) + ((unsigned int)video_fb_address + + (((y + yoff) * VIDEO_COLS) + + xoff) * bpp); + continue; + case 1: + /* end of bitmap data marker */ + decode = 0; + break; + case 2: + /* run offset marker */ + x += bm[2]; + y -= bm[3]; + fbp = (unsigned char *) + ((unsigned int)video_fb_address + + (((y + yoff) * VIDEO_COLS) + + x + xoff) * bpp); + bm += 4; + break; + default: + /* unencoded run */ + cnt = bm[1]; + runlen = cnt; + bm += 2; + if (y < height) { + if (x >= width) { + x += runlen; + goto next_run; + } + if (x + runlen > width) + cnt = width - x; + + draw_bitmap (&fbp, bm, p, cnt, 0); + x += runlen; + } +next_run: + bm += runlen; + if (runlen & 1) + bm++; /* 0 padding if length is odd */ + } + break; + default: + /* encoded run */ + if (y < height) { /* only draw into visible area */ + cnt = bm[0]; + runlen = cnt; + if (x >= width) { + x += runlen; + bm += 2; + continue; + } + if (x + runlen > width) + cnt = width - x; + + draw_bitmap (&fbp, bm, p, cnt, 1); + x += runlen; + } + bm += 2; + break; + } + } + return 0; +} +#endif + +/* + * Display the BMP file located at address bmp_image. + */ +int video_display_bitmap (ulong bmp_image, int x, int y) +{ + ushort xcount, ycount; + uchar *fb; + bmp_image_t *bmp = (bmp_image_t *) bmp_image; + uchar *bmap; + ushort padded_line; + unsigned long width, height, bpp; + unsigned colors; + unsigned long compression; + bmp_color_table_entry_t cte; +#ifdef CONFIG_VIDEO_BMP_GZIP + unsigned char *dst = NULL; + ulong len; +#endif + + WATCHDOG_RESET (); + + if (!((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M'))) { + +#ifdef CONFIG_VIDEO_BMP_GZIP + /* + * Could be a gzipped bmp image, try to decrompress... + */ + len = CONFIG_SYS_VIDEO_LOGO_MAX_SIZE; + dst = malloc(CONFIG_SYS_VIDEO_LOGO_MAX_SIZE); + if (dst == NULL) { + printf("Error: malloc in gunzip failed!\n"); + return(1); + } + if (gunzip(dst, CONFIG_SYS_VIDEO_LOGO_MAX_SIZE, (uchar *)bmp_image, &len) != 0) { + printf ("Error: no valid bmp or bmp.gz image at %lx\n", bmp_image); + free(dst); + return 1; + } + if (len == CONFIG_SYS_VIDEO_LOGO_MAX_SIZE) { + printf("Image could be truncated (increase CONFIG_SYS_VIDEO_LOGO_MAX_SIZE)!\n"); + } + + /* + * Set addr to decompressed image + */ + bmp = (bmp_image_t *)dst; + + if (!((bmp->header.signature[0] == 'B') && + (bmp->header.signature[1] == 'M'))) { + printf ("Error: no valid bmp.gz image at %lx\n", bmp_image); + free(dst); + return 1; + } +#else + printf ("Error: no valid bmp image at %lx\n", bmp_image); + return 1; +#endif /* CONFIG_VIDEO_BMP_GZIP */ + } + + width = le32_to_cpu (bmp->header.width); + height = le32_to_cpu (bmp->header.height); + bpp = le16_to_cpu (bmp->header.bit_count); + colors = le32_to_cpu (bmp->header.colors_used); + compression = le32_to_cpu (bmp->header.compression); + + debug ("Display-bmp: %d x %d with %d colors\n", + width, height, colors); + + if (compression != BMP_BI_RGB +#ifdef CONFIG_VIDEO_BMP_RLE8 + && compression != BMP_BI_RLE8 +#endif + ) { + printf ("Error: compression type %ld not supported\n", + compression); +#ifdef CONFIG_VIDEO_BMP_GZIP + if (dst) + free(dst); +#endif + return 1; + } + + padded_line = (((width * bpp + 7) / 8) + 3) & ~0x3; + +#ifdef CONFIG_SPLASH_SCREEN_ALIGN + if (x == BMP_ALIGN_CENTER) + x = max(0, (VIDEO_VISIBLE_COLS - width) / 2); + else if (x < 0) + x = max(0, VIDEO_VISIBLE_COLS - width + x + 1); + + if (y == BMP_ALIGN_CENTER) + y = max(0, (VIDEO_VISIBLE_ROWS - height) / 2); + else if (y < 0) + y = max(0, VIDEO_VISIBLE_ROWS - height + y + 1); +#endif /* CONFIG_SPLASH_SCREEN_ALIGN */ + + if ((x + width) > VIDEO_VISIBLE_COLS) + width = VIDEO_VISIBLE_COLS - x; + if ((y + height) > VIDEO_VISIBLE_ROWS) + height = VIDEO_VISIBLE_ROWS - y; + + bmap = (uchar *) bmp + le32_to_cpu (bmp->header.data_offset); + fb = (uchar *) (video_fb_address + + ((y + height - 1) * VIDEO_COLS * VIDEO_PIXEL_SIZE) + + x * VIDEO_PIXEL_SIZE); + +#ifdef CONFIG_VIDEO_BMP_RLE8 + if (compression == BMP_BI_RLE8) { + return display_rle8_bitmap(bmp, + x, y, width, height); + } +#endif + + /* We handle only 4, 8, or 24 bpp bitmaps */ + switch (le16_to_cpu (bmp->header.bit_count)) { + case 4: + padded_line -= width / 2; + ycount = height; + + switch (VIDEO_DATA_FORMAT) { + case GDF_32BIT_X888RGB: + while (ycount--) { + WATCHDOG_RESET (); + /* + * Don't assume that 'width' is an + * even number + */ + for (xcount = 0; xcount < width; xcount++) { + uchar idx; + + if (xcount & 1) { + idx = *bmap & 0xF; + bmap++; + } else + idx = *bmap >> 4; + cte = bmp->color_table[idx]; + FILL_32BIT_X888RGB(cte.red, cte.green, + cte.blue); + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * + VIDEO_PIXEL_SIZE; + } + break; + default: + puts("4bpp bitmap unsupported with current " + "video mode\n"); + break; + } + break; + + case 8: + padded_line -= width; + if (VIDEO_DATA_FORMAT == GDF__8BIT_INDEX) { + /* Copy colormap */ + for (xcount = 0; xcount < colors; ++xcount) { + cte = bmp->color_table[xcount]; + video_set_lut (xcount, cte.red, cte.green, cte.blue); + } + } + ycount = height; + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + while (ycount--) { + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { + *fb++ = *bmap++; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + case GDF__8BIT_332RGB: + while (ycount--) { + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { + cte = bmp->color_table[*bmap++]; + FILL_8BIT_332RGB (cte.red, cte.green, cte.blue); + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + case GDF_15BIT_555RGB: + while (ycount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + int xpos = x; +#endif + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { + cte = bmp->color_table[*bmap++]; +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + fill_555rgb_pswap (fb, xpos++, cte.red, + cte.green, cte.blue); + fb += 2; +#else + FILL_15BIT_555RGB (cte.red, cte.green, cte.blue); +#endif + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + case GDF_16BIT_565RGB: + while (ycount--) { + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { + cte = bmp->color_table[*bmap++]; + FILL_16BIT_565RGB (cte.red, cte.green, cte.blue); + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + case GDF_32BIT_X888RGB: + while (ycount--) { + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { + cte = bmp->color_table[*bmap++]; + FILL_32BIT_X888RGB (cte.red, cte.green, cte.blue); + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + case GDF_24BIT_888RGB: + while (ycount--) { + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { + cte = bmp->color_table[*bmap++]; + FILL_24BIT_888RGB (cte.red, cte.green, cte.blue); + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + } + break; + case 24: + padded_line -= 3 * width; + ycount = height; + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_332RGB: + while (ycount--) { + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { + FILL_8BIT_332RGB (bmap[2], bmap[1], bmap[0]); + bmap += 3; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + case GDF_15BIT_555RGB: + while (ycount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + int xpos = x; +#endif + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + fill_555rgb_pswap (fb, xpos++, bmap[2], + bmap[1], bmap[0]); + fb += 2; +#else + FILL_15BIT_555RGB (bmap[2], bmap[1], bmap[0]); +#endif + bmap += 3; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + case GDF_16BIT_565RGB: + while (ycount--) { + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { + FILL_16BIT_565RGB (bmap[2], bmap[1], bmap[0]); + bmap += 3; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + case GDF_32BIT_X888RGB: + while (ycount--) { + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { + FILL_32BIT_X888RGB (bmap[2], bmap[1], bmap[0]); + bmap += 3; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + case GDF_24BIT_888RGB: + while (ycount--) { + WATCHDOG_RESET (); + xcount = width; + while (xcount--) { + FILL_24BIT_888RGB (bmap[2], bmap[1], bmap[0]); + bmap += 3; + } + bmap += padded_line; + fb -= (VIDEO_VISIBLE_COLS + width) * VIDEO_PIXEL_SIZE; + } + break; + default: + printf ("Error: 24 bits/pixel bitmap incompatible with current video mode\n"); + break; + } + break; + default: + printf ("Error: %d bit/pixel bitmaps not supported by U-Boot\n", + le16_to_cpu (bmp->header.bit_count)); + break; + } + +#ifdef CONFIG_VIDEO_BMP_GZIP + if (dst) { + free(dst); + } +#endif + + return (0); +} +#endif + +/*****************************************************************************/ + +#ifdef CONFIG_VIDEO_LOGO +void logo_plot (void *screen, int width, int x, int y) +{ + + int xcount, i; + int skip = (width - VIDEO_LOGO_WIDTH) * VIDEO_PIXEL_SIZE; + int ycount = video_logo_height; + unsigned char r, g, b, *logo_red, *logo_blue, *logo_green; + unsigned char *source; + unsigned char *dest = (unsigned char *)screen + + ((y * width * VIDEO_PIXEL_SIZE) + + x * VIDEO_PIXEL_SIZE); + +#ifdef CONFIG_VIDEO_BMP_LOGO + source = bmp_logo_bitmap; + + /* Allocate temporary space for computing colormap */ + logo_red = malloc (BMP_LOGO_COLORS); + logo_green = malloc (BMP_LOGO_COLORS); + logo_blue = malloc (BMP_LOGO_COLORS); + /* Compute color map */ + for (i = 0; i < VIDEO_LOGO_COLORS; i++) { + logo_red[i] = (bmp_logo_palette[i] & 0x0f00) >> 4; + logo_green[i] = (bmp_logo_palette[i] & 0x00f0); + logo_blue[i] = (bmp_logo_palette[i] & 0x000f) << 4; + } +#else + source = linux_logo; + logo_red = linux_logo_red; + logo_green = linux_logo_green; + logo_blue = linux_logo_blue; +#endif + + if (VIDEO_DATA_FORMAT == GDF__8BIT_INDEX) { + for (i = 0; i < VIDEO_LOGO_COLORS; i++) { + video_set_lut (i + VIDEO_LOGO_LUT_OFFSET, + logo_red[i], logo_green[i], logo_blue[i]); + } + } + + while (ycount--) { +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + int xpos = x; +#endif + xcount = VIDEO_LOGO_WIDTH; + while (xcount--) { + r = logo_red[*source - VIDEO_LOGO_LUT_OFFSET]; + g = logo_green[*source - VIDEO_LOGO_LUT_OFFSET]; + b = logo_blue[*source - VIDEO_LOGO_LUT_OFFSET]; + + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + *dest = *source; + break; + case GDF__8BIT_332RGB: + *dest = ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6); + break; + case GDF_15BIT_555RGB: +#if defined(VIDEO_FB_16BPP_PIXEL_SWAP) + fill_555rgb_pswap (dest, xpos++, r, g, b); +#else + *(unsigned short *) dest = + SWAP16 ((unsigned short) (((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3))); +#endif + break; + case GDF_16BIT_565RGB: + *(unsigned short *) dest = + SWAP16 ((unsigned short) (((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3))); + break; + case GDF_32BIT_X888RGB: + *(unsigned long *) dest = + SWAP32 ((unsigned long) ((r << 16) | (g << 8) | b)); + break; + case GDF_24BIT_888RGB: +#ifdef VIDEO_FB_LITTLE_ENDIAN + dest[0] = b; + dest[1] = g; + dest[2] = r; +#else + dest[0] = r; + dest[1] = g; + dest[2] = b; +#endif + break; + } + source++; + dest += VIDEO_PIXEL_SIZE; + } + dest += skip; + } +#ifdef CONFIG_VIDEO_BMP_LOGO + free (logo_red); + free (logo_green); + free (logo_blue); +#endif +} + +/*****************************************************************************/ + +static void *video_logo (void) +{ + char info[128]; + extern char version_string; + int space, len, y_off = 0; + +#ifdef CONFIG_SPLASH_SCREEN + char *s; + ulong addr; + + if ((s = getenv ("splashimage")) != NULL) { + int x = 0, y = 0; + + addr = simple_strtoul (s, NULL, 16); +#ifdef CONFIG_SPLASH_SCREEN_ALIGN + if ((s = getenv ("splashpos")) != NULL) { + if (s[0] == 'm') + x = BMP_ALIGN_CENTER; + else + x = simple_strtol (s, NULL, 0); + + if ((s = strchr (s + 1, ',')) != NULL) { + if (s[1] == 'm') + y = BMP_ALIGN_CENTER; + else + y = simple_strtol (s + 1, NULL, 0); + } + } +#endif /* CONFIG_SPLASH_SCREEN_ALIGN */ + + if (video_display_bitmap (addr, x, y) == 0) { + video_logo_height = 0; + return ((void *) (video_fb_address)); + } + } +#endif /* CONFIG_SPLASH_SCREEN */ + + logo_plot (video_fb_address, VIDEO_COLS, 0, 0); + + sprintf (info, " %s", &version_string); + + space = (VIDEO_LINE_LEN / 2 - VIDEO_INFO_X) / VIDEO_FONT_WIDTH; + len = strlen(info); + + if (len > space) { + video_drawchars (VIDEO_INFO_X, VIDEO_INFO_Y, + (uchar *)info, space); + video_drawchars (VIDEO_INFO_X + VIDEO_FONT_WIDTH, + VIDEO_INFO_Y + VIDEO_FONT_HEIGHT, + (uchar *)info + space, len - space); + y_off = 1; + } else + video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y, (uchar *)info); + +#ifdef CONFIG_CONSOLE_EXTRA_INFO + { + int i, n = ((video_logo_height - VIDEO_FONT_HEIGHT) / VIDEO_FONT_HEIGHT); + + for (i = 1; i < n; i++) { + video_get_info_str (i, info); + if (!*info) + continue; + + len = strlen(info); + if (len > space) { + video_drawchars (VIDEO_INFO_X, + VIDEO_INFO_Y + + (i + y_off) * VIDEO_FONT_HEIGHT, + (uchar *)info, space); + y_off++; + video_drawchars (VIDEO_INFO_X + VIDEO_FONT_WIDTH, + VIDEO_INFO_Y + + (i + y_off) * VIDEO_FONT_HEIGHT, + (uchar *)info + space, + len - space); + } else { + video_drawstring (VIDEO_INFO_X, + VIDEO_INFO_Y + + (i + y_off) * VIDEO_FONT_HEIGHT, + (uchar *)info); + } + } + } +#endif + + return (video_fb_address + video_logo_height * VIDEO_LINE_LEN); +} +#endif + + +/*****************************************************************************/ + +static int video_init (void) +{ + unsigned char color8; + + if ((pGD = video_hw_init ()) == NULL) + return -1; + + video_fb_address = (void *) VIDEO_FB_ADRS; +#ifdef CONFIG_VIDEO_HW_CURSOR + video_init_hw_cursor (VIDEO_FONT_WIDTH, VIDEO_FONT_HEIGHT); +#endif + + /* Init drawing pats */ + switch (VIDEO_DATA_FORMAT) { + case GDF__8BIT_INDEX: + video_set_lut (0x01, CONSOLE_FG_COL, CONSOLE_FG_COL, CONSOLE_FG_COL); + video_set_lut (0x00, CONSOLE_BG_COL, CONSOLE_BG_COL, CONSOLE_BG_COL); + fgx = 0x01010101; + bgx = 0x00000000; + break; + case GDF__8BIT_332RGB: + color8 = ((CONSOLE_FG_COL & 0xe0) | + ((CONSOLE_FG_COL >> 3) & 0x1c) | CONSOLE_FG_COL >> 6); + fgx = (color8 << 24) | (color8 << 16) | (color8 << 8) | color8; + color8 = ((CONSOLE_BG_COL & 0xe0) | + ((CONSOLE_BG_COL >> 3) & 0x1c) | CONSOLE_BG_COL >> 6); + bgx = (color8 << 24) | (color8 << 16) | (color8 << 8) | color8; + break; + case GDF_15BIT_555RGB: + fgx = (((CONSOLE_FG_COL >> 3) << 26) | + ((CONSOLE_FG_COL >> 3) << 21) | ((CONSOLE_FG_COL >> 3) << 16) | + ((CONSOLE_FG_COL >> 3) << 10) | ((CONSOLE_FG_COL >> 3) << 5) | + (CONSOLE_FG_COL >> 3)); + bgx = (((CONSOLE_BG_COL >> 3) << 26) | + ((CONSOLE_BG_COL >> 3) << 21) | ((CONSOLE_BG_COL >> 3) << 16) | + ((CONSOLE_BG_COL >> 3) << 10) | ((CONSOLE_BG_COL >> 3) << 5) | + (CONSOLE_BG_COL >> 3)); + break; + case GDF_16BIT_565RGB: + fgx = (((CONSOLE_FG_COL >> 3) << 27) | + ((CONSOLE_FG_COL >> 2) << 21) | ((CONSOLE_FG_COL >> 3) << 16) | + ((CONSOLE_FG_COL >> 3) << 11) | ((CONSOLE_FG_COL >> 2) << 5) | + (CONSOLE_FG_COL >> 3)); + bgx = (((CONSOLE_BG_COL >> 3) << 27) | + ((CONSOLE_BG_COL >> 2) << 21) | ((CONSOLE_BG_COL >> 3) << 16) | + ((CONSOLE_BG_COL >> 3) << 11) | ((CONSOLE_BG_COL >> 2) << 5) | + (CONSOLE_BG_COL >> 3)); + break; + case GDF_32BIT_X888RGB: + fgx = (CONSOLE_FG_COL << 16) | (CONSOLE_FG_COL << 8) | CONSOLE_FG_COL; + bgx = (CONSOLE_BG_COL << 16) | (CONSOLE_BG_COL << 8) | CONSOLE_BG_COL; + break; + case GDF_24BIT_888RGB: + fgx = (CONSOLE_FG_COL << 24) | (CONSOLE_FG_COL << 16) | + (CONSOLE_FG_COL << 8) | CONSOLE_FG_COL; + bgx = (CONSOLE_BG_COL << 24) | (CONSOLE_BG_COL << 16) | + (CONSOLE_BG_COL << 8) | CONSOLE_BG_COL; + break; + } + eorx = fgx ^ bgx; + +#ifdef CONFIG_VIDEO_LOGO + /* Plot the logo and get start point of console */ + PRINTD ("Video: Drawing the logo ...\n"); + video_console_address = video_logo (); +#else + video_console_address = video_fb_address; +#endif + + /* Initialize the console */ + console_col = 0; + console_row = 0; + + return 0; +} + + +/*****************************************************************************/ + +/* + * Implement a weak default function for boards that optionally + * need to skip the video initialization. + */ +int __board_video_skip(void) +{ + /* As default, don't skip test */ + return 0; +} +int board_video_skip(void) __attribute__((weak, alias("__board_video_skip"))); + +int drv_video_init (void) +{ + int skip_dev_init; + struct stdio_dev console_dev; + + /* Check if video initialization should be skipped */ + if (board_video_skip()) + return 0; + + /* Init video chip - returns with framebuffer cleared */ + skip_dev_init = (video_init () == -1); + +#if !defined(CONFIG_VGA_AS_SINGLE_DEVICE) + PRINTD ("KBD: Keyboard init ...\n"); + skip_dev_init |= (VIDEO_KBD_INIT_FCT == -1); +#endif + + if (skip_dev_init) + return 0; + + /* Init vga device */ + memset (&console_dev, 0, sizeof (console_dev)); + strcpy (console_dev.name, "vga"); + console_dev.ext = DEV_EXT_VIDEO; /* Video extensions */ + console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_SYSTEM; + console_dev.putc = video_putc; /* 'putc' function */ + console_dev.puts = video_puts; /* 'puts' function */ + console_dev.tstc = NULL; /* 'tstc' function */ + console_dev.getc = NULL; /* 'getc' function */ + +#if !defined(CONFIG_VGA_AS_SINGLE_DEVICE) + /* Also init console device */ + console_dev.flags |= DEV_FLAGS_INPUT; + console_dev.tstc = VIDEO_TSTC_FCT; /* 'tstc' function */ + console_dev.getc = VIDEO_GETC_FCT; /* 'getc' function */ +#endif /* CONFIG_VGA_AS_SINGLE_DEVICE */ + + if (stdio_register (&console_dev) != 0) + return 0; + + /* Return success */ + return 1; +} diff --git a/u-boot/drivers/video/ct69000.c b/u-boot/drivers/video/ct69000.c new file mode 100644 index 0000000..ae219cc --- /dev/null +++ b/u-boot/drivers/video/ct69000.c @@ -0,0 +1,1281 @@ +/* ported from ctfb.c (linux kernel): + * Created in Jan - July 2000 by Thomas Höhenleitner <th@visuelle-maschinen.de> + * + * Ported to U-Boot: + * (C) Copyright 2002 Denis Peter, MPL AG Switzerland + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#include <common.h> + +#ifdef CONFIG_VIDEO + +#include <pci.h> +#include <video_fb.h> +#include "videomodes.h" + +/* debug */ +#undef VGA_DEBUG +#undef VGA_DUMP_REG +#ifdef VGA_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + +/* Macros */ +#ifndef min +#define min( a, b ) ( ( a ) < ( b ) ) ? ( a ) : ( b ) +#endif +#ifndef max +#define max( a, b ) ( ( a ) > ( b ) ) ? ( a ) : ( b ) +#endif +#ifdef minmax +#error "term minmax already used." +#endif +#define minmax( a, x, b ) max( ( a ), min( ( x ), ( b ) ) ) +#define N_ELTS( x ) ( sizeof( x ) / sizeof( x[ 0 ] ) ) + +/* CT Register Offsets */ +#define CT_AR_O 0x3c0 /* Index and Data write port of the attribute Registers */ +#define CT_GR_O 0x3ce /* Index port of the Graphic Controller Registers */ +#define CT_SR_O 0x3c4 /* Index port of the Sequencer Controller */ +#define CT_CR_O 0x3d4 /* Index port of the CRT Controller */ +#define CT_XR_O 0x3d6 /* Extended Register index */ +#define CT_MSR_W_O 0x3c2 /* Misc. Output Register (write only) */ +#define CT_LUT_MASK_O 0x3c6 /* Color Palette Mask */ +#define CT_LUT_START_O 0x3c8 /* Color Palette Write Mode Index */ +#define CT_LUT_RGB_O 0x3c9 /* Color Palette Data Port */ +#define CT_STATUS_REG0_O 0x3c2 /* Status Register 0 (read only) */ +#define CT_STATUS_REG1_O 0x3da /* Input Status Register 1 (read only) */ + +#define CT_FP_O 0x3d0 /* Index port of the Flat panel Registers */ +#define CT_MR_O 0x3d2 /* Index Port of the Multimedia Extension */ + +/* defines for the memory mapped registers */ +#define BR00_o 0x400000 /* Source and Destination Span Register */ +#define BR01_o 0x400004 /* Pattern/Source Expansion Background Color & Transparency Key Register */ +#define BR02_o 0x400008 /* Pattern/Source Expansion Foreground Color Register */ +#define BR03_o 0x40000C /* Monochrome Source Control Register */ +#define BR04_o 0x400010 /* BitBLT Control Register */ +#define BR05_o 0x400014 /* Pattern Address Registe */ +#define BR06_o 0x400018 /* Source Address Register */ +#define BR07_o 0x40001C /* Destination Address Register */ +#define BR08_o 0x400020 /* Destination Width & Height Register */ +#define BR09_o 0x400024 /* Source Expansion Background Color & Transparency Key Register */ +#define BR0A_o 0x400028 /* Source Expansion Foreground Color Register */ + +#define CURSOR_SIZE 0x1000 /* in KByte for HW Cursor */ +#define PATTERN_ADR (pGD->dprBase + CURSOR_SIZE) /* pattern Memory after Cursor Memory */ +#define PATTERN_SIZE 8*8*4 /* 4 Bytes per Pixel 8 x 8 Pixel */ +#define ACCELMEMORY (CURSOR_SIZE + PATTERN_SIZE) /* reserved Memory for BITBlt and hw cursor */ + +/* Some Mode definitions */ +#define FB_SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */ +#define FB_SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */ +#define FB_SYNC_EXT 4 /* external sync */ +#define FB_SYNC_COMP_HIGH_ACT 8 /* composite sync high active */ +#define FB_SYNC_BROADCAST 16 /* broadcast video timings */ + /* vtotal = 144d/288n/576i => PAL */ + /* vtotal = 121d/242n/484i => NTSC */ +#define FB_SYNC_ON_GREEN 32 /* sync on green */ + +#define FB_VMODE_NONINTERLACED 0 /* non interlaced */ +#define FB_VMODE_INTERLACED 1 /* interlaced */ +#define FB_VMODE_DOUBLE 2 /* double scan */ +#define FB_VMODE_MASK 255 + +#define FB_VMODE_YWRAP 256 /* ywrap instead of panning */ +#define FB_VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */ +#define FB_VMODE_CONUPDATE 512 /* don't update x/yoffset */ + +#define text 0 +#define fntwidth 8 + +/* table for VGA Initialization */ +typedef struct { + const unsigned char reg; + const unsigned char val; +} CT_CFG_TABLE; + +/* this table provides some basic initialisations such as Memory Clock etc */ +static CT_CFG_TABLE xreg[] = { + {0x09, 0x01}, /* CRT Controller Extensions Enable */ + {0x0A, 0x02}, /* Frame Buffer Mapping */ + {0x0B, 0x01}, /* PCI Write Burst support */ + {0x20, 0x00}, /* BitBLT Configuration */ + {0x40, 0x03}, /* Memory Access Control */ + {0x60, 0x00}, /* Video Pin Control */ + {0x61, 0x00}, /* DPMS Synch control */ + {0x62, 0x00}, /* GPIO Pin Control */ + {0x63, 0xBD}, /* GPIO Pin Data */ + {0x67, 0x00}, /* Pin Tri-State */ + {0x80, 0x80}, /* Pixel Pipeline Config 0 register */ + {0xA0, 0x00}, /* Cursor 1 Control Reg */ + {0xA1, 0x00}, /* Cursor 1 Vertical Extension Reg */ + {0xA2, 0x00}, /* Cursor 1 Base Address Low */ + {0xA3, 0x00}, /* Cursor 1 Base Address High */ + {0xA4, 0x00}, /* Cursor 1 X-Position Low */ + {0xA5, 0x00}, /* Cursor 1 X-Position High */ + {0xA6, 0x00}, /* Cursor 1 Y-Position Low */ + {0xA7, 0x00}, /* Cursor 1 Y-Position High */ + {0xA8, 0x00}, /* Cursor 2 Control Reg */ + {0xA9, 0x00}, /* Cursor 2 Vertical Extension Reg */ + {0xAA, 0x00}, /* Cursor 2 Base Address Low */ + {0xAB, 0x00}, /* Cursor 2 Base Address High */ + {0xAC, 0x00}, /* Cursor 2 X-Position Low */ + {0xAD, 0x00}, /* Cursor 2 X-Position High */ + {0xAE, 0x00}, /* Cursor 2 Y-Position Low */ + {0xAF, 0x00}, /* Cursor 2 Y-Position High */ + {0xC0, 0x7D}, /* Dot Clock 0 VCO M-Divisor */ + {0xC1, 0x07}, /* Dot Clock 0 VCO N-Divisor */ + {0xC3, 0x34}, /* Dot Clock 0 Divisor select */ + {0xC4, 0x55}, /* Dot Clock 1 VCO M-Divisor */ + {0xC5, 0x09}, /* Dot Clock 1 VCO N-Divisor */ + {0xC7, 0x24}, /* Dot Clock 1 Divisor select */ + {0xC8, 0x7D}, /* Dot Clock 2 VCO M-Divisor */ + {0xC9, 0x07}, /* Dot Clock 2 VCO N-Divisor */ + {0xCB, 0x34}, /* Dot Clock 2 Divisor select */ + {0xCC, 0x38}, /* Memory Clock 0 VCO M-Divisor */ + {0xCD, 0x03}, /* Memory Clock 0 VCO N-Divisor */ + {0xCE, 0x90}, /* Memory Clock 0 Divisor select */ + {0xCF, 0x06}, /* Clock Config */ + {0xD0, 0x0F}, /* Power Down */ + {0xD1, 0x01}, /* Power Down BitBLT */ + {0xFF, 0xFF} /* end of table */ +}; +/* Clock Config: + * ============= + * + * PD Registers: + * ------------- + * Bit2 and Bit4..6 are used for the Loop Divisor and Post Divisor. + * They are encoded as follows: + * + * +---+--------------+ + * | 2 | Loop Divisor | + * +---+--------------+ + * | 1 | 1 | + * +---+--------------+ + * | 0 | 4 | + * +---+--------------+ + * Note: The Memory Clock does not have a Loop Divisor. + * +---+---+---+--------------+ + * | 6 | 5 | 4 | Post Divisor | + * +---+---+---+--------------+ + * | 0 | 0 | 0 | 1 | + * +---+---+---+--------------+ + * | 0 | 0 | 1 | 2 | + * +---+---+---+--------------+ + * | 0 | 1 | 0 | 4 | + * +---+---+---+--------------+ + * | 0 | 1 | 1 | 8 | + * +---+---+---+--------------+ + * | 1 | 0 | 0 | 16 | + * +---+---+---+--------------+ + * | 1 | 0 | 1 | 32 | + * +---+---+---+--------------+ + * | 1 | 1 | X | reserved | + * +---+---+---+--------------+ + * + * All other bits are reserved in these registers. + * + * Clock VCO M Registers: + * ---------------------- + * These Registers contain the M Value -2. + * + * Clock VCO N Registers: + * ---------------------- + * These Registers contain the N Value -2. + * + * Formulas: + * --------- + * Fvco = (Fref * Loop Divisor * M/N), whereas 100MHz < Fvco < 220MHz + * Fout = Fvco / Post Divisor + * + * Dot Clk0 (default 25MHz): + * ------------------------- + * Fvco = 14.318 * 127 / 9 = 202.045MHz + * Fout = 202.045MHz / 8 = 25.25MHz + * Post Divisor = 8 + * Loop Divisor = 1 + * XRC0 = (M - 2) = 125 = 0x7D + * XRC1 = (N - 2) = 7 = 0x07 + * XRC3 = 0x34 + * + * Dot Clk1 (default 28MHz): + * ------------------------- + * Fvco = 14.318 * 87 / 11 = 113.24MHz + * Fout = 113.24MHz / 4 = 28.31MHz + * Post Divisor = 4 + * Loop Divisor = 1 + * XRC4 = (M - 2) = 85 = 0x55 + * XRC5 = (N - 2) = 9 = 0x09 + * XRC7 = 0x24 + * + * Dot Clk2 (variable for extended modes set to 25MHz): + * ---------------------------------------------------- + * Fvco = 14.318 * 127 / 9 = 202.045MHz + * Fout = 202.045MHz / 8 = 25.25MHz + * Post Divisor = 8 + * Loop Divisor = 1 + * XRC8 = (M - 2) = 125 = 0x7D + * XRC9 = (N - 2) = 7 = 0x07 + * XRCB = 0x34 + * + * Memory Clk for most modes >50MHz: + * ---------------------------------- + * Fvco = 14.318 * 58 / 5 = 166MHz + * Fout = 166MHz / 2 = 83MHz + * Post Divisor = 2 + * XRCC = (M - 2) = 57 = 0x38 + * XRCD = (N - 2) = 3 = 0x03 + * XRCE = 0x90 + * + * Note Bit7 enables the clock source from the VCO + * + */ + +/******************************************************************* + * Chips struct + *******************************************************************/ +struct ctfb_chips_properties { + int device_id; /* PCI Device ID */ + unsigned long max_mem; /* memory for frame buffer */ + int vld_set; /* value of VLD if bit2 in clock control is set */ + int vld_not_set; /* value of VLD if bit2 in clock control is set */ + int mn_diff; /* difference between M/N Value + mn_diff = M/N Register */ + int mn_min; /* min value of M/N Value */ + int mn_max; /* max value of M/N Value */ + int vco_min; /* VCO Min in MHz */ + int vco_max; /* VCO Max in MHz */ +}; + +static const struct ctfb_chips_properties chips[] = { + {PCI_DEVICE_ID_CT_69000, 0x200000, 1, 4, -2, 3, 257, 100, 220}, +#ifdef CONFIG_USE_CPCIDVI + {PCI_DEVICE_ID_CT_69030, 0x400000, 1, 4, -2, 3, 257, 100, 220}, +#endif + {PCI_DEVICE_ID_CT_65555, 0x100000, 16, 4, 0, 1, 255, 48, 220}, /* NOT TESTED */ + {0, 0, 0, 0, 0, 0, 0, 0, 0} /* Terminator */ +}; + +/* + * The Graphic Device + */ +GraphicDevice ctfb; + +/******************************************************************************* +* +* Low Level Routines +*/ + +/******************************************************************************* +* +* Read CT ISA register +*/ +#ifdef VGA_DEBUG +static unsigned char +ctRead (unsigned short index) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + if (index == CT_AR_O) + /* synch the Flip Flop */ + in8 (pGD->isaBase + CT_STATUS_REG1_O); + + return (in8 (pGD->isaBase + index)); +} +#endif +/******************************************************************************* +* +* Write CT ISA register +*/ +static void +ctWrite (unsigned short index, unsigned char val) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + + out8 ((pGD->isaBase + index), val); +} + +/******************************************************************************* +* +* Read CT ISA register indexed +*/ +static unsigned char +ctRead_i (unsigned short index, char reg) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + if (index == CT_AR_O) + /* synch the Flip Flop */ + in8 (pGD->isaBase + CT_STATUS_REG1_O); + out8 ((pGD->isaBase + index), reg); + return (in8 (pGD->isaBase + index + 1)); +} + +/******************************************************************************* +* +* Write CT ISA register indexed +*/ +static void +ctWrite_i (unsigned short index, char reg, char val) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + if (index == CT_AR_O) { + /* synch the Flip Flop */ + in8 (pGD->isaBase + CT_STATUS_REG1_O); + out8 ((pGD->isaBase + index), reg); + out8 ((pGD->isaBase + index), val); + } else { + out8 ((pGD->isaBase + index), reg); + out8 ((pGD->isaBase + index + 1), val); + } +} + +/******************************************************************************* +* +* Write a table of CT ISA register +*/ +static void +ctLoadRegs (unsigned short index, CT_CFG_TABLE * regTab) +{ + while (regTab->reg != 0xFF) { + ctWrite_i (index, regTab->reg, regTab->val); + regTab++; + } +} + +/*****************************************************************************/ +static void +SetArRegs (void) +{ + int i, tmp; + + for (i = 0; i < 0x10; i++) + ctWrite_i (CT_AR_O, i, i); + if (text) + tmp = 0x04; + else + tmp = 0x41; + + ctWrite_i (CT_AR_O, 0x10, tmp); /* Mode Control Register */ + ctWrite_i (CT_AR_O, 0x11, 0x00); /* Overscan Color Register */ + ctWrite_i (CT_AR_O, 0x12, 0x0f); /* Memory Plane Enable Register */ + if (fntwidth == 9) + tmp = 0x08; + else + tmp = 0x00; + ctWrite_i (CT_AR_O, 0x13, tmp); /* Horizontal Pixel Panning */ + ctWrite_i (CT_AR_O, 0x14, 0x00); /* Color Select Register */ + ctWrite (CT_AR_O, 0x20); /* enable video */ +} + +/*****************************************************************************/ +static void +SetGrRegs (void) +{ /* Set Graphics Mode */ + int i; + + for (i = 0; i < 0x05; i++) + ctWrite_i (CT_GR_O, i, 0); + if (text) { + ctWrite_i (CT_GR_O, 0x05, 0x10); + ctWrite_i (CT_GR_O, 0x06, 0x02); + } else { + ctWrite_i (CT_GR_O, 0x05, 0x40); + ctWrite_i (CT_GR_O, 0x06, 0x05); + } + ctWrite_i (CT_GR_O, 0x07, 0x0f); + ctWrite_i (CT_GR_O, 0x08, 0xff); +} + +/*****************************************************************************/ +static void +SetSrRegs (void) +{ + int tmp = 0; + + ctWrite_i (CT_SR_O, 0x00, 0x00); /* reset */ + /*rr( sr, 0x01, tmp ); + if( fntwidth == 8 ) tmp |= 0x01; else tmp &= ~0x01; + wr( sr, 0x01, tmp ); */ + if (fntwidth == 8) + ctWrite_i (CT_SR_O, 0x01, 0x01); /* Clocking Mode Register */ + else + ctWrite_i (CT_SR_O, 0x01, 0x00); /* Clocking Mode Register */ + ctWrite_i (CT_SR_O, 0x02, 0x0f); /* Enable CPU wr access to given memory plane */ + ctWrite_i (CT_SR_O, 0x03, 0x00); /* Character Map Select Register */ + if (text) + tmp = 0x02; + else + tmp = 0x0e; + ctWrite_i (CT_SR_O, 0x04, tmp); /* Enable CPU accesses to the rest of the 256KB + total VGA memory beyond the first 64KB and set + fb mapping mode. */ + ctWrite_i (CT_SR_O, 0x00, 0x03); /* enable */ +} + +/*****************************************************************************/ +static void +SetBitsPerPixelIntoXrRegs (int bpp) +{ + unsigned int n = (bpp >> 3), tmp; /* only for 15, 8, 16, 24 bpp */ + static char md[4] = { 0x04, 0x02, 0x05, 0x06 }; /* DisplayColorMode */ + static char off[4] = { ~0x20, ~0x30, ~0x20, ~0x10 }; /* mask */ + static char on[4] = { 0x10, 0x00, 0x10, 0x20 }; /* mask */ + if (bpp == 15) + n = 0; + tmp = ctRead_i (CT_XR_O, 0x20); + tmp &= off[n]; + tmp |= on[n]; + ctWrite_i (CT_XR_O, 0x20, tmp); /* BitBLT Configuration */ + ctWrite_i (CT_XR_O, 0x81, md[n]); +} + +/*****************************************************************************/ +static void +SetCrRegs (struct ctfb_res_modes *var, int bits_per_pixel) +{ /* he -le- ht|0 hd -ri- hs -h- he */ + unsigned char cr[0x7a]; + int i, tmp; + unsigned int hd, hs, he, ht, hbe; /* Horizontal. */ + unsigned int vd, vs, ve, vt; /* vertical */ + unsigned int bpp, wd, dblscan, interlaced, bcast, CrtHalfLine; + unsigned int CompSyncCharClkDelay, CompSyncPixelClkDelay; + unsigned int NTSC_PAL_HorizontalPulseWidth, BlDelayCtrl; + unsigned int HorizontalEqualizationPulses; + unsigned int HorizontalSerration1Start, HorizontalSerration2Start; + + const int LineCompare = 0x3ff; + unsigned int TextScanLines = 1; /* this is in fact a vertical zoom factor */ + unsigned int RAMDAC_BlankPedestalEnable = 0; /* 1=en-, 0=disable, see XR82 */ + + hd = (var->xres) / 8; /* HDisp. */ + hs = (var->xres + var->right_margin) / 8; /* HsStrt */ + he = (var->xres + var->right_margin + var->hsync_len) / 8; /* HsEnd */ + ht = (var->left_margin + var->xres + var->right_margin + var->hsync_len) / 8; /* HTotal */ + hbe = ht - 1; /* HBlankEnable todo docu wants ht here, but it does not work */ + /* ve -up- vt|0 vd -lo- vs -v- ve */ + vd = var->yres; /* VDisplay */ + vs = var->yres + var->lower_margin; /* VSyncStart */ + ve = var->yres + var->lower_margin + var->vsync_len; /* VSyncEnd */ + vt = var->upper_margin + var->yres + var->lower_margin + var->vsync_len; /* VTotal */ + bpp = bits_per_pixel; + dblscan = (var->vmode & FB_VMODE_DOUBLE) ? 1 : 0; + interlaced = var->vmode & FB_VMODE_INTERLACED; + bcast = var->sync & FB_SYNC_BROADCAST; + CrtHalfLine = bcast ? (hd >> 1) : 0; + BlDelayCtrl = bcast ? 1 : 0; + CompSyncCharClkDelay = 0; /* 2 bit */ + CompSyncPixelClkDelay = 0; /* 3 bit */ + if (bcast) { + NTSC_PAL_HorizontalPulseWidth = 7; /*( var->hsync_len >> 1 ) + 1 */ + HorizontalEqualizationPulses = 0; /* inverse value */ + HorizontalSerration1Start = 31; /* ( ht >> 1 ) */ + HorizontalSerration2Start = 89; /* ( ht >> 1 ) */ + } else { + NTSC_PAL_HorizontalPulseWidth = 0; + /* 4 bit: hsync pulse width = ( ( CR74[4:0] - CR74[5] ) + * / 2 ) + 1 --> CR74[4:0] = 2*(hs-1) + CR74[5] */ + HorizontalEqualizationPulses = 1; /* inverse value */ + HorizontalSerration1Start = 0; /* ( ht >> 1 ) */ + HorizontalSerration2Start = 0; /* ( ht >> 1 ) */ + } + + if (bpp == 15) + bpp = 16; + wd = var->xres * bpp / 64; /* double words per line */ + if (interlaced) { /* we divide all vertical timings, exept vd */ + vs >>= 1; + ve >>= 1; + vt >>= 1; + } + memset (cr, 0, sizeof (cr)); + cr[0x00] = 0xff & (ht - 5); + cr[0x01] = hd - 1; /* soll:4f ist 59 */ + cr[0x02] = hd; + cr[0x03] = (hbe & 0x1F) | 0x80; /* hd + ht - hd */ + cr[0x04] = hs; + cr[0x05] = ((hbe & 0x20) << 2) | (he & 0x1f); + cr[0x06] = (vt - 2) & 0xFF; + cr[0x30] = (vt - 2) >> 8; + cr[0x07] = ((vt & 0x100) >> 8) + | ((vd & 0x100) >> 7) + | ((vs & 0x100) >> 6) + | ((vs & 0x100) >> 5) + | ((LineCompare & 0x100) >> 4) + | ((vt & 0x200) >> 4) + | ((vd & 0x200) >> 3) + | ((vs & 0x200) >> 2); + cr[0x08] = 0x00; + cr[0x09] = (dblscan << 7) + | ((LineCompare & 0x200) >> 3) + | ((vs & 0x200) >> 4) + | (TextScanLines - 1); + cr[0x10] = vs & 0xff; /* VSyncPulseStart */ + cr[0x32] = (vs & 0xf00) >> 8; /* VSyncPulseStart */ + cr[0x11] = (ve & 0x0f); /* | 0x20; */ + cr[0x12] = (vd - 1) & 0xff; /* LineCount */ + cr[0x31] = ((vd - 1) & 0xf00) >> 8; /* LineCount */ + cr[0x13] = wd & 0xff; + cr[0x41] = (wd & 0xf00) >> 8; + cr[0x15] = vs & 0xff; + cr[0x33] = (vs & 0xf00) >> 8; + cr[0x38] = (0x100 & (ht - 5)) >> 8; + cr[0x3C] = 0xc0 & hbe; + cr[0x16] = (vt - 1) & 0xff; /* vbe - docu wants vt here, */ + cr[0x17] = 0xe3; /* but it does not work */ + cr[0x18] = 0xff & LineCompare; + cr[0x22] = 0xff; /* todo? */ + cr[0x70] = interlaced ? (0x80 | CrtHalfLine) : 0x00; /* check:0xa6 */ + cr[0x71] = 0x80 | (RAMDAC_BlankPedestalEnable << 6) + | (BlDelayCtrl << 5) + | ((0x03 & CompSyncCharClkDelay) << 3) + | (0x07 & CompSyncPixelClkDelay); /* todo: see XR82 */ + cr[0x72] = HorizontalSerration1Start; + cr[0x73] = HorizontalSerration2Start; + cr[0x74] = (HorizontalEqualizationPulses << 5) + | NTSC_PAL_HorizontalPulseWidth; + /* todo: ct69000 has also 0x75-79 */ + /* now set the registers */ + for (i = 0; i <= 0x0d; i++) { /*CR00 .. CR0D */ + ctWrite_i (CT_CR_O, i, cr[i]); + } + for (i = 0x10; i <= 0x18; i++) { /*CR10 .. CR18 */ + ctWrite_i (CT_CR_O, i, cr[i]); + } + i = 0x22; /*CR22 */ + ctWrite_i (CT_CR_O, i, cr[i]); + for (i = 0x30; i <= 0x33; i++) { /*CR30 .. CR33 */ + ctWrite_i (CT_CR_O, i, cr[i]); + } + i = 0x38; /*CR38 */ + ctWrite_i (CT_CR_O, i, cr[i]); + i = 0x3C; /*CR3C */ + ctWrite_i (CT_CR_O, i, cr[i]); + for (i = 0x40; i <= 0x41; i++) { /*CR40 .. CR41 */ + ctWrite_i (CT_CR_O, i, cr[i]); + } + for (i = 0x70; i <= 0x74; i++) { /*CR70 .. CR74 */ + ctWrite_i (CT_CR_O, i, cr[i]); + } + tmp = ctRead_i (CT_CR_O, 0x40); + tmp &= 0x0f; + tmp |= 0x80; + ctWrite_i (CT_CR_O, 0x40, tmp); /* StartAddressEnable */ +} + +/* pixelclock control */ + +/***************************************************************************** + We have a rational number p/q and need an m/n which is very close to p/q + but has m and n within mnmin and mnmax. We have no floating point in the + kernel. We can use long long without divide. And we have time to compute... +******************************************************************************/ +static unsigned int +FindBestPQFittingMN (unsigned int p, unsigned int q, unsigned int mnmin, + unsigned int mnmax, unsigned int *pm, unsigned int *pn) +{ + /* this code is not for general purpose usable but good for our number ranges */ + unsigned int n = mnmin, m = 0; + long long int L = 0, P = p, Q = q, H = P >> 1; + long long int D = 0x7ffffffffffffffLL; + for (n = mnmin; n <= mnmax; n++) { + m = mnmin; /* p/q ~ m/n -> p*n ~ m*q -> p*n-x*q ~ 0 */ + L = P * n - m * Q; /* n * vco - m * fref should be near 0 */ + while (L > 0 && m < mnmax) { + L -= q; /* difference is greater as 0 subtract fref */ + m++; /* and increment m */ + } + /* difference is less or equal than 0 or m > maximum */ + if (m > mnmax) + break; /* no solution: if we increase n we get the same situation */ + /* L is <= 0 now */ + if (-L > H && m > mnmin) { /* if difference > the half fref */ + L += q; /* we take the situation before */ + m--; /* because its closer to 0 */ + } + L = (L < 0) ? -L : +L; /* absolute value */ + if (D < L) /* if last difference was better take next n */ + continue; + D = L; + *pm = m; + *pn = n; /* keep improved data */ + if (D == 0) + break; /* best result we can get */ + } + return (unsigned int) (0xffffffff & D); +} + +/* that is the hardware < 69000 we have to manage + +---------+ +-------------------+ +----------------------+ +--+ + | REFCLK |__|NTSC Divisor Select|__|FVCO Reference Divisor|__|÷N|__ + | 14.3MHz | |(NTSCDS) (÷1, ÷5) | |Select (RDS) (÷1, ÷4) | | | | + +---------+ +-------------------+ +----------------------+ +--+ | + ___________________________________________________________________| + | + | fvco fout + | +--------+ +------------+ +-----+ +-------------------+ +----+ + +-| Phase |__|Charge Pump |__| VCO |_____|Post Divisor (PD) |___|CLK |---> + +-| Detect | |& Filter VCO| | | | |÷1, 2, 4, 8, 16, 32| | | + | +--------+ +------------+ +-----+ | +-------------------+ +----+ + | | + | +--+ +---------------+ | + |____|÷M|___|VCO Loop Divide|__________| + | | |(VLD)(÷4, ÷16) | + +--+ +---------------+ +**************************************************************************** + that is the hardware >= 69000 we have to manage + +---------+ +--+ + | REFCLK |__|÷N|__ + | 14.3MHz | | | | + +---------+ +--+ | + __________________| + | + | fvco fout + | +--------+ +------------+ +-----+ +-------------------+ +----+ + +-| Phase |__|Charge Pump |__| VCO |_____|Post Divisor (PD) |___|CLK |---> + +-| Detect | |& Filter VCO| | | | |÷1, 2, 4, 8, 16, 32| | | + | +--------+ +------------+ +-----+ | +-------------------+ +----+ + | | + | +--+ +---------------+ | + |____|÷M|___|VCO Loop Divide|__________| + | | |(VLD)(÷1, ÷4) | + +--+ +---------------+ + + +*/ + +#define VIDEO_FREF 14318180; /* Hz */ +/*****************************************************************************/ +static int +ReadPixClckFromXrRegsBack (struct ctfb_chips_properties *param) +{ + unsigned int m, n, vld, pd, PD, fref, xr_cb, i, pixclock; + i = 0; + pixclock = -1; + fref = VIDEO_FREF; + m = ctRead_i (CT_XR_O, 0xc8); + n = ctRead_i (CT_XR_O, 0xc9); + m -= param->mn_diff; + n -= param->mn_diff; + xr_cb = ctRead_i (CT_XR_O, 0xcb); + PD = (0x70 & xr_cb) >> 4; + pd = 1; + for (i = 0; i < PD; i++) { + pd *= 2; + } + vld = (0x04 & xr_cb) ? param->vld_set : param->vld_not_set; + if (n * vld * m) { + unsigned long long p = 1000000000000LL * pd * n; + unsigned long long q = (long long) fref * vld * m; + while ((p > 0xffffffffLL) || (q > 0xffffffffLL)) { + p >>= 1; /* can't divide with long long so we scale down */ + q >>= 1; + } + pixclock = (unsigned) p / (unsigned) q; + } else + printf ("Invalid data in xr regs.\n"); + return pixclock; +} + +/*****************************************************************************/ +static void +FindAndSetPllParamIntoXrRegs (unsigned int pixelclock, + struct ctfb_chips_properties *param) +{ + unsigned int m, n, vld, pd, PD, fref, xr_cb; + unsigned int fvcomin, fvcomax, pclckmin, pclckmax, pclk; + unsigned int pfreq, fvco, new_pixclock; + unsigned int D,nback,mback; + + fref = VIDEO_FREF; + pd = 1; + PD = 0; + fvcomin = param->vco_min; + fvcomax = param->vco_max; /* MHz */ + pclckmin = 1000000 / fvcomax + 1; /* 4546 */ + pclckmax = 32000000 / fvcomin - 1; /* 666665 */ + pclk = minmax (pclckmin, pixelclock, pclckmax); /* ps pp */ + pfreq = 250 * (4000000000U / pclk); + fvco = pfreq; /* Hz */ + new_pixclock = 0; + while (fvco < fvcomin * 1000000) { + /* double VCO starting with the pixelclock frequency + * as long as it is lower than the minimal VCO frequency */ + fvco *= 2; + pd *= 2; + PD++; + } + /* fvco is exactly pd * pixelclock and higher than the ninmal VCO frequency */ + /* first try */ + vld = param->vld_set; + D=FindBestPQFittingMN (fvco / vld, fref, param->mn_min, param->mn_max, &m, &n); /* rds = 1 */ + mback=m; + nback=n; + /* second try */ + vld = param->vld_not_set; + if(D<FindBestPQFittingMN (fvco / vld, fref, param->mn_min, param->mn_max, &m, &n)) { /* rds = 1 */ + /* first try was better */ + m=mback; + n=nback; + vld = param->vld_set; + } + m += param->mn_diff; + n += param->mn_diff; + PRINTF ("VCO %d, pd %d, m %d n %d vld %d \n", fvco, pd, m, n, vld); + xr_cb = ((0x7 & PD) << 4) | (vld == param->vld_set ? 0x04 : 0); + /* All four of the registers used for dot clock 2 (XRC8 - XRCB) must be + * written, and in order from XRC8 to XRCB, before the hardware will + * update the synthesizer s settings. + */ + ctWrite_i (CT_XR_O, 0xc8, m); + ctWrite_i (CT_XR_O, 0xc9, n); /* xrca does not exist in CT69000 and CT69030 */ + ctWrite_i (CT_XR_O, 0xca, 0); /* because of a hw bug I guess, but we write */ + ctWrite_i (CT_XR_O, 0xcb, xr_cb); /* 0 to it for savety */ + new_pixclock = ReadPixClckFromXrRegsBack (param); + PRINTF ("pixelclock.set = %d, pixelclock.real = %d \n", + pixelclock, new_pixclock); +} + +/*****************************************************************************/ +static void +SetMsrRegs (struct ctfb_res_modes *mode) +{ + unsigned char h_synch_high, v_synch_high; + + h_synch_high = (mode->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 0x40; /* horizontal Synch High active */ + v_synch_high = (mode->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 0x80; /* vertical Synch High active */ + ctWrite (CT_MSR_W_O, (h_synch_high | v_synch_high | 0x29)); + /* upper64K==0x20, CLC2select==0x08, RAMenable==0x02!(todo), CGA==0x01 + * Selects the upper 64KB page.Bit5=1 + * CLK2 (left reserved in standard VGA) Bit3|2=1|0 + * Disables CPU access to frame buffer. Bit1=0 + * Sets the I/O address decode for ST01, FCR, and all CR registers + * to the 3Dx I/O address range (CGA emulation). Bit0=1 + */ +} + +/************************************************************************************/ +#ifdef VGA_DUMP_REG + +static void +ctDispRegs (unsigned short index, int from, int to) +{ + unsigned char status; + int i; + + for (i = from; i < to; i++) { + status = ctRead_i (index, i); + printf ("%02X: is %02X\n", i, status); + } +} + +void +video_dump_reg (void) +{ + int i; + + printf ("Extended Regs:\n"); + ctDispRegs (CT_XR_O, 0, 0xC); + ctDispRegs (CT_XR_O, 0xe, 0xf); + ctDispRegs (CT_XR_O, 0x20, 0x21); + ctDispRegs (CT_XR_O, 0x40, 0x50); + ctDispRegs (CT_XR_O, 0x60, 0x64); + ctDispRegs (CT_XR_O, 0x67, 0x68); + ctDispRegs (CT_XR_O, 0x70, 0x72); + ctDispRegs (CT_XR_O, 0x80, 0x83); + ctDispRegs (CT_XR_O, 0xA0, 0xB0); + ctDispRegs (CT_XR_O, 0xC0, 0xD3); + printf ("Sequencer Regs:\n"); + ctDispRegs (CT_SR_O, 0, 0x8); + printf ("Graphic Regs:\n"); + ctDispRegs (CT_GR_O, 0, 0x9); + printf ("CRT Regs:\n"); + ctDispRegs (CT_CR_O, 0, 0x19); + ctDispRegs (CT_CR_O, 0x22, 0x23); + ctDispRegs (CT_CR_O, 0x30, 0x34); + ctDispRegs (CT_CR_O, 0x38, 0x39); + ctDispRegs (CT_CR_O, 0x3C, 0x3D); + ctDispRegs (CT_CR_O, 0x40, 0x42); + ctDispRegs (CT_CR_O, 0x70, 0x80); + /* don't display the attributes */ +} + +#endif + +#ifdef CONFIG_VIDEO_HW_CURSOR +/*************************************************************** + * Set Hardware Cursor in Pixel + */ +void +video_set_hw_cursor (int x, int y) +{ + int sig_x = 0, sig_y = 0; + if (x < 0) { + x *= -1; + sig_x = 1; + } + if (y < 0) { + y *= -1; + sig_y = 1; + } + ctWrite_i (CT_XR_O, 0xa4, x & 0xff); + ctWrite_i (CT_XR_O, 0xa5, (x >> 8) & 0x7); + ctWrite_i (CT_XR_O, 0xa6, y & 0xff); + ctWrite_i (CT_XR_O, 0xa7, (y >> 8) & 0x7); +} + +/*************************************************************** + * Init Hardware Cursor. To know the size of the Cursor, + * we have to know the Font size. + */ +void +video_init_hw_cursor (int font_width, int font_height) +{ + unsigned char xr_80; + unsigned long *curs, pattern; + int i; + int cursor_start; + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + + cursor_start = pGD->dprBase; + xr_80 = ctRead_i (CT_XR_O, 0x80); + /* set start address */ + ctWrite_i (CT_XR_O, 0xa2, (cursor_start >> 8) & 0xf0); + ctWrite_i (CT_XR_O, 0xa3, (cursor_start >> 16) & 0x3f); + /* set cursor shape */ + curs = (unsigned long *) cursor_start; + i = 0; + while (i < 0x400) { + curs[i++] = 0xffffffff; /* AND mask */ + curs[i++] = 0xffffffff; /* AND mask */ + curs[i++] = 0; /* XOR mask */ + curs[i++] = 0; /* XOR mask */ + /* Transparent */ + } + pattern = 0xffffffff >> font_width; + i = 0; + while (i < (font_height * 2)) { + curs[i++] = pattern; /* AND mask */ + curs[i++] = pattern; /* AND mask */ + curs[i++] = 0; /* XOR mask */ + curs[i++] = 0; /* XOR mask */ + /* Cursor Color 0 */ + } + /* set blink rate */ + ctWrite_i (CT_FP_O, 0x19, 0xf); + + /* set cursors colors */ + xr_80 = ctRead_i (CT_XR_O, 0x80); + xr_80 |= 0x1; /* alternate palette select */ + ctWrite_i (CT_XR_O, 0x80, xr_80); + video_set_lut (4, CONSOLE_FG_COL, CONSOLE_FG_COL, CONSOLE_FG_COL); + /* position 4 is color 0 cursor 0 */ + xr_80 &= 0xfe; /* normal palette select */ + ctWrite_i (CT_XR_O, 0x80, xr_80); + /* cursor enable */ + ctWrite_i (CT_XR_O, 0xa0, 0x91); + xr_80 |= 0x10; /* enable hwcursor */ + ctWrite_i (CT_XR_O, 0x80, xr_80); + video_set_hw_cursor (0, 0); +} +#endif /* CONFIG_VIDEO_HW_CURSOR */ + +/*************************************************************** + * Wait for BitBlt ready + */ +static int +video_wait_bitblt (unsigned long addr) +{ + unsigned long br04; + int i = 0; + br04 = in32r (addr); + while (br04 & 0x80000000) { + udelay (1); + br04 = in32r (addr); + if (i++ > 1000000) { + printf ("ERROR Timeout %lx\n", br04); + return 1; + } + } + return 0; +} + +/*************************************************************** + * Set up BitBlt Registrs + */ +static void +SetDrawingEngine (int bits_per_pixel) +{ + unsigned long br04, br00; + unsigned char tmp; + + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + + tmp = ctRead_i (CT_XR_O, 0x20); /* BitBLT Configuration */ + tmp |= 0x02; /* reset BitBLT */ + ctWrite_i (CT_XR_O, 0x20, tmp); /* BitBLT Configuration */ + udelay (10); + tmp &= 0xfd; /* release reset BitBLT */ + ctWrite_i (CT_XR_O, 0x20, tmp); /* BitBLT Configuration */ + video_wait_bitblt (pGD->pciBase + BR04_o); + + /* set pattern Address */ + out32r (pGD->pciBase + BR05_o, PATTERN_ADR & 0x003ffff8); + br04 = 0; + if (bits_per_pixel == 1) { + br04 |= 0x00040000; /* monochome Pattern */ + br04 |= 0x00001000; /* monochome source */ + } + br00 = ((pGD->winSizeX * pGD->gdfBytesPP) << 16) + (pGD->winSizeX * pGD->gdfBytesPP); /* bytes per scanline */ + out32r (pGD->pciBase + BR00_o, br00); /* */ + out32r (pGD->pciBase + BR08_o, (10 << 16) + 10); /* dummy */ + out32r (pGD->pciBase + BR04_o, br04); /* write all 0 */ + out32r (pGD->pciBase + BR07_o, 0); /* destination */ + video_wait_bitblt (pGD->pciBase + BR04_o); +} + +/**************************************************************************** +* supported Video Chips +*/ +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_69000}, +#ifdef CONFIG_USE_CPCIDVI + {PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_69030}, +#endif + {} +}; + +/******************************************************************************* +* +* Init video chip +*/ +void * +video_hw_init (void) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + unsigned short device_id; + pci_dev_t devbusfn; + int videomode; + unsigned long t1, hsynch, vsynch; + unsigned int pci_mem_base, *vm; + int tmp, i, bits_per_pixel; + char *penv; + struct ctfb_res_modes *res_mode; + struct ctfb_res_modes var_mode; + struct ctfb_chips_properties *chips_param; + /* Search for video chip */ + + if ((devbusfn = pci_find_devices (supported, 0)) < 0) { +#ifdef CONFIG_VIDEO_ONBOARD + printf ("Video: Controller not found !\n"); +#endif + return (NULL); + } + + /* PCI setup */ + pci_write_config_dword (devbusfn, PCI_COMMAND, + (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)); + pci_read_config_word (devbusfn, PCI_DEVICE_ID, &device_id); + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base); + pci_mem_base = pci_mem_to_phys (devbusfn, pci_mem_base); + + /* get chips params */ + for (chips_param = (struct ctfb_chips_properties *) &chips[0]; + chips_param->device_id != 0; chips_param++) { + if (chips_param->device_id == device_id) + break; + } + if (chips_param->device_id == 0) { +#ifdef CONFIG_VIDEO_ONBOARD + printf ("Video: controller 0x%X not supported\n", device_id); +#endif + return NULL; + } + /* supported Video controller found */ + printf ("Video: "); + + tmp = 0; + videomode = 0x301; + /* get video mode via environment */ + if ((penv = getenv ("videomode")) != NULL) { + /* deceide if it is a string */ + if (penv[0] <= '9') { + videomode = (int) simple_strtoul (penv, NULL, 16); + tmp = 1; + } + } else { + tmp = 1; + } + if (tmp) { + /* parameter are vesa modes */ + /* search params */ + for (i = 0; i < VESA_MODES_COUNT; i++) { + if (vesa_modes[i].vesanr == videomode) + break; + } + if (i == VESA_MODES_COUNT) { + printf ("no VESA Mode found, switching to mode 0x301 "); + i = 0; + } + res_mode = + (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i]. + resindex]; + bits_per_pixel = vesa_modes[i].bits_per_pixel; + } else { + + res_mode = (struct ctfb_res_modes *) &var_mode; + bits_per_pixel = video_get_params (res_mode, penv); + } + + /* calculate available color depth for controller memory */ + if (bits_per_pixel == 15) + tmp = 2; + else + tmp = bits_per_pixel >> 3; /* /8 */ + if (((chips_param->max_mem - + ACCELMEMORY) / (res_mode->xres * res_mode->yres)) < tmp) { + tmp = + ((chips_param->max_mem - + ACCELMEMORY) / (res_mode->xres * res_mode->yres)); + if (tmp == 0) { + printf + ("No matching videomode found .-> reduce resolution\n"); + return NULL; + } else { + printf ("Switching back to %d Bits per Pixel ", + tmp << 3); + bits_per_pixel = tmp << 3; + } + } + + /* calculate hsynch and vsynch freq (info only) */ + t1 = (res_mode->left_margin + res_mode->xres + + res_mode->right_margin + res_mode->hsync_len) / 8; + t1 *= 8; + t1 *= res_mode->pixclock; + t1 /= 1000; + hsynch = 1000000000L / t1; + t1 *= + (res_mode->upper_margin + res_mode->yres + + res_mode->lower_margin + res_mode->vsync_len); + t1 /= 1000; + vsynch = 1000000000L / t1; + + /* fill in Graphic device struct */ + sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres, + res_mode->yres, bits_per_pixel, (hsynch / 1000), + (vsynch / 1000)); + printf ("%s\n", pGD->modeIdent); + pGD->winSizeX = res_mode->xres; + pGD->winSizeY = res_mode->yres; + pGD->plnSizeX = res_mode->xres; + pGD->plnSizeY = res_mode->yres; + switch (bits_per_pixel) { + case 8: + pGD->gdfBytesPP = 1; + pGD->gdfIndex = GDF__8BIT_INDEX; + break; + case 15: + pGD->gdfBytesPP = 2; + pGD->gdfIndex = GDF_15BIT_555RGB; + break; + case 16: + pGD->gdfBytesPP = 2; + pGD->gdfIndex = GDF_16BIT_565RGB; + break; + case 24: + pGD->gdfBytesPP = 3; + pGD->gdfIndex = GDF_24BIT_888RGB; + break; + } + pGD->isaBase = CONFIG_SYS_ISA_IO_BASE_ADDRESS; + pGD->pciBase = pci_mem_base; + pGD->frameAdrs = pci_mem_base; + pGD->memSize = chips_param->max_mem; + /* Cursor Start Address */ + pGD->dprBase = + (pGD->winSizeX * pGD->winSizeY * pGD->gdfBytesPP) + pci_mem_base; + if ((pGD->dprBase & 0x0fff) != 0) { + /* allign it */ + pGD->dprBase &= 0xfffff000; + pGD->dprBase += 0x00001000; + } + PRINTF ("Cursor Start %x Pattern Start %x\n", pGD->dprBase, + PATTERN_ADR); + pGD->vprBase = pci_mem_base; /* Dummy */ + pGD->cprBase = pci_mem_base; /* Dummy */ + /* set up Hardware */ + +#ifdef CONFIG_USE_CPCIDVI + if (device_id == PCI_DEVICE_ID_CT_69030) { + ctWrite (CT_MSR_W_O, 0x0b); + ctWrite (0x3cd, 0x13); + ctWrite_i (CT_FP_O, 0x02, 0x00); + ctWrite_i (CT_FP_O, 0x05, 0x00); + ctWrite_i (CT_FP_O, 0x06, 0x00); + ctWrite (0x3c2, 0x0b); + ctWrite_i (CT_FP_O, 0x02, 0x10); + ctWrite_i (CT_FP_O, 0x01, 0x09); + } else { + ctWrite (CT_MSR_W_O, 0x01); + } +#else + ctWrite (CT_MSR_W_O, 0x01); +#endif + + /* set the extended Registers */ + ctLoadRegs (CT_XR_O, xreg); + /* set atribute registers */ + SetArRegs (); + /* set Graphics register */ + SetGrRegs (); + /* set sequencer */ + SetSrRegs (); + + /* set msr */ + SetMsrRegs (res_mode); + + /* set CRT Registers */ + SetCrRegs (res_mode, bits_per_pixel); + /* set color mode */ + SetBitsPerPixelIntoXrRegs (bits_per_pixel); + + /* set PLL */ + FindAndSetPllParamIntoXrRegs (res_mode->pixclock, chips_param); + + ctWrite_i (CT_SR_O, 0, 0x03); /* clear synchronous reset */ + /* Clear video memory */ + i = pGD->memSize / 4; + vm = (unsigned int *) pGD->pciBase; + while (i--) + *vm++ = 0; + SetDrawingEngine (bits_per_pixel); +#ifdef VGA_DUMP_REG + video_dump_reg (); +#endif + + return ((void *) &ctfb); +} + + /******************************************************************************* +* +* Set a RGB color in the LUT (8 bit index) +*/ +void +video_set_lut (unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + + ctWrite (CT_LUT_MASK_O, 0xff); + + ctWrite (CT_LUT_START_O, (char) index); + + ctWrite (CT_LUT_RGB_O, r); /* red */ + ctWrite (CT_LUT_RGB_O, g); /* green */ + ctWrite (CT_LUT_RGB_O, b); /* blue */ + udelay (1); + ctWrite (CT_LUT_MASK_O, 0xff); +} + +/******************************************************************************* +* +* Drawing engine fill on screen region +*/ +void +video_hw_rectfill (unsigned int bpp, /* bytes per pixel */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y, /* frame height */ + unsigned int color /* fill color */ + ) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + unsigned long *p, br04; + + video_wait_bitblt (pGD->pciBase + BR04_o); + + p = (unsigned long *) PATTERN_ADR; + dim_x *= bpp; + if (bpp == 3) + bpp++; /* 24Bit needs a 32bit pattern */ + memset (p, color, (bpp * sizeof (unsigned char) * 8 * 8)); /* 8 x 8 pattern data */ + out32r (pGD->pciBase + BR07_o, ((pGD->winSizeX * dst_y) + dst_x) * pGD->gdfBytesPP); /* destination */ + br04 = in32r (pGD->pciBase + BR04_o) & 0xffffff00; + br04 |= 0xF0; /* write Pattern P -> D */ + out32r (pGD->pciBase + BR04_o, br04); /* */ + out32r (pGD->pciBase + BR08_o, (dim_y << 16) + dim_x); /* starts the BITBlt */ + video_wait_bitblt (pGD->pciBase + BR04_o); +} + +/******************************************************************************* +* +* Drawing engine bitblt with screen region +*/ +void +video_hw_bitblt (unsigned int bpp, /* bytes per pixel */ + unsigned int src_x, /* source pos x */ + unsigned int src_y, /* source pos y */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y /* frame height */ + ) +{ + GraphicDevice *pGD = (GraphicDevice *) & ctfb; + unsigned long br04; + + br04 = in32r (pGD->pciBase + BR04_o); + + /* to prevent data corruption due to overlap, we have to + * find out if, and how the frames overlaps */ + if (src_x < dst_x) { + /* src is more left than dest + * the frame may overlap -> start from right to left */ + br04 |= 0x00000100; /* set bit 8 */ + src_x += dim_x; + dst_x += dim_x; + } else { + br04 &= 0xfffffeff; /* clear bit 8 left to right */ + } + if (src_y < dst_y) { + /* src is higher than dst + * the frame may overlap => start from bottom */ + br04 |= 0x00000200; /* set bit 9 */ + src_y += dim_y; + dst_y += dim_y; + } else { + br04 &= 0xfffffdff; /* clear bit 9 top to bottom */ + } + dim_x *= bpp; + out32r (pGD->pciBase + BR06_o, ((pGD->winSizeX * src_y) + src_x) * pGD->gdfBytesPP); /* source */ + out32r (pGD->pciBase + BR07_o, ((pGD->winSizeX * dst_y) + dst_x) * pGD->gdfBytesPP); /* destination */ + br04 &= 0xffffff00; + br04 |= 0x000000CC; /* S -> D */ + out32r (pGD->pciBase + BR04_o, br04); /* */ + out32r (pGD->pciBase + BR08_o, (dim_y << 16) + dim_x); /* start the BITBlt */ + video_wait_bitblt (pGD->pciBase + BR04_o); +} +#endif /* CONFIG_VIDEO */ diff --git a/u-boot/drivers/video/fsl_diu_fb.c b/u-boot/drivers/video/fsl_diu_fb.c new file mode 100644 index 0000000..35ed938 --- /dev/null +++ b/u-boot/drivers/video/fsl_diu_fb.c @@ -0,0 +1,513 @@ +/* + * Copyright 2007, 2010 Freescale Semiconductor, Inc. + * York Sun <yorksun@freescale.com> + * + * FSL DIU Framebuffer driver + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#include <common.h> +#include <i2c.h> +#include <malloc.h> +#include <asm/io.h> + +#include <fsl_diu_fb.h> + +struct fb_videomode { + const char *name; /* optional */ + unsigned int refresh; /* optional */ + unsigned int xres; + unsigned int yres; + unsigned int pixclock; + unsigned int left_margin; + unsigned int right_margin; + unsigned int upper_margin; + unsigned int lower_margin; + unsigned int hsync_len; + unsigned int vsync_len; + unsigned int sync; + unsigned int vmode; + unsigned int flag; +}; + +#define FB_SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */ +#define FB_SYNC_COMP_HIGH_ACT 8 /* composite sync high active */ +#define FB_VMODE_NONINTERLACED 0 /* non interlaced */ + +/* This setting is used for the ifm pdm360ng with PRIMEVIEW PM070WL3 */ +static struct fb_videomode fsl_diu_mode_800 = { + .refresh = 60, + .xres = 800, + .yres = 480, + .pixclock = 31250, + .left_margin = 86, + .right_margin = 42, + .upper_margin = 33, + .lower_margin = 10, + .hsync_len = 128, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED +}; + +/* + * These parameters give default parameters + * for video output 1024x768, + * FIXME - change timing to proper amounts + * hsync 31.5kHz, vsync 60Hz + */ +static struct fb_videomode fsl_diu_mode_1024 = { + .refresh = 60, + .xres = 1024, + .yres = 768, + .pixclock = 15385, + .left_margin = 160, + .right_margin = 24, + .upper_margin = 29, + .lower_margin = 3, + .hsync_len = 136, + .vsync_len = 6, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED +}; + +static struct fb_videomode fsl_diu_mode_1280 = { + .name = "1280x1024-60", + .refresh = 60, + .xres = 1280, + .yres = 1024, + .pixclock = 9375, + .left_margin = 38, + .right_margin = 128, + .upper_margin = 2, + .lower_margin = 7, + .hsync_len = 216, + .vsync_len = 37, + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED +}; + +/* + * These are the fields of area descriptor(in DDR memory) for every plane + */ +struct diu_ad { + /* Word 0(32-bit) in DDR memory */ + unsigned int pix_fmt; /* hard coding pixel format */ + /* Word 1(32-bit) in DDR memory */ + unsigned int addr; + /* Word 2(32-bit) in DDR memory */ + unsigned int src_size_g_alpha; + /* Word 3(32-bit) in DDR memory */ + unsigned int aoi_size; + /* Word 4(32-bit) in DDR memory */ + unsigned int offset_xyi; + /* Word 5(32-bit) in DDR memory */ + unsigned int offset_xyd; + /* Word 6(32-bit) in DDR memory */ + unsigned int ckmax_r:8; + unsigned int ckmax_g:8; + unsigned int ckmax_b:8; + unsigned int res9:8; + /* Word 7(32-bit) in DDR memory */ + unsigned int ckmin_r:8; + unsigned int ckmin_g:8; + unsigned int ckmin_b:8; + unsigned int res10:8; + /* Word 8(32-bit) in DDR memory */ + unsigned int next_ad; + /* Word 9(32-bit) in DDR memory, just for 64-bit aligned */ + unsigned int res1; + unsigned int res2; + unsigned int res3; +}__attribute__ ((packed)); + +/* + * DIU register map + */ +struct diu { + unsigned int desc[3]; + unsigned int gamma; + unsigned int pallete; + unsigned int cursor; + unsigned int curs_pos; + unsigned int diu_mode; + unsigned int bgnd; + unsigned int bgnd_wb; + unsigned int disp_size; + unsigned int wb_size; + unsigned int wb_mem_addr; + unsigned int hsyn_para; + unsigned int vsyn_para; + unsigned int syn_pol; + unsigned int thresholds; + unsigned int int_status; + unsigned int int_mask; + unsigned int colorbar[8]; + unsigned int filling; + unsigned int plut; +} __attribute__ ((packed)); + +struct diu_hw { + struct diu *diu_reg; + volatile unsigned int mode; /* DIU operation mode */ +}; + +struct diu_addr { + unsigned char * paddr; /* Virtual address */ + unsigned int offset; +}; + +/* + * Modes of operation of DIU + */ +#define MFB_MODE0 0 /* DIU off */ +#define MFB_MODE1 1 /* All three planes output to display */ +#define MFB_MODE2 2 /* Plane 1 to display, + * planes 2+3 written back to memory */ +#define MFB_MODE3 3 /* All three planes written back to memory */ +#define MFB_MODE4 4 /* Color bar generation */ + +#define MAX_CURS 32 + +static struct fb_info fsl_fb_info; +static struct diu_addr gamma, cursor; +static struct diu_ad fsl_diu_fb_ad __attribute__ ((aligned(32))); +static struct diu_ad dummy_ad __attribute__ ((aligned(32))); +static unsigned char *dummy_fb; +static struct diu_hw dr = { + .mode = MFB_MODE1, +}; + +int fb_enabled = 0; +int fb_initialized = 0; +const int default_xres = 1280; +const int default_pixel_format = 0x88882317; + +static int map_video_memory(struct fb_info *info, unsigned long bytes_align); +static void enable_lcdc(void); +static void disable_lcdc(void); +static int fsl_diu_enable_panel(struct fb_info *info); +static int fsl_diu_disable_panel(struct fb_info *info); +static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align); +void diu_set_pixel_clock(unsigned int pixclock); + +int fsl_diu_init(int xres, unsigned int pixel_format, int gamma_fix) +{ + struct fb_videomode *fsl_diu_mode_db; + struct diu_ad *ad = &fsl_diu_fb_ad; + struct diu *hw; + struct fb_info *info = &fsl_fb_info; + struct fb_var_screeninfo *var = &info->var; + unsigned char *gamma_table_base; + unsigned int i, j; + + debug("Enter fsl_diu_init\n"); + dr.diu_reg = (struct diu *) (CONFIG_SYS_DIU_ADDR); + hw = (struct diu *) dr.diu_reg; + + disable_lcdc(); + + switch (xres) { + case 800: + fsl_diu_mode_db = &fsl_diu_mode_800; + break; + case 1280: + fsl_diu_mode_db = &fsl_diu_mode_1280; + break; + default: + fsl_diu_mode_db = &fsl_diu_mode_1024; + } + + if (0 == fb_initialized) { + allocate_buf(&gamma, 768, 32); + debug("gamma is allocated @ 0x%x\n", + (unsigned int)gamma.paddr); + allocate_buf(&cursor, MAX_CURS * MAX_CURS * 2, 32); + debug("curosr is allocated @ 0x%x\n", + (unsigned int)cursor.paddr); + + /* create a dummy fb and dummy ad */ + dummy_fb = malloc(64); + if (NULL == dummy_fb) { + printf("Cannot allocate dummy fb\n"); + return -1; + } + dummy_ad.addr = cpu_to_le32((unsigned int)dummy_fb); + dummy_ad.pix_fmt = 0x88882317; + dummy_ad.src_size_g_alpha = 0x04400000; /* alpha = 0 */ + dummy_ad.aoi_size = 0x02000400; + dummy_ad.offset_xyi = 0; + dummy_ad.offset_xyd = 0; + dummy_ad.next_ad = 0; + /* Memory allocation for framebuffer */ + if (map_video_memory(info, 32)) { + printf("Unable to allocate fb memory 1\n"); + return -1; + } + } + + memset(info->screen_base, 0, info->smem_len); + + out_be32(&dr.diu_reg->desc[0], (int)&dummy_ad); + out_be32(&dr.diu_reg->desc[1], (int)&dummy_ad); + out_be32(&dr.diu_reg->desc[2], (int)&dummy_ad); + debug("dummy dr.diu_reg->desc[0] = 0x%x\n", dr.diu_reg->desc[0]); + debug("dummy desc[0] = 0x%x\n", hw->desc[0]); + + /* read mode info */ + var->xres = fsl_diu_mode_db->xres; + var->yres = fsl_diu_mode_db->yres; + var->bits_per_pixel = 32; + var->pixclock = fsl_diu_mode_db->pixclock; + var->left_margin = fsl_diu_mode_db->left_margin; + var->right_margin = fsl_diu_mode_db->right_margin; + var->upper_margin = fsl_diu_mode_db->upper_margin; + var->lower_margin = fsl_diu_mode_db->lower_margin; + var->hsync_len = fsl_diu_mode_db->hsync_len; + var->vsync_len = fsl_diu_mode_db->vsync_len; + var->sync = fsl_diu_mode_db->sync; + var->vmode = fsl_diu_mode_db->vmode; + info->line_length = var->xres * var->bits_per_pixel / 8; + + ad->pix_fmt = pixel_format; + ad->addr = cpu_to_le32((unsigned int)info->screen_base); + ad->src_size_g_alpha + = cpu_to_le32((var->yres << 12) | var->xres); + /* fix me. AOI should not be greater than display size */ + ad->aoi_size = cpu_to_le32(( var->yres << 16) | var->xres); + ad->offset_xyi = 0; + ad->offset_xyd = 0; + + /* Disable chroma keying function */ + ad->ckmax_r = 0; + ad->ckmax_g = 0; + ad->ckmax_b = 0; + + ad->ckmin_r = 255; + ad->ckmin_g = 255; + ad->ckmin_b = 255; + + gamma_table_base = gamma.paddr; + debug("gamma_table_base is allocated @ 0x%x\n", + (unsigned int)gamma_table_base); + + /* Prep for DIU init - gamma table */ + + for (i = 0; i <= 2; i++) + for (j = 0; j <= 255; j++) + *gamma_table_base++ = j; + + if (gamma_fix == 1) { /* fix the gamma */ + debug("Fix gamma table\n"); + gamma_table_base = gamma.paddr; + for (i = 0; i < 256*3; i++) { + gamma_table_base[i] = (gamma_table_base[i] << 2) + | ((gamma_table_base[i] >> 6) & 0x03); + } + } + + debug("update-lcdc: HW - %p\n Disabling DIU\n", hw); + + /* Program DIU registers */ + + out_be32(&hw->gamma, (int)gamma.paddr); + out_be32(&hw->cursor, (int)cursor.paddr); + out_be32(&hw->bgnd, 0x007F7F7F); + out_be32(&hw->bgnd_wb, 0); /* BGND_WB */ + out_be32(&hw->disp_size, var->yres << 16 | var->xres); /* DISP SIZE */ + out_be32(&hw->wb_size, 0); /* WB SIZE */ + out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */ + out_be32(&hw->hsyn_para, var->left_margin << 22 | /* BP_H */ + var->hsync_len << 11 | /* PW_H */ + var->right_margin); /* FP_H */ + + out_be32(&hw->vsyn_para, var->upper_margin << 22 | /* BP_V */ + var->vsync_len << 11 | /* PW_V */ + var->lower_margin); /* FP_V */ + + out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */ + out_be32(&hw->thresholds, 0x00037800); /* The Thresholds */ + out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */ + out_be32(&hw->int_mask, 0); /* INT MASK */ + out_be32(&hw->plut, 0x01F5F666); + /* Pixel Clock configuration */ + debug("DIU pixclock in ps - %d\n", var->pixclock); + diu_set_pixel_clock(var->pixclock); + + fb_initialized = 1; + + /* Enable the DIU */ + fsl_diu_enable_panel(info); + enable_lcdc(); + + return 0; +} + +char *fsl_fb_open(struct fb_info **info) +{ + *info = &fsl_fb_info; + return fsl_fb_info.screen_base; +} + +void fsl_diu_close(void) +{ + struct fb_info *info = &fsl_fb_info; + fsl_diu_disable_panel(info); +} + +static int fsl_diu_enable_panel(struct fb_info *info) +{ + struct diu *hw = dr.diu_reg; + struct diu_ad *ad = &fsl_diu_fb_ad; + + debug("Entered: enable_panel\n"); + if (in_be32(&hw->desc[0]) != (unsigned)ad) + out_be32(&hw->desc[0], (unsigned)ad); + debug("desc[0] = 0x%x\n", hw->desc[0]); + return 0; +} + +static int fsl_diu_disable_panel(struct fb_info *info) +{ + struct diu *hw = dr.diu_reg; + + debug("Entered: disable_panel\n"); + if (in_be32(&hw->desc[0]) != (unsigned)&dummy_ad) + out_be32(&hw->desc[0], (unsigned)&dummy_ad); + return 0; +} + +static int map_video_memory(struct fb_info *info, unsigned long bytes_align) +{ + unsigned long offset; + unsigned long mask; + + debug("Entered: map_video_memory\n"); + /* allocate maximum 1280*1024 with 32bpp */ + info->smem_len = 1280 * 4 *1024 + bytes_align; + debug("MAP_VIDEO_MEMORY: smem_len = %d\n", info->smem_len); + info->screen_base = malloc(info->smem_len); + if (info->screen_base == NULL) { + printf("Unable to allocate fb memory\n"); + return -1; + } + info->smem_start = (unsigned int) info->screen_base; + mask = bytes_align - 1; + offset = (unsigned long)info->screen_base & mask; + if (offset) { + info->screen_base += (bytes_align - offset); + info->smem_len = info->smem_len - (bytes_align - offset); + } else + info->smem_len = info->smem_len - bytes_align; + + info->screen_size = info->smem_len; + + debug("Allocated fb @ 0x%08lx, size=%d.\n", + info->smem_start, info->smem_len); + + return 0; +} + +static void enable_lcdc(void) +{ + struct diu *hw = dr.diu_reg; + + debug("Entered: enable_lcdc, fb_enabled = %d\n", fb_enabled); + if (!fb_enabled) { + out_be32(&hw->diu_mode, dr.mode); + fb_enabled++; + } + debug("diu_mode = %d\n", hw->diu_mode); +} + +static void disable_lcdc(void) +{ + struct diu *hw = dr.diu_reg; + + debug("Entered: disable_lcdc, fb_enabled = %d\n", fb_enabled); + if (fb_enabled) { + out_be32(&hw->diu_mode, 0); + fb_enabled = 0; + } +} + +/* + * Align to 64-bit(8-byte), 32-byte, etc. + */ +static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align) +{ + u32 offset, ssize; + u32 mask; + + debug("Entered: allocate_buf\n"); + ssize = size + bytes_align; + buf->paddr = malloc(ssize); + if (!buf->paddr) + return -1; + + memset(buf->paddr, 0, ssize); + mask = bytes_align - 1; + offset = (u32)buf->paddr & mask; + if (offset) { + buf->offset = bytes_align - offset; + buf->paddr = (unsigned char *) ((u32)buf->paddr + offset); + } else + buf->offset = 0; + return 0; +} + +#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) +#include <stdio_dev.h> +#include <video_fb.h> +/* + * The Graphic Device + */ +static GraphicDevice ctfb; + +void *video_hw_init(void) +{ + struct fb_info *info; + + if (platform_diu_init(&ctfb.winSizeX, &ctfb.winSizeY) < 0) + return NULL; + + /* fill in Graphic device struct */ + sprintf(ctfb.modeIdent, "%ix%ix%i %ikHz %iHz", + ctfb.winSizeX, ctfb.winSizeY, 32, 64, 60); + + ctfb.frameAdrs = (unsigned int)fsl_fb_open(&info); + ctfb.plnSizeX = ctfb.winSizeX; + ctfb.plnSizeY = ctfb.winSizeY; + + ctfb.gdfBytesPP = 4; + ctfb.gdfIndex = GDF_32BIT_X888RGB; + + ctfb.isaBase = 0; + ctfb.pciBase = 0; + ctfb.memSize = info->screen_size; + + /* Cursor Start Address */ + ctfb.dprBase = 0; + ctfb.vprBase = 0; + ctfb.cprBase = 0; + + return &ctfb; +} +#endif /* defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) */ diff --git a/u-boot/drivers/video/ipu.h b/u-boot/drivers/video/ipu.h new file mode 100644 index 0000000..d8bc287 --- /dev/null +++ b/u-boot/drivers/video/ipu.h @@ -0,0 +1,321 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 __ASM_ARCH_IPU_H__ +#define __ASM_ARCH_IPU_H__ + +#include <linux/types.h> + +#define IDMA_CHAN_INVALID 0xFF +#define HIGH_RESOLUTION_WIDTH 1024 + +struct clk { + const char *name; + int id; + /* Source clock this clk depends on */ + struct clk *parent; + /* Secondary clock to enable/disable with this clock */ + struct clk *secondary; + /* Current clock rate */ + unsigned long rate; + /* Reference count of clock enable/disable */ + __s8 usecount; + /* Register bit position for clock's enable/disable control. */ + u8 enable_shift; + /* Register address for clock's enable/disable control. */ + void *enable_reg; + u32 flags; + /* + * Function ptr to recalculate the clock's rate based on parent + * clock's rate + */ + void (*recalc) (struct clk *); + /* + * Function ptr to set the clock to a new rate. The rate must match a + * supported rate returned from round_rate. Leave blank if clock is not + * programmable + */ + int (*set_rate) (struct clk *, unsigned long); + /* + * Function ptr to round the requested clock rate to the nearest + * supported rate that is less than or equal to the requested rate. + */ + unsigned long (*round_rate) (struct clk *, unsigned long); + /* + * Function ptr to enable the clock. Leave blank if clock can not + * be gated. + */ + int (*enable) (struct clk *); + /* + * Function ptr to disable the clock. Leave blank if clock can not + * be gated. + */ + void (*disable) (struct clk *); + /* Function ptr to set the parent clock of the clock. */ + int (*set_parent) (struct clk *, struct clk *); +}; + +/* + * Enumeration of Synchronous (Memory-less) panel types + */ +typedef enum { + IPU_PANEL_SHARP_TFT, + IPU_PANEL_TFT, +} ipu_panel_t; + +/* IPU Pixel format definitions */ +#define fourcc(a, b, c, d)\ + (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24)) + +/* + * Pixel formats are defined with ASCII FOURCC code. The pixel format codes are + * the same used by V4L2 API. + */ + +#define IPU_PIX_FMT_GENERIC fourcc('I', 'P', 'U', '0') +#define IPU_PIX_FMT_GENERIC_32 fourcc('I', 'P', 'U', '1') +#define IPU_PIX_FMT_LVDS666 fourcc('L', 'V', 'D', '6') +#define IPU_PIX_FMT_LVDS888 fourcc('L', 'V', 'D', '8') + +#define IPU_PIX_FMT_RGB332 fourcc('R', 'G', 'B', '1') /*< 8 RGB-3-3-2 */ +#define IPU_PIX_FMT_RGB555 fourcc('R', 'G', 'B', 'O') /*< 16 RGB-5-5-5 */ +#define IPU_PIX_FMT_RGB565 fourcc('R', 'G', 'B', 'P') /*< 1 6 RGB-5-6-5 */ +#define IPU_PIX_FMT_RGB666 fourcc('R', 'G', 'B', '6') /*< 18 RGB-6-6-6 */ +#define IPU_PIX_FMT_BGR666 fourcc('B', 'G', 'R', '6') /*< 18 BGR-6-6-6 */ +#define IPU_PIX_FMT_BGR24 fourcc('B', 'G', 'R', '3') /*< 24 BGR-8-8-8 */ +#define IPU_PIX_FMT_RGB24 fourcc('R', 'G', 'B', '3') /*< 24 RGB-8-8-8 */ +#define IPU_PIX_FMT_BGR32 fourcc('B', 'G', 'R', '4') /*< 32 BGR-8-8-8-8 */ +#define IPU_PIX_FMT_BGRA32 fourcc('B', 'G', 'R', 'A') /*< 32 BGR-8-8-8-8 */ +#define IPU_PIX_FMT_RGB32 fourcc('R', 'G', 'B', '4') /*< 32 RGB-8-8-8-8 */ +#define IPU_PIX_FMT_RGBA32 fourcc('R', 'G', 'B', 'A') /*< 32 RGB-8-8-8-8 */ +#define IPU_PIX_FMT_ABGR32 fourcc('A', 'B', 'G', 'R') /*< 32 ABGR-8-8-8-8 */ + +/* YUV Interleaved Formats */ +#define IPU_PIX_FMT_YUYV fourcc('Y', 'U', 'Y', 'V') /*< 16 YUV 4:2:2 */ +#define IPU_PIX_FMT_UYVY fourcc('U', 'Y', 'V', 'Y') /*< 16 YUV 4:2:2 */ +#define IPU_PIX_FMT_Y41P fourcc('Y', '4', '1', 'P') /*< 12 YUV 4:1:1 */ +#define IPU_PIX_FMT_YUV444 fourcc('Y', '4', '4', '4') /*< 24 YUV 4:4:4 */ + +/* two planes -- one Y, one Cb + Cr interleaved */ +#define IPU_PIX_FMT_NV12 fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */ + +#define IPU_PIX_FMT_GREY fourcc('G', 'R', 'E', 'Y') /*< 8 Greyscale */ +#define IPU_PIX_FMT_YVU410P fourcc('Y', 'V', 'U', '9') /*< 9 YVU 4:1:0 */ +#define IPU_PIX_FMT_YUV410P fourcc('Y', 'U', 'V', '9') /*< 9 YUV 4:1:0 */ +#define IPU_PIX_FMT_YVU420P fourcc('Y', 'V', '1', '2') /*< 12 YVU 4:2:0 */ +#define IPU_PIX_FMT_YUV420P fourcc('I', '4', '2', '0') /*< 12 YUV 4:2:0 */ +#define IPU_PIX_FMT_YUV420P2 fourcc('Y', 'U', '1', '2') /*< 12 YUV 4:2:0 */ +#define IPU_PIX_FMT_YVU422P fourcc('Y', 'V', '1', '6') /*< 16 YVU 4:2:2 */ +#define IPU_PIX_FMT_YUV422P fourcc('4', '2', '2', 'P') /*< 16 YUV 4:2:2 */ + +/* + * IPU Driver channels definitions. + * Note these are different from IDMA channels + */ +#define IPU_MAX_CH 32 +#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \ + ((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out) +#define _MAKE_ALT_CHAN(ch) (ch | (IPU_MAX_CH << 24)) +#define IPU_CHAN_ID(ch) (ch >> 24) +#define IPU_CHAN_ALT(ch) (ch & 0x02000000) +#define IPU_CHAN_ALPHA_IN_DMA(ch) ((uint32_t) (ch >> 6) & 0x3F) +#define IPU_CHAN_GRAPH_IN_DMA(ch) ((uint32_t) (ch >> 12) & 0x3F) +#define IPU_CHAN_VIDEO_IN_DMA(ch) ((uint32_t) (ch >> 18) & 0x3F) +#define IPU_CHAN_OUT_DMA(ch) ((uint32_t) (ch & 0x3F)) +#define NO_DMA 0x3F +#define ALT 1 + +/* + * Enumeration of IPU logical channels. An IPU logical channel is defined as a + * combination of an input (memory to IPU), output (IPU to memory), and/or + * secondary input IDMA channels and in some cases an Image Converter task. + * Some channels consist of only an input or output. + */ +typedef enum { + CHAN_NONE = -1, + + MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA), + MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA), + MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA), + MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA), + + MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA), + MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA), + MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0), + MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0), + + DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA), + DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA), + +} ipu_channel_t; + +/* + * Enumeration of types of buffers for a logical channel. + */ +typedef enum { + IPU_OUTPUT_BUFFER = 0, /*< Buffer for output from IPU */ + IPU_ALPHA_IN_BUFFER = 1, /*< Buffer for input to IPU */ + IPU_GRAPH_IN_BUFFER = 2, /*< Buffer for input to IPU */ + IPU_VIDEO_IN_BUFFER = 3, /*< Buffer for input to IPU */ + IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER, + IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER, +} ipu_buffer_t; + +#define IPU_PANEL_SERIAL 1 +#define IPU_PANEL_PARALLEL 2 + +struct ipu_channel { + u8 video_in_dma; + u8 alpha_in_dma; + u8 graph_in_dma; + u8 out_dma; +}; + +enum ipu_dmfc_type { + DMFC_NORMAL = 0, + DMFC_HIGH_RESOLUTION_DC, + DMFC_HIGH_RESOLUTION_DP, + DMFC_HIGH_RESOLUTION_ONLY_DP, +}; + + +/* + * Union of initialization parameters for a logical channel. + */ +typedef union { + struct { + uint32_t di; + unsigned char interlaced; + } mem_dc_sync; + struct { + uint32_t temp; + } mem_sdc_fg; + struct { + uint32_t di; + unsigned char interlaced; + uint32_t in_pixel_fmt; + uint32_t out_pixel_fmt; + unsigned char alpha_chan_en; + } mem_dp_bg_sync; + struct { + uint32_t temp; + } mem_sdc_bg; + struct { + uint32_t di; + unsigned char interlaced; + uint32_t in_pixel_fmt; + uint32_t out_pixel_fmt; + unsigned char alpha_chan_en; + } mem_dp_fg_sync; +} ipu_channel_params_t; + +/* + * Bitfield of Display Interface signal polarities. + */ +typedef struct { + unsigned datamask_en:1; + unsigned ext_clk:1; + unsigned interlaced:1; + unsigned odd_field_first:1; + unsigned clksel_en:1; + unsigned clkidle_en:1; + unsigned data_pol:1; /* true = inverted */ + unsigned clk_pol:1; /* true = rising edge */ + unsigned enable_pol:1; + unsigned Hsync_pol:1; /* true = active high */ + unsigned Vsync_pol:1; +} ipu_di_signal_cfg_t; + +typedef enum { + RGB, + YCbCr, + YUV +} ipu_color_space_t; + +/* Common IPU API */ +int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params); +void ipu_uninit_channel(ipu_channel_t channel); + +int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, + uint32_t u_offset, uint32_t v_offset); + +int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum, dma_addr_t phyaddr); + +int32_t ipu_is_channel_busy(ipu_channel_t channel); +void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum); +int32_t ipu_enable_channel(ipu_channel_t channel); +int32_t ipu_disable_channel(ipu_channel_t channel); + +int32_t ipu_init_sync_panel(int disp, + uint32_t pixel_clk, + uint16_t width, uint16_t height, + uint32_t pixel_fmt, + uint16_t h_start_width, uint16_t h_sync_width, + uint16_t h_end_width, uint16_t v_start_width, + uint16_t v_sync_width, uint16_t v_end_width, + uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig); + +int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, unsigned char enable, + uint8_t alpha); +int32_t ipu_disp_set_color_key(ipu_channel_t channel, unsigned char enable, + uint32_t colorKey); + +uint32_t bytes_per_pixel(uint32_t fmt); + +void clk_enable(struct clk *clk); +void clk_disable(struct clk *clk); +u32 clk_get_rate(struct clk *clk); +int clk_set_rate(struct clk *clk, unsigned long rate); +long clk_round_rate(struct clk *clk, unsigned long rate); +int clk_set_parent(struct clk *clk, struct clk *parent); +int clk_get_usecount(struct clk *clk); +struct clk *clk_get_parent(struct clk *clk); + +void ipu_dump_registers(void); +int ipu_probe(void); + +void ipu_dmfc_init(int dmfc_type, int first); +void ipu_init_dc_mappings(void); +void ipu_dmfc_set_wait4eot(int dma_chan, int width); +void ipu_dc_init(int dc_chan, int di, unsigned char interlaced); +void ipu_dc_uninit(int dc_chan); +void ipu_dp_dc_enable(ipu_channel_t channel); +int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt, + uint32_t out_pixel_fmt); +void ipu_dp_uninit(ipu_channel_t channel); +void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap); +ipu_color_space_t format_to_colorspace(uint32_t fmt); + +#endif diff --git a/u-boot/drivers/video/ipu_common.c b/u-boot/drivers/video/ipu_common.c new file mode 100644 index 0000000..9d20c86 --- /dev/null +++ b/u-boot/drivers/video/ipu_common.c @@ -0,0 +1,1183 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* #define DEBUG */ +#include <common.h> +#include <linux/types.h> +#include <linux/err.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/crm_regs.h> +#include "ipu.h" +#include "ipu_regs.h" + +extern struct mxc_ccm_reg *mxc_ccm; +extern u32 *ipu_cpmem_base; + +struct ipu_ch_param_word { + uint32_t data[5]; + uint32_t res[3]; +}; + +struct ipu_ch_param { + struct ipu_ch_param_word word[2]; +}; + +#define ipu_ch_param_addr(ch) (((struct ipu_ch_param *)ipu_cpmem_base) + (ch)) + +#define _param_word(base, w) \ + (((struct ipu_ch_param *)(base))->word[(w)].data) + +#define ipu_ch_param_set_field(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + _param_word(base, w)[i] |= (v) << off; \ + if (((bit) + (size) - 1) / 32 > i) { \ + _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \ + } \ +} + +#define ipu_ch_param_mod_field(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << size) - 1; \ + u32 temp = _param_word(base, w)[i]; \ + temp &= ~(mask << off); \ + _param_word(base, w)[i] = temp | (v) << off; \ + if (((bit) + (size) - 1) / 32 > i) { \ + temp = _param_word(base, w)[i + 1]; \ + temp &= ~(mask >> (32 - off)); \ + _param_word(base, w)[i + 1] = \ + temp | ((v) >> (off ? (32 - off) : 0)); \ + } \ +} + +#define ipu_ch_param_read_field(base, w, bit, size) ({ \ + u32 temp2; \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << size) - 1; \ + u32 temp1 = _param_word(base, w)[i]; \ + temp1 = mask & (temp1 >> off); \ + if (((bit)+(size) - 1) / 32 > i) { \ + temp2 = _param_word(base, w)[i + 1]; \ + temp2 &= mask >> (off ? (32 - off) : 0); \ + temp1 |= temp2 << (off ? (32 - off) : 0); \ + } \ + temp1; \ +}) + + +void clk_enable(struct clk *clk) +{ + if (clk) { + if (clk->usecount++ == 0) { + clk->enable(clk); + } + } +} + +void clk_disable(struct clk *clk) +{ + if (clk) { + if (!(--clk->usecount)) { + if (clk->disable) + clk->disable(clk); + } + } +} + +int clk_get_usecount(struct clk *clk) +{ + if (clk == NULL) + return 0; + + return clk->usecount; +} + +u32 clk_get_rate(struct clk *clk) +{ + if (!clk) + return 0; + + return clk->rate; +} + +struct clk *clk_get_parent(struct clk *clk) +{ + if (!clk) + return 0; + + return clk->parent; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + if (clk && clk->set_rate) + clk->set_rate(clk, rate); + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (clk == NULL || !clk->round_rate) + return 0; + + return clk->round_rate(clk, rate); +} + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + clk->parent = parent; + if (clk->set_parent) + return clk->set_parent(clk, parent); + return 0; +} + +static int clk_ipu_enable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(clk->enable_reg); + reg |= MXC_CCM_CCGR_CG_MASK << clk->enable_shift; + __raw_writel(reg, clk->enable_reg); + + /* Handshake with IPU when certain clock rates are changed. */ + reg = __raw_readl(&mxc_ccm->ccdr); + reg &= ~MXC_CCM_CCDR_IPU_HS_MASK; + __raw_writel(reg, &mxc_ccm->ccdr); + + /* Handshake with IPU when LPM is entered as its enabled. */ + reg = __raw_readl(&mxc_ccm->clpcr); + reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; + __raw_writel(reg, &mxc_ccm->clpcr); + + return 0; +} + +static void clk_ipu_disable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(clk->enable_reg); + reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift); + __raw_writel(reg, clk->enable_reg); + + /* + * No handshake with IPU whe dividers are changed + * as its not enabled. + */ + reg = __raw_readl(&mxc_ccm->ccdr); + reg |= MXC_CCM_CCDR_IPU_HS_MASK; + __raw_writel(reg, &mxc_ccm->ccdr); + + /* No handshake with IPU when LPM is entered as its not enabled. */ + reg = __raw_readl(&mxc_ccm->clpcr); + reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; + __raw_writel(reg, &mxc_ccm->clpcr); +} + + +static struct clk ipu_clk = { + .name = "ipu_clk", + .rate = 133000000, + .enable_reg = (u32 *)(MXC_CCM_BASE + + offsetof(struct mxc_ccm_reg, CCGR5)), + .enable_shift = MXC_CCM_CCGR5_CG5_OFFSET, + .enable = clk_ipu_enable, + .disable = clk_ipu_disable, + .usecount = 0, +}; + +/* Globals */ +struct clk *g_ipu_clk; +unsigned char g_ipu_clk_enabled; +struct clk *g_di_clk[2]; +struct clk *g_pixel_clk[2]; +unsigned char g_dc_di_assignment[10]; +uint32_t g_channel_init_mask; +uint32_t g_channel_enable_mask; + +static int ipu_dc_use_count; +static int ipu_dp_use_count; +static int ipu_dmfc_use_count; +static int ipu_di_use_count[2]; + +u32 *ipu_cpmem_base; +u32 *ipu_dc_tmpl_reg; + +/* Static functions */ + +static inline void ipu_ch_param_set_high_priority(uint32_t ch) +{ + ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 93, 2, 1); +}; + +static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type) +{ + return ((uint32_t) ch >> (6 * type)) & 0x3F; +}; + +/* Either DP BG or DP FG can be graphic window */ +static inline int ipu_is_dp_graphic_chan(uint32_t dma_chan) +{ + return (dma_chan == 23 || dma_chan == 27); +} + +static inline int ipu_is_dmfc_chan(uint32_t dma_chan) +{ + return ((dma_chan >= 23) && (dma_chan <= 29)); +} + + +static inline void ipu_ch_param_set_buffer(uint32_t ch, int bufNum, + dma_addr_t phyaddr) +{ + ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 29 * bufNum, 29, + phyaddr / 8); +}; + +#define idma_is_valid(ch) (ch != NO_DMA) +#define idma_mask(ch) (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0) +#define idma_is_set(reg, dma) (__raw_readl(reg(dma)) & idma_mask(dma)) + +static void ipu_pixel_clk_recalc(struct clk *clk) +{ + u32 div = __raw_readl(DI_BS_CLKGEN0(clk->id)); + if (div == 0) + clk->rate = 0; + else + clk->rate = (clk->parent->rate * 16) / div; +} + +static unsigned long ipu_pixel_clk_round_rate(struct clk *clk, + unsigned long rate) +{ + u32 div, div1; + u32 tmp; + /* + * Calculate divider + * Fractional part is 4 bits, + * so simply multiply by 2^4 to get fractional part. + */ + tmp = (clk->parent->rate * 16); + div = tmp / rate; + + if (div < 0x10) /* Min DI disp clock divider is 1 */ + div = 0x10; + if (div & ~0xFEF) + div &= 0xFF8; + else { + div1 = div & 0xFE0; + if ((tmp/div1 - tmp/div) < rate / 4) + div = div1; + else + div &= 0xFF8; + } + return (clk->parent->rate * 16) / div; +} + +static int ipu_pixel_clk_set_rate(struct clk *clk, unsigned long rate) +{ + u32 div = (clk->parent->rate * 16) / rate; + + __raw_writel(div, DI_BS_CLKGEN0(clk->id)); + + /* Setup pixel clock timing */ + __raw_writel((div / 16) << 16, DI_BS_CLKGEN1(clk->id)); + + clk->rate = (clk->parent->rate * 16) / div; + return 0; +} + +static int ipu_pixel_clk_enable(struct clk *clk) +{ + u32 disp_gen = __raw_readl(IPU_DISP_GEN); + disp_gen |= clk->id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE; + __raw_writel(disp_gen, IPU_DISP_GEN); + + return 0; +} + +static void ipu_pixel_clk_disable(struct clk *clk) +{ + u32 disp_gen = __raw_readl(IPU_DISP_GEN); + disp_gen &= clk->id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE; + __raw_writel(disp_gen, IPU_DISP_GEN); + +} + +static int ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent) +{ + u32 di_gen = __raw_readl(DI_GENERAL(clk->id)); + + if (parent == g_ipu_clk) + di_gen &= ~DI_GEN_DI_CLK_EXT; + else if (!IS_ERR(g_di_clk[clk->id]) && parent == g_di_clk[clk->id]) + di_gen |= DI_GEN_DI_CLK_EXT; + else + return -EINVAL; + + __raw_writel(di_gen, DI_GENERAL(clk->id)); + ipu_pixel_clk_recalc(clk); + return 0; +} + +static struct clk pixel_clk[] = { + { + .name = "pixel_clk", + .id = 0, + .recalc = ipu_pixel_clk_recalc, + .set_rate = ipu_pixel_clk_set_rate, + .round_rate = ipu_pixel_clk_round_rate, + .set_parent = ipu_pixel_clk_set_parent, + .enable = ipu_pixel_clk_enable, + .disable = ipu_pixel_clk_disable, + .usecount = 0, + }, + { + .name = "pixel_clk", + .id = 1, + .recalc = ipu_pixel_clk_recalc, + .set_rate = ipu_pixel_clk_set_rate, + .round_rate = ipu_pixel_clk_round_rate, + .set_parent = ipu_pixel_clk_set_parent, + .enable = ipu_pixel_clk_enable, + .disable = ipu_pixel_clk_disable, + .usecount = 0, + }, +}; + +/* + * This function resets IPU + */ +void ipu_reset(void) +{ + u32 *reg; + u32 value; + + reg = (u32 *)SRC_BASE_ADDR; + value = __raw_readl(reg); + value = value | SW_IPU_RST; + __raw_writel(value, reg); +} + +/* + * This function is called by the driver framework to initialize the IPU + * hardware. + * + * @param dev The device structure for the IPU passed in by the + * driver framework. + * + * @return Returns 0 on success or negative error code on error + */ +int ipu_probe(void) +{ + unsigned long ipu_base; + u32 temp; + + u32 *reg_hsc_mcd = (u32 *)MIPI_HSC_BASE_ADDR; + u32 *reg_hsc_mxt_conf = (u32 *)(MIPI_HSC_BASE_ADDR + 0x800); + + __raw_writel(0xF00, reg_hsc_mcd); + + /* CSI mode reserved*/ + temp = __raw_readl(reg_hsc_mxt_conf); + __raw_writel(temp | 0x0FF, reg_hsc_mxt_conf); + + temp = __raw_readl(reg_hsc_mxt_conf); + __raw_writel(temp | 0x10000, reg_hsc_mxt_conf); + + ipu_base = IPU_CTRL_BASE_ADDR; + ipu_cpmem_base = (u32 *)(ipu_base + IPU_CPMEM_REG_BASE); + ipu_dc_tmpl_reg = (u32 *)(ipu_base + IPU_DC_TMPL_REG_BASE); + + g_pixel_clk[0] = &pixel_clk[0]; + g_pixel_clk[1] = &pixel_clk[1]; + + g_ipu_clk = &ipu_clk; + debug("ipu_clk = %u\n", clk_get_rate(g_ipu_clk)); + + ipu_reset(); + + clk_set_parent(g_pixel_clk[0], g_ipu_clk); + clk_set_parent(g_pixel_clk[1], g_ipu_clk); + clk_enable(g_ipu_clk); + + g_di_clk[0] = NULL; + g_di_clk[1] = NULL; + + __raw_writel(0x807FFFFF, IPU_MEM_RST); + while (__raw_readl(IPU_MEM_RST) & 0x80000000) + ; + + ipu_init_dc_mappings(); + + __raw_writel(0, IPU_INT_CTRL(5)); + __raw_writel(0, IPU_INT_CTRL(6)); + __raw_writel(0, IPU_INT_CTRL(9)); + __raw_writel(0, IPU_INT_CTRL(10)); + + /* DMFC Init */ + ipu_dmfc_init(DMFC_NORMAL, 1); + + /* Set sync refresh channels as high priority */ + __raw_writel(0x18800000L, IDMAC_CHA_PRI(0)); + + /* Set MCU_T to divide MCU access window into 2 */ + __raw_writel(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN); + + clk_disable(g_ipu_clk); + + return 0; +} + +void ipu_dump_registers(void) +{ + debug("IPU_CONF = \t0x%08X\n", __raw_readl(IPU_CONF)); + debug("IDMAC_CONF = \t0x%08X\n", __raw_readl(IDMAC_CONF)); + debug("IDMAC_CHA_EN1 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_EN(0))); + debug("IDMAC_CHA_EN2 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_EN(32))); + debug("IDMAC_CHA_PRI1 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_PRI(0))); + debug("IDMAC_CHA_PRI2 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_PRI(32))); + debug("IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", + __raw_readl(IPU_CHA_DB_MODE_SEL(0))); + debug("IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", + __raw_readl(IPU_CHA_DB_MODE_SEL(32))); + debug("DMFC_WR_CHAN = \t0x%08X\n", + __raw_readl(DMFC_WR_CHAN)); + debug("DMFC_WR_CHAN_DEF = \t0x%08X\n", + __raw_readl(DMFC_WR_CHAN_DEF)); + debug("DMFC_DP_CHAN = \t0x%08X\n", + __raw_readl(DMFC_DP_CHAN)); + debug("DMFC_DP_CHAN_DEF = \t0x%08X\n", + __raw_readl(DMFC_DP_CHAN_DEF)); + debug("DMFC_IC_CTRL = \t0x%08X\n", + __raw_readl(DMFC_IC_CTRL)); + debug("IPU_FS_PROC_FLOW1 = \t0x%08X\n", + __raw_readl(IPU_FS_PROC_FLOW1)); + debug("IPU_FS_PROC_FLOW2 = \t0x%08X\n", + __raw_readl(IPU_FS_PROC_FLOW2)); + debug("IPU_FS_PROC_FLOW3 = \t0x%08X\n", + __raw_readl(IPU_FS_PROC_FLOW3)); + debug("IPU_FS_DISP_FLOW1 = \t0x%08X\n", + __raw_readl(IPU_FS_DISP_FLOW1)); +} + +/* + * This function is called to initialize a logical IPU channel. + * + * @param channel Input parameter for the logical channel ID to init. + * + * @param params Input parameter containing union of channel + * initialization parameters. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params) +{ + int ret = 0; + uint32_t ipu_conf; + + debug("init channel = %d\n", IPU_CHAN_ID(channel)); + + if (g_ipu_clk_enabled == 0) { + g_ipu_clk_enabled = 1; + clk_enable(g_ipu_clk); + } + + + if (g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) { + printf("Warning: channel already initialized %d\n", + IPU_CHAN_ID(channel)); + } + + ipu_conf = __raw_readl(IPU_CONF); + + switch (channel) { + case MEM_DC_SYNC: + if (params->mem_dc_sync.di > 1) { + ret = -EINVAL; + goto err; + } + + g_dc_di_assignment[1] = params->mem_dc_sync.di; + ipu_dc_init(1, params->mem_dc_sync.di, + params->mem_dc_sync.interlaced); + ipu_di_use_count[params->mem_dc_sync.di]++; + ipu_dc_use_count++; + ipu_dmfc_use_count++; + break; + case MEM_BG_SYNC: + if (params->mem_dp_bg_sync.di > 1) { + ret = -EINVAL; + goto err; + } + + g_dc_di_assignment[5] = params->mem_dp_bg_sync.di; + ipu_dp_init(channel, params->mem_dp_bg_sync.in_pixel_fmt, + params->mem_dp_bg_sync.out_pixel_fmt); + ipu_dc_init(5, params->mem_dp_bg_sync.di, + params->mem_dp_bg_sync.interlaced); + ipu_di_use_count[params->mem_dp_bg_sync.di]++; + ipu_dc_use_count++; + ipu_dp_use_count++; + ipu_dmfc_use_count++; + break; + case MEM_FG_SYNC: + ipu_dp_init(channel, params->mem_dp_fg_sync.in_pixel_fmt, + params->mem_dp_fg_sync.out_pixel_fmt); + + ipu_dc_use_count++; + ipu_dp_use_count++; + ipu_dmfc_use_count++; + break; + default: + printf("Missing channel initialization\n"); + break; + } + + /* Enable IPU sub module */ + g_channel_init_mask |= 1L << IPU_CHAN_ID(channel); + if (ipu_dc_use_count == 1) + ipu_conf |= IPU_CONF_DC_EN; + if (ipu_dp_use_count == 1) + ipu_conf |= IPU_CONF_DP_EN; + if (ipu_dmfc_use_count == 1) + ipu_conf |= IPU_CONF_DMFC_EN; + if (ipu_di_use_count[0] == 1) { + ipu_conf |= IPU_CONF_DI0_EN; + } + if (ipu_di_use_count[1] == 1) { + ipu_conf |= IPU_CONF_DI1_EN; + } + + __raw_writel(ipu_conf, IPU_CONF); + +err: + return ret; +} + +/* + * This function is called to uninitialize a logical IPU channel. + * + * @param channel Input parameter for the logical channel ID to uninit. + */ +void ipu_uninit_channel(ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma, out_dma = 0; + uint32_t ipu_conf; + + if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) { + debug("Channel already uninitialized %d\n", + IPU_CHAN_ID(channel)); + return; + } + + /* + * Make sure channel is disabled + * Get input and output dma channels + */ + in_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + out_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + if (idma_is_set(IDMAC_CHA_EN, in_dma) || + idma_is_set(IDMAC_CHA_EN, out_dma)) { + printf( + "Channel %d is not disabled, disable first\n", + IPU_CHAN_ID(channel)); + return; + } + + ipu_conf = __raw_readl(IPU_CONF); + + /* Reset the double buffer */ + reg = __raw_readl(IPU_CHA_DB_MODE_SEL(in_dma)); + __raw_writel(reg & ~idma_mask(in_dma), IPU_CHA_DB_MODE_SEL(in_dma)); + reg = __raw_readl(IPU_CHA_DB_MODE_SEL(out_dma)); + __raw_writel(reg & ~idma_mask(out_dma), IPU_CHA_DB_MODE_SEL(out_dma)); + + switch (channel) { + case MEM_DC_SYNC: + ipu_dc_uninit(1); + ipu_di_use_count[g_dc_di_assignment[1]]--; + ipu_dc_use_count--; + ipu_dmfc_use_count--; + break; + case MEM_BG_SYNC: + ipu_dp_uninit(channel); + ipu_dc_uninit(5); + ipu_di_use_count[g_dc_di_assignment[5]]--; + ipu_dc_use_count--; + ipu_dp_use_count--; + ipu_dmfc_use_count--; + break; + case MEM_FG_SYNC: + ipu_dp_uninit(channel); + ipu_dc_use_count--; + ipu_dp_use_count--; + ipu_dmfc_use_count--; + break; + default: + break; + } + + g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel)); + + if (ipu_dc_use_count == 0) + ipu_conf &= ~IPU_CONF_DC_EN; + if (ipu_dp_use_count == 0) + ipu_conf &= ~IPU_CONF_DP_EN; + if (ipu_dmfc_use_count == 0) + ipu_conf &= ~IPU_CONF_DMFC_EN; + if (ipu_di_use_count[0] == 0) { + ipu_conf &= ~IPU_CONF_DI0_EN; + } + if (ipu_di_use_count[1] == 0) { + ipu_conf &= ~IPU_CONF_DI1_EN; + } + + __raw_writel(ipu_conf, IPU_CONF); + + if (ipu_conf == 0) { + clk_disable(g_ipu_clk); + g_ipu_clk_enabled = 0; + } + +} + +static inline void ipu_ch_param_dump(int ch) +{ +#ifdef DEBUG + struct ipu_ch_param *p = ipu_ch_param_addr(ch); + debug("ch %d word 0 - %08X %08X %08X %08X %08X\n", ch, + p->word[0].data[0], p->word[0].data[1], p->word[0].data[2], + p->word[0].data[3], p->word[0].data[4]); + debug("ch %d word 1 - %08X %08X %08X %08X %08X\n", ch, + p->word[1].data[0], p->word[1].data[1], p->word[1].data[2], + p->word[1].data[3], p->word[1].data[4]); + debug("PFS 0x%x, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 85, 4)); + debug("BPP 0x%x, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 107, 3)); + debug("NPB 0x%x\n", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 78, 7)); + + debug("FW %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 125, 13)); + debug("FH %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 138, 12)); + debug("Stride %d\n", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 102, 14)); + + debug("Width0 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 116, 3)); + debug("Width1 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 119, 3)); + debug("Width2 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 122, 3)); + debug("Width3 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 125, 3)); + debug("Offset0 %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 128, 5)); + debug("Offset1 %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 133, 5)); + debug("Offset2 %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 138, 5)); + debug("Offset3 %d\n", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 143, 5)); +#endif +} + +static inline void ipu_ch_params_set_packing(struct ipu_ch_param *p, + int red_width, int red_offset, + int green_width, int green_offset, + int blue_width, int blue_offset, + int alpha_width, int alpha_offset) +{ + /* Setup red width and offset */ + ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1); + ipu_ch_param_set_field(p, 1, 128, 5, red_offset); + /* Setup green width and offset */ + ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1); + ipu_ch_param_set_field(p, 1, 133, 5, green_offset); + /* Setup blue width and offset */ + ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1); + ipu_ch_param_set_field(p, 1, 138, 5, blue_offset); + /* Setup alpha width and offset */ + ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1); + ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset); +} + +static void ipu_ch_param_init(int ch, + uint32_t pixel_fmt, uint32_t width, + uint32_t height, uint32_t stride, + uint32_t u, uint32_t v, + uint32_t uv_stride, dma_addr_t addr0, + dma_addr_t addr1) +{ + uint32_t u_offset = 0; + uint32_t v_offset = 0; + struct ipu_ch_param params; + + memset(¶ms, 0, sizeof(params)); + + ipu_ch_param_set_field(¶ms, 0, 125, 13, width - 1); + + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 0, 138, 12, (height / 2) - 1); + ipu_ch_param_set_field(¶ms, 1, 102, 14, (stride * 2) - 1); + } else { + ipu_ch_param_set_field(¶ms, 0, 138, 12, height - 1); + ipu_ch_param_set_field(¶ms, 1, 102, 14, stride - 1); + } + + ipu_ch_param_set_field(¶ms, 1, 0, 29, addr0 >> 3); + ipu_ch_param_set_field(¶ms, 1, 29, 29, addr1 >> 3); + + switch (pixel_fmt) { + case IPU_PIX_FMT_GENERIC: + /*Represents 8-bit Generic data */ + ipu_ch_param_set_field(¶ms, 0, 107, 3, 5); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 6); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 63); /* burst size */ + + break; + case IPU_PIX_FMT_GENERIC_32: + /*Represents 32-bit Generic data */ + break; + case IPU_PIX_FMT_RGB565: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 5, 0, 6, 5, 5, 11, 8, 16); + break; + case IPU_PIX_FMT_BGR24: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_YUV444: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 16, 8, 8, 8, 0, 8, 24); + break; + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0); + break; + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 24, 8, 16, 8, 8, 8, 0); + break; + case IPU_PIX_FMT_ABGR32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + + ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_UYVY: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0xA); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + break; + case IPU_PIX_FMT_YUYV: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0x8); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + break; + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = stride * height; + v_offset = u_offset + (uv_stride * height / 2); + /* burst size */ + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); + uv_stride = uv_stride*2; + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); + } + break; + case IPU_PIX_FMT_YVU422P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + v_offset = (v == 0) ? stride * height : v; + u_offset = (u == 0) ? v_offset + v_offset / 2 : u; + break; + case IPU_PIX_FMT_YUV422P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = (u == 0) ? stride * height : u; + v_offset = (v == 0) ? u_offset + u_offset / 2 : v; + break; + case IPU_PIX_FMT_NV12: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 4); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + uv_stride = stride; + u_offset = (u == 0) ? stride * height : u; + break; + default: + puts("mxc ipu: unimplemented pixel format\n"); + break; + } + + + if (uv_stride) + ipu_ch_param_set_field(¶ms, 1, 128, 14, uv_stride - 1); + + /* Get the uv offset from user when need cropping */ + if (u || v) { + u_offset = u; + v_offset = v; + } + + /* UBO and VBO are 22-bit */ + if (u_offset/8 > 0x3fffff) + puts("The value of U offset exceeds IPU limitation\n"); + if (v_offset/8 > 0x3fffff) + puts("The value of V offset exceeds IPU limitation\n"); + + ipu_ch_param_set_field(¶ms, 0, 46, 22, u_offset / 8); + ipu_ch_param_set_field(¶ms, 0, 68, 22, v_offset / 8); + + debug("initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ch)); + memcpy(ipu_ch_param_addr(ch), ¶ms, sizeof(params)); +}; + +/* + * This function is called to initialize a buffer for logical IPU channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to initialize. + * + * @param pixel_fmt Input parameter for pixel format of buffer. + * Pixel format is a FOURCC ASCII code. + * + * @param width Input parameter for width of buffer in pixels. + * + * @param height Input parameter for height of buffer in pixels. + * + * @param stride Input parameter for stride length of buffer + * in pixels. + * + * @param phyaddr_0 Input parameter buffer 0 physical address. + * + * @param phyaddr_1 Input parameter buffer 1 physical address. + * Setting this to a value other than NULL enables + * double buffering mode. + * + * @param u private u offset for additional cropping, + * zero if not used. + * + * @param v private v offset for additional cropping, + * zero if not used. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, + uint32_t u, uint32_t v) +{ + uint32_t reg; + uint32_t dma_chan; + + dma_chan = channel_2_dma(channel, type); + if (!idma_is_valid(dma_chan)) + return -EINVAL; + + if (stride < width * bytes_per_pixel(pixel_fmt)) + stride = width * bytes_per_pixel(pixel_fmt); + + if (stride % 4) { + printf( + "Stride not 32-bit aligned, stride = %d\n", stride); + return -EINVAL; + } + /* Build parameter memory data for DMA channel */ + ipu_ch_param_init(dma_chan, pixel_fmt, width, height, stride, u, v, 0, + phyaddr_0, phyaddr_1); + + if (ipu_is_dmfc_chan(dma_chan)) { + ipu_dmfc_set_wait4eot(dma_chan, width); + } + + if (idma_is_set(IDMAC_CHA_PRI, dma_chan)) + ipu_ch_param_set_high_priority(dma_chan); + + ipu_ch_param_dump(dma_chan); + + reg = __raw_readl(IPU_CHA_DB_MODE_SEL(dma_chan)); + if (phyaddr_1) + reg |= idma_mask(dma_chan); + else + reg &= ~idma_mask(dma_chan); + __raw_writel(reg, IPU_CHA_DB_MODE_SEL(dma_chan)); + + /* Reset to buffer 0 */ + __raw_writel(idma_mask(dma_chan), IPU_CHA_CUR_BUF(dma_chan)); + + return 0; +} + +/* + * This function enables a logical channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_enable_channel(ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma; + uint32_t out_dma; + + if (g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) { + printf("Warning: channel already enabled %d\n", + IPU_CHAN_ID(channel)); + } + + /* Get input and output dma channels */ + out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + if (idma_is_valid(in_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(in_dma)); + __raw_writel(reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); + } + if (idma_is_valid(out_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(out_dma)); + __raw_writel(reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); + } + + if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) || + (channel == MEM_FG_SYNC)) + ipu_dp_dc_enable(channel); + + g_channel_enable_mask |= 1L << IPU_CHAN_ID(channel); + + return 0; +} + +/* + * This function clear buffer ready for a logical channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to clear. + * + * @param bufNum Input parameter for which buffer number clear + * ready state. + * + */ +void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum) +{ + uint32_t dma_ch = channel_2_dma(channel, type); + + if (!idma_is_valid(dma_ch)) + return; + + __raw_writel(0xF0000000, IPU_GPR); /* write one to clear */ + if (bufNum == 0) { + if (idma_is_set(IPU_CHA_BUF0_RDY, dma_ch)) { + __raw_writel(idma_mask(dma_ch), + IPU_CHA_BUF0_RDY(dma_ch)); + } + } else { + if (idma_is_set(IPU_CHA_BUF1_RDY, dma_ch)) { + __raw_writel(idma_mask(dma_ch), + IPU_CHA_BUF1_RDY(dma_ch)); + } + } + __raw_writel(0x0, IPU_GPR); /* write one to set */ +} + +/* + * This function disables a logical channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @param wait_for_stop Flag to set whether to wait for channel end + * of frame or return immediately. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_disable_channel(ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma; + uint32_t out_dma; + + if ((g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) { + debug("Channel already disabled %d\n", + IPU_CHAN_ID(channel)); + return 0; + } + + /* Get input and output dma channels */ + out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + if ((idma_is_valid(in_dma) && + !idma_is_set(IDMAC_CHA_EN, in_dma)) + && (idma_is_valid(out_dma) && + !idma_is_set(IDMAC_CHA_EN, out_dma))) + return -EINVAL; + + if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) || + (channel == MEM_DC_SYNC)) { + ipu_dp_dc_disable(channel, 0); + } + + /* Disable DMA channel(s) */ + if (idma_is_valid(in_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(in_dma)); + __raw_writel(reg & ~idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); + __raw_writel(idma_mask(in_dma), IPU_CHA_CUR_BUF(in_dma)); + } + if (idma_is_valid(out_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(out_dma)); + __raw_writel(reg & ~idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); + __raw_writel(idma_mask(out_dma), IPU_CHA_CUR_BUF(out_dma)); + } + + g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel)); + + /* Set channel buffers NOT to be ready */ + if (idma_is_valid(in_dma)) { + ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 0); + ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 1); + } + if (idma_is_valid(out_dma)) { + ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 0); + ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 1); + } + + return 0; +} + +uint32_t bytes_per_pixel(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_GENERIC: /*generic data */ + case IPU_PIX_FMT_RGB332: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YUV422P: + return 1; + break; + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_YUYV: + case IPU_PIX_FMT_UYVY: + return 2; + break; + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + return 3; + break; + case IPU_PIX_FMT_GENERIC_32: /*generic data */ + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_ABGR32: + return 4; + break; + default: + return 1; + break; + } + return 0; +} + +ipu_color_space_t format_to_colorspace(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_RGB666: + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_ABGR32: + case IPU_PIX_FMT_LVDS666: + case IPU_PIX_FMT_LVDS888: + return RGB; + break; + + default: + return YCbCr; + break; + } + return RGB; +} diff --git a/u-boot/drivers/video/ipu_disp.c b/u-boot/drivers/video/ipu_disp.c new file mode 100644 index 0000000..11cf98d --- /dev/null +++ b/u-boot/drivers/video/ipu_disp.c @@ -0,0 +1,1359 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* #define DEBUG */ + +#include <common.h> +#include <linux/types.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include "ipu.h" +#include "ipu_regs.h" + +enum csc_type_t { + RGB2YUV = 0, + YUV2RGB, + RGB2RGB, + YUV2YUV, + CSC_NONE, + CSC_NUM +}; + +struct dp_csc_param_t { + int mode; + void *coeff; +}; + +#define SYNC_WAVE 0 + +/* DC display ID assignments */ +#define DC_DISP_ID_SYNC(di) (di) +#define DC_DISP_ID_SERIAL 2 +#define DC_DISP_ID_ASYNC 3 + +int dmfc_type_setup; +static int dmfc_size_28, dmfc_size_29, dmfc_size_24, dmfc_size_27, dmfc_size_23; +int g_di1_tvout; + +extern struct clk *g_ipu_clk; +extern struct clk *g_di_clk[2]; +extern struct clk *g_pixel_clk[2]; + +extern unsigned char g_ipu_clk_enabled; +extern unsigned char g_dc_di_assignment[]; + +void ipu_dmfc_init(int dmfc_type, int first) +{ + u32 dmfc_wr_chan, dmfc_dp_chan; + + if (first) { + if (dmfc_type_setup > dmfc_type) + dmfc_type = dmfc_type_setup; + else + dmfc_type_setup = dmfc_type; + + /* disable DMFC-IC channel*/ + __raw_writel(0x2, DMFC_IC_CTRL); + } else if (dmfc_type_setup >= DMFC_HIGH_RESOLUTION_DC) { + printf("DMFC high resolution has set, will not change\n"); + return; + } else + dmfc_type_setup = dmfc_type; + + if (dmfc_type == DMFC_HIGH_RESOLUTION_DC) { + /* 1 - segment 0~3; + * 5B - segement 4, 5; + * 5F - segement 6, 7; + * 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC DC HIGH RES: 1(0~3), 5B(4,5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000088; + dmfc_dp_chan = 0x00009694; + dmfc_size_28 = 256 * 4; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 128 * 4; + dmfc_size_23 = 128 * 4; + } else if (dmfc_type == DMFC_HIGH_RESOLUTION_DP) { + /* 1 - segment 0, 1; + * 5B - segement 2~5; + * 5F - segement 6,7; + * 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC DP HIGH RES: 1(0,1), 5B(2~5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000090; + dmfc_dp_chan = 0x0000968a; + dmfc_size_28 = 128 * 4; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 128 * 4; + dmfc_size_23 = 256 * 4; + } else if (dmfc_type == DMFC_HIGH_RESOLUTION_ONLY_DP) { + /* 5B - segement 0~3; + * 5F - segement 4~7; + * 1, 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC ONLY-DP HIGH RES: 5B(0~3), 5F(4~7)\n"); + dmfc_wr_chan = 0x00000000; + dmfc_dp_chan = 0x00008c88; + dmfc_size_28 = 0; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 256 * 4; + dmfc_size_23 = 256 * 4; + } else { + /* 1 - segment 0, 1; + * 5B - segement 4, 5; + * 5F - segement 6, 7; + * 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000090; + dmfc_dp_chan = 0x00009694; + dmfc_size_28 = 128 * 4; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 128 * 4; + dmfc_size_23 = 128 * 4; + } + __raw_writel(dmfc_wr_chan, DMFC_WR_CHAN); + __raw_writel(0x202020F6, DMFC_WR_CHAN_DEF); + __raw_writel(dmfc_dp_chan, DMFC_DP_CHAN); + /* Enable chan 5 watermark set at 5 bursts and clear at 7 bursts */ + __raw_writel(0x2020F6F6, DMFC_DP_CHAN_DEF); +} + +void ipu_dmfc_set_wait4eot(int dma_chan, int width) +{ + u32 dmfc_gen1 = __raw_readl(DMFC_GENERAL1); + + if (width >= HIGH_RESOLUTION_WIDTH) { + if (dma_chan == 23) + ipu_dmfc_init(DMFC_HIGH_RESOLUTION_DP, 0); + else if (dma_chan == 28) + ipu_dmfc_init(DMFC_HIGH_RESOLUTION_DC, 0); + } + + if (dma_chan == 23) { /*5B*/ + if (dmfc_size_23 / width > 3) + dmfc_gen1 |= 1UL << 20; + else + dmfc_gen1 &= ~(1UL << 20); + } else if (dma_chan == 24) { /*6B*/ + if (dmfc_size_24 / width > 1) + dmfc_gen1 |= 1UL << 22; + else + dmfc_gen1 &= ~(1UL << 22); + } else if (dma_chan == 27) { /*5F*/ + if (dmfc_size_27 / width > 2) + dmfc_gen1 |= 1UL << 21; + else + dmfc_gen1 &= ~(1UL << 21); + } else if (dma_chan == 28) { /*1*/ + if (dmfc_size_28 / width > 2) + dmfc_gen1 |= 1UL << 16; + else + dmfc_gen1 &= ~(1UL << 16); + } else if (dma_chan == 29) { /*6F*/ + if (dmfc_size_29 / width > 1) + dmfc_gen1 |= 1UL << 23; + else + dmfc_gen1 &= ~(1UL << 23); + } + + __raw_writel(dmfc_gen1, DMFC_GENERAL1); +} + +static void ipu_di_data_wave_config(int di, + int wave_gen, + int access_size, int component_size) +{ + u32 reg; + reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | + (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); + __raw_writel(reg, DI_DW_GEN(di, wave_gen)); +} + +static void ipu_di_data_pin_config(int di, int wave_gen, int di_pin, int set, + int up, int down) +{ + u32 reg; + + reg = __raw_readl(DI_DW_GEN(di, wave_gen)); + reg &= ~(0x3 << (di_pin * 2)); + reg |= set << (di_pin * 2); + __raw_writel(reg, DI_DW_GEN(di, wave_gen)); + + __raw_writel((down << 16) | up, DI_DW_SET(di, wave_gen, set)); +} + +static void ipu_di_sync_config(int di, int wave_gen, + int run_count, int run_src, + int offset_count, int offset_src, + int repeat_count, int cnt_clr_src, + int cnt_polarity_gen_en, + int cnt_polarity_clr_src, + int cnt_polarity_trigger_src, + int cnt_up, int cnt_down) +{ + u32 reg; + + if ((run_count >= 0x1000) || (offset_count >= 0x1000) || + (repeat_count >= 0x1000) || + (cnt_up >= 0x400) || (cnt_down >= 0x400)) { + printf("DI%d counters out of range.\n", di); + return; + } + + reg = (run_count << 19) | (++run_src << 16) | + (offset_count << 3) | ++offset_src; + __raw_writel(reg, DI_SW_GEN0(di, wave_gen)); + reg = (cnt_polarity_gen_en << 29) | (++cnt_clr_src << 25) | + (++cnt_polarity_trigger_src << 12) | (++cnt_polarity_clr_src << 9); + reg |= (cnt_down << 16) | cnt_up; + if (repeat_count == 0) { + /* Enable auto reload */ + reg |= 0x10000000; + } + __raw_writel(reg, DI_SW_GEN1(di, wave_gen)); + reg = __raw_readl(DI_STP_REP(di, wave_gen)); + reg &= ~(0xFFFF << (16 * ((wave_gen - 1) & 0x1))); + reg |= repeat_count << (16 * ((wave_gen - 1) & 0x1)); + __raw_writel(reg, DI_STP_REP(di, wave_gen)); +} + +static void ipu_dc_map_config(int map, int byte_num, int offset, int mask) +{ + int ptr = map * 3 + byte_num; + u32 reg; + + reg = __raw_readl(DC_MAP_CONF_VAL(ptr)); + reg &= ~(0xFFFF << (16 * (ptr & 0x1))); + reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); + __raw_writel(reg, DC_MAP_CONF_VAL(ptr)); + + reg = __raw_readl(DC_MAP_CONF_PTR(map)); + reg &= ~(0x1F << ((16 * (map & 0x1)) + (5 * byte_num))); + reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); + __raw_writel(reg, DC_MAP_CONF_PTR(map)); +} + +static void ipu_dc_map_clear(int map) +{ + u32 reg = __raw_readl(DC_MAP_CONF_PTR(map)); + __raw_writel(reg & ~(0xFFFF << (16 * (map & 0x1))), + DC_MAP_CONF_PTR(map)); +} + +static void ipu_dc_write_tmpl(int word, u32 opcode, u32 operand, int map, + int wave, int glue, int sync) +{ + u32 reg; + int stop = 1; + + reg = sync; + reg |= (glue << 4); + reg |= (++wave << 11); + reg |= (++map << 15); + reg |= (operand << 20) & 0xFFF00000; + __raw_writel(reg, ipu_dc_tmpl_reg + word * 2); + + reg = (operand >> 12); + reg |= opcode << 4; + reg |= (stop << 9); + __raw_writel(reg, ipu_dc_tmpl_reg + word * 2 + 1); +} + +static void ipu_dc_link_event(int chan, int event, int addr, int priority) +{ + u32 reg; + + reg = __raw_readl(DC_RL_CH(chan, event)); + reg &= ~(0xFFFF << (16 * (event & 0x1))); + reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); + __raw_writel(reg, DC_RL_CH(chan, event)); +} + +/* Y = R * 1.200 + G * 2.343 + B * .453 + 0.250; + * U = R * -.672 + G * -1.328 + B * 2.000 + 512.250.; + * V = R * 2.000 + G * -1.672 + B * -.328 + 512.250.; + */ +static const int rgb2ycbcr_coeff[5][3] = { + {0x4D, 0x96, 0x1D}, + {0x3D5, 0x3AB, 0x80}, + {0x80, 0x395, 0x3EB}, + {0x0000, 0x0200, 0x0200}, /* B0, B1, B2 */ + {0x2, 0x2, 0x2}, /* S0, S1, S2 */ +}; + +/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); + * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); + * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); + */ +static const int ycbcr2rgb_coeff[5][3] = { + {0x095, 0x000, 0x0CC}, + {0x095, 0x3CE, 0x398}, + {0x095, 0x0FF, 0x000}, + {0x3E42, 0x010A, 0x3DD6}, /*B0,B1,B2 */ + {0x1, 0x1, 0x1}, /*S0,S1,S2 */ +}; + +#define mask_a(a) ((u32)(a) & 0x3FF) +#define mask_b(b) ((u32)(b) & 0x3FFF) + +/* Pls keep S0, S1 and S2 as 0x2 by using this convertion */ +static int rgb_to_yuv(int n, int red, int green, int blue) +{ + int c; + c = red * rgb2ycbcr_coeff[n][0]; + c += green * rgb2ycbcr_coeff[n][1]; + c += blue * rgb2ycbcr_coeff[n][2]; + c /= 16; + c += rgb2ycbcr_coeff[3][n] * 4; + c += 8; + c /= 16; + if (c < 0) + c = 0; + if (c > 255) + c = 255; + return c; +} + +/* + * Row is for BG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE + * Column is for FG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE + */ +static struct dp_csc_param_t dp_csc_array[CSC_NUM][CSC_NUM] = { + { + {DP_COM_CONF_CSC_DEF_BOTH, &rgb2ycbcr_coeff}, + {0, 0}, + {0, 0}, + {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff}, + {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff} + }, + { + {0, 0}, + {DP_COM_CONF_CSC_DEF_BOTH, &ycbcr2rgb_coeff}, + {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff}, + {0, 0}, + {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff} + }, + { + {0, 0}, + {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, + {0, 0}, + {0, 0}, + {0, 0} + }, + { + {DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} + }, + { + {DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, + {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, + {0, 0}, + {0, 0}, + {0, 0} + } +}; + +static enum csc_type_t fg_csc_type = CSC_NONE, bg_csc_type = CSC_NONE; +static int color_key_4rgb = 1; + +void ipu_dp_csc_setup(int dp, struct dp_csc_param_t dp_csc_param, + unsigned char srm_mode_update) +{ + u32 reg; + const int (*coeff)[5][3]; + + if (dp_csc_param.mode >= 0) { + reg = __raw_readl(DP_COM_CONF(dp)); + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + reg |= dp_csc_param.mode; + __raw_writel(reg, DP_COM_CONF(dp)); + } + + coeff = dp_csc_param.coeff; + + if (coeff) { + __raw_writel(mask_a((*coeff)[0][0]) | + (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0(dp)); + __raw_writel(mask_a((*coeff)[0][2]) | + (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1(dp)); + __raw_writel(mask_a((*coeff)[1][1]) | + (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2(dp)); + __raw_writel(mask_a((*coeff)[2][0]) | + (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3(dp)); + __raw_writel(mask_a((*coeff)[2][2]) | + (mask_b((*coeff)[3][0]) << 16) | + ((*coeff)[4][0] << 30), DP_CSC_0(dp)); + __raw_writel(mask_b((*coeff)[3][1]) | ((*coeff)[4][1] << 14) | + (mask_b((*coeff)[3][2]) << 16) | + ((*coeff)[4][2] << 30), DP_CSC_1(dp)); + } + + if (srm_mode_update) { + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + } +} + +int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt, + uint32_t out_pixel_fmt) +{ + int in_fmt, out_fmt; + int dp; + int partial = 0; + uint32_t reg; + + if (channel == MEM_FG_SYNC) { + dp = DP_SYNC; + partial = 1; + } else if (channel == MEM_BG_SYNC) { + dp = DP_SYNC; + partial = 0; + } else if (channel == MEM_BG_ASYNC0) { + dp = DP_ASYNC0; + partial = 0; + } else { + return -EINVAL; + } + + in_fmt = format_to_colorspace(in_pixel_fmt); + out_fmt = format_to_colorspace(out_pixel_fmt); + + if (partial) { + if (in_fmt == RGB) { + if (out_fmt == RGB) + fg_csc_type = RGB2RGB; + else + fg_csc_type = RGB2YUV; + } else { + if (out_fmt == RGB) + fg_csc_type = YUV2RGB; + else + fg_csc_type = YUV2YUV; + } + } else { + if (in_fmt == RGB) { + if (out_fmt == RGB) + bg_csc_type = RGB2RGB; + else + bg_csc_type = RGB2YUV; + } else { + if (out_fmt == RGB) + bg_csc_type = YUV2RGB; + else + bg_csc_type = YUV2YUV; + } + } + + /* Transform color key from rgb to yuv if CSC is enabled */ + reg = __raw_readl(DP_COM_CONF(dp)); + if (color_key_4rgb && (reg & DP_COM_CONF_GWCKE) && + (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB)))) { + int red, green, blue; + int y, u, v; + uint32_t color_key = __raw_readl(DP_GRAPH_WIND_CTRL(dp)) & + 0xFFFFFFL; + + debug("_ipu_dp_init color key 0x%x need change to yuv fmt!\n", + color_key); + + red = (color_key >> 16) & 0xFF; + green = (color_key >> 8) & 0xFF; + blue = color_key & 0xFF; + + y = rgb_to_yuv(0, red, green, blue); + u = rgb_to_yuv(1, red, green, blue); + v = rgb_to_yuv(2, red, green, blue); + color_key = (y << 16) | (u << 8) | v; + + reg = __raw_readl(DP_GRAPH_WIND_CTRL(dp)) & 0xFF000000L; + __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(dp)); + color_key_4rgb = 0; + + debug("_ipu_dp_init color key change to yuv fmt 0x%x!\n", + color_key); + } + + ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], 1); + + return 0; +} + +void ipu_dp_uninit(ipu_channel_t channel) +{ + int dp; + int partial = 0; + + if (channel == MEM_FG_SYNC) { + dp = DP_SYNC; + partial = 1; + } else if (channel == MEM_BG_SYNC) { + dp = DP_SYNC; + partial = 0; + } else if (channel == MEM_BG_ASYNC0) { + dp = DP_ASYNC0; + partial = 0; + } else { + return; + } + + if (partial) + fg_csc_type = CSC_NONE; + else + bg_csc_type = CSC_NONE; + + ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], 0); +} + +void ipu_dc_init(int dc_chan, int di, unsigned char interlaced) +{ + u32 reg = 0; + + if ((dc_chan == 1) || (dc_chan == 5)) { + if (interlaced) { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 3); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1); + } else { + if (di) { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, + 4, 1); + } else { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, + 7, 1); + } + } + ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0); + + reg = 0x2; + reg |= DC_DISP_ID_SYNC(di) << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET; + reg |= di << 2; + if (interlaced) + reg |= DC_WR_CH_CONF_FIELD_MODE; + } else if ((dc_chan == 8) || (dc_chan == 9)) { + /* async channels */ + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0x64, 1); + + reg = 0x3; + reg |= DC_DISP_ID_SERIAL << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET; + } + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + + __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan)); + + __raw_writel(0x00000084, DC_GEN); +} + +void ipu_dc_uninit(int dc_chan) +{ + if ((dc_chan == 1) || (dc_chan == 5)) { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0); + } else if ((dc_chan == 8) || (dc_chan == 9)) { + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_1, 0, 0); + } +} + +int ipu_chan_is_interlaced(ipu_channel_t channel) +{ + if (channel == MEM_DC_SYNC) + return !!(__raw_readl(DC_WR_CH_CONF_1) & + DC_WR_CH_CONF_FIELD_MODE); + else if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC)) + return !!(__raw_readl(DC_WR_CH_CONF_5) & + DC_WR_CH_CONF_FIELD_MODE); + return 0; +} + +void ipu_dp_dc_enable(ipu_channel_t channel) +{ + int di; + uint32_t reg; + uint32_t dc_chan; + + if (channel == MEM_FG_SYNC) + dc_chan = 5; + if (channel == MEM_DC_SYNC) + dc_chan = 1; + else if (channel == MEM_BG_SYNC) + dc_chan = 5; + else + return; + + if (channel == MEM_FG_SYNC) { + /* Enable FG channel */ + reg = __raw_readl(DP_COM_CONF(DP_SYNC)); + __raw_writel(reg | DP_COM_CONF_FG_EN, DP_COM_CONF(DP_SYNC)); + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + return; + } + + di = g_dc_di_assignment[dc_chan]; + + /* Make sure other DC sync channel is not assigned same DI */ + reg = __raw_readl(DC_WR_CH_CONF(6 - dc_chan)); + if ((di << 2) == (reg & DC_WR_CH_CONF_PROG_DI_ID)) { + reg &= ~DC_WR_CH_CONF_PROG_DI_ID; + reg |= di ? 0 : DC_WR_CH_CONF_PROG_DI_ID; + __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan)); + } + + reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); + reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET; + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + + clk_enable(g_pixel_clk[di]); +} + +static unsigned char dc_swap; + +void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) +{ + uint32_t reg; + uint32_t csc; + uint32_t dc_chan = 0; + int timeout = 50; + + dc_swap = swap; + + if (channel == MEM_DC_SYNC) { + dc_chan = 1; + } else if (channel == MEM_BG_SYNC) { + dc_chan = 5; + } else if (channel == MEM_FG_SYNC) { + /* Disable FG channel */ + dc_chan = 5; + + reg = __raw_readl(DP_COM_CONF(DP_SYNC)); + csc = reg & DP_COM_CONF_CSC_DEF_MASK; + if (csc == DP_COM_CONF_CSC_DEF_FG) + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + + reg &= ~DP_COM_CONF_FG_EN; + __raw_writel(reg, DP_COM_CONF(DP_SYNC)); + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + + timeout = 50; + + /* + * Wait for DC triple buffer to empty, + * this check is useful for tv overlay. + */ + if (g_dc_di_assignment[dc_chan] == 0) + while ((__raw_readl(DC_STAT) & 0x00000002) + != 0x00000002) { + udelay(2000); + timeout -= 2; + if (timeout <= 0) + break; + } + else if (g_dc_di_assignment[dc_chan] == 1) + while ((__raw_readl(DC_STAT) & 0x00000020) + != 0x00000020) { + udelay(2000); + timeout -= 2; + if (timeout <= 0) + break; + } + return; + } else { + return; + } + + if (dc_swap) { + /* Swap DC channel 1 and 5 settings, and disable old dc chan */ + reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); + __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan)); + reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + reg ^= DC_WR_CH_CONF_PROG_DI_ID; + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + } else { + timeout = 50; + + /* Wait for DC triple buffer to empty */ + if (g_dc_di_assignment[dc_chan] == 0) + while ((__raw_readl(DC_STAT) & 0x00000002) + != 0x00000002) { + udelay(2000); + timeout -= 2; + if (timeout <= 0) + break; + } + else if (g_dc_di_assignment[dc_chan] == 1) + while ((__raw_readl(DC_STAT) & 0x00000020) + != 0x00000020) { + udelay(2000); + timeout -= 2; + if (timeout <= 0) + break; + } + + reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); + reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + + reg = __raw_readl(IPU_DISP_GEN); + if (g_dc_di_assignment[dc_chan]) + reg &= ~DI1_COUNTER_RELEASE; + else + reg &= ~DI0_COUNTER_RELEASE; + __raw_writel(reg, IPU_DISP_GEN); + + /* Clock is already off because it must be done quickly, but + we need to fix the ref count */ + clk_disable(g_pixel_clk[g_dc_di_assignment[dc_chan]]); + } +} + +void ipu_init_dc_mappings(void) +{ + /* IPU_PIX_FMT_RGB24 */ + ipu_dc_map_clear(0); + ipu_dc_map_config(0, 0, 7, 0xFF); + ipu_dc_map_config(0, 1, 15, 0xFF); + ipu_dc_map_config(0, 2, 23, 0xFF); + + /* IPU_PIX_FMT_RGB666 */ + ipu_dc_map_clear(1); + ipu_dc_map_config(1, 0, 5, 0xFC); + ipu_dc_map_config(1, 1, 11, 0xFC); + ipu_dc_map_config(1, 2, 17, 0xFC); + + /* IPU_PIX_FMT_YUV444 */ + ipu_dc_map_clear(2); + ipu_dc_map_config(2, 0, 15, 0xFF); + ipu_dc_map_config(2, 1, 23, 0xFF); + ipu_dc_map_config(2, 2, 7, 0xFF); + + /* IPU_PIX_FMT_RGB565 */ + ipu_dc_map_clear(3); + ipu_dc_map_config(3, 0, 4, 0xF8); + ipu_dc_map_config(3, 1, 10, 0xFC); + ipu_dc_map_config(3, 2, 15, 0xF8); + + /* IPU_PIX_FMT_LVDS666 */ + ipu_dc_map_clear(4); + ipu_dc_map_config(4, 0, 5, 0xFC); + ipu_dc_map_config(4, 1, 13, 0xFC); + ipu_dc_map_config(4, 2, 21, 0xFC); +} + +int ipu_pixfmt_to_map(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_GENERIC: + case IPU_PIX_FMT_RGB24: + return 0; + case IPU_PIX_FMT_RGB666: + return 1; + case IPU_PIX_FMT_YUV444: + return 2; + case IPU_PIX_FMT_RGB565: + return 3; + case IPU_PIX_FMT_LVDS666: + return 4; + } + + return -1; +} + +/* + * This function is called to adapt synchronous LCD panel to IPU restriction. + */ +void adapt_panel_to_ipu_restricitions(uint32_t *pixel_clk, + uint16_t width, uint16_t height, + uint16_t h_start_width, + uint16_t h_end_width, + uint16_t v_start_width, + uint16_t *v_end_width) +{ + if (*v_end_width < 2) { + uint16_t total_width = width + h_start_width + h_end_width; + uint16_t total_height_old = height + v_start_width + + (*v_end_width); + uint16_t total_height_new = height + v_start_width + 2; + *v_end_width = 2; + *pixel_clk = (*pixel_clk) * total_width * total_height_new / + (total_width * total_height_old); + printf("WARNING: adapt panel end blank lines\n"); + } +} + +/* + * This function is called to initialize a synchronous LCD panel. + * + * @param disp The DI the panel is attached to. + * + * @param pixel_clk Desired pixel clock frequency in Hz. + * + * @param pixel_fmt Input parameter for pixel format of buffer. + * Pixel format is a FOURCC ASCII code. + * + * @param width The width of panel in pixels. + * + * @param height The height of panel in pixels. + * + * @param hStartWidth The number of pixel clocks between the HSYNC + * signal pulse and the start of valid data. + * + * @param hSyncWidth The width of the HSYNC signal in units of pixel + * clocks. + * + * @param hEndWidth The number of pixel clocks between the end of + * valid data and the HSYNC signal for next line. + * + * @param vStartWidth The number of lines between the VSYNC + * signal pulse and the start of valid data. + * + * @param vSyncWidth The width of the VSYNC signal in units of lines + * + * @param vEndWidth The number of lines between the end of valid + * data and the VSYNC signal for next frame. + * + * @param sig Bitfield of signal polarities for LCD interface. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ + +int32_t ipu_init_sync_panel(int disp, uint32_t pixel_clk, + uint16_t width, uint16_t height, + uint32_t pixel_fmt, + uint16_t h_start_width, uint16_t h_sync_width, + uint16_t h_end_width, uint16_t v_start_width, + uint16_t v_sync_width, uint16_t v_end_width, + uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig) +{ + uint32_t reg; + uint32_t di_gen, vsync_cnt; + uint32_t div, rounded_pixel_clk; + uint32_t h_total, v_total; + int map; + struct clk *di_parent; + + debug("panel size = %d x %d\n", width, height); + + if ((v_sync_width == 0) || (h_sync_width == 0)) + return EINVAL; + + adapt_panel_to_ipu_restricitions(&pixel_clk, width, height, + h_start_width, h_end_width, + v_start_width, &v_end_width); + h_total = width + h_sync_width + h_start_width + h_end_width; + v_total = height + v_sync_width + v_start_width + v_end_width; + + /* Init clocking */ + debug("pixel clk = %d\n", pixel_clk); + + if (sig.ext_clk) { + if (!(g_di1_tvout && (disp == 1))) { /*not round div for tvout*/ + /* + * Set the PLL to be an even multiple + * of the pixel clock. + */ + if ((clk_get_usecount(g_pixel_clk[0]) == 0) && + (clk_get_usecount(g_pixel_clk[1]) == 0)) { + di_parent = clk_get_parent(g_di_clk[disp]); + rounded_pixel_clk = + clk_round_rate(g_pixel_clk[disp], + pixel_clk); + div = clk_get_rate(di_parent) / + rounded_pixel_clk; + if (div % 2) + div++; + if (clk_get_rate(di_parent) != div * + rounded_pixel_clk) + clk_set_rate(di_parent, + div * rounded_pixel_clk); + udelay(10000); + clk_set_rate(g_di_clk[disp], + 2 * rounded_pixel_clk); + udelay(10000); + } + } + clk_set_parent(g_pixel_clk[disp], g_di_clk[disp]); + } else { + if (clk_get_usecount(g_pixel_clk[disp]) != 0) + clk_set_parent(g_pixel_clk[disp], g_ipu_clk); + } + rounded_pixel_clk = clk_round_rate(g_pixel_clk[disp], pixel_clk); + clk_set_rate(g_pixel_clk[disp], rounded_pixel_clk); + udelay(5000); + /* Get integer portion of divider */ + div = clk_get_rate(clk_get_parent(g_pixel_clk[disp])) / + rounded_pixel_clk; + + ipu_di_data_wave_config(disp, SYNC_WAVE, div - 1, div - 1); + ipu_di_data_pin_config(disp, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); + + map = ipu_pixfmt_to_map(pixel_fmt); + if (map < 0) { + debug("IPU_DISP: No MAP\n"); + return -EINVAL; + } + + di_gen = __raw_readl(DI_GENERAL(disp)); + + if (sig.interlaced) { + /* Setup internal HSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 1, /* counter */ + h_total / 2 - 1,/* run count */ + DI_SYNC_CLK, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Field 1 VSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 2, /* counter */ + h_total - 1, /* run count */ + DI_SYNC_CLK, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 4 /* COUNT DOWN */ + ); + + /* Setup internal HSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 3, /* counter */ + v_total * 2 - 1,/* run count */ + DI_SYNC_INT_HSYNC, /* run_resolution */ + 1, /* offset */ + DI_SYNC_INT_HSYNC, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 4 /* COUNT DOWN */ + ); + + /* Active Field ? */ + ipu_di_sync_config( + disp, /* display */ + 4, /* counter */ + v_total / 2 - 1,/* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + v_start_width, /* offset */ + DI_SYNC_HSYNC, /* offset resolution */ + 2, /* repeat count */ + DI_SYNC_VSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Active Line */ + ipu_di_sync_config( + disp, /* display */ + 5, /* counter */ + 0, /* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + height / 2, /* repeat count */ + 4, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Field 0 VSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 6, /* counter */ + v_total - 1, /* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* DC VSYNC waveform */ + vsync_cnt = 7; + ipu_di_sync_config( + disp, /* display */ + 7, /* counter */ + v_total / 2 - 1,/* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 9, /* offset */ + DI_SYNC_HSYNC, /* offset resolution */ + 2, /* repeat count */ + DI_SYNC_VSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* active pixel waveform */ + ipu_di_sync_config( + disp, /* display */ + 8, /* counter */ + 0, /* run count */ + DI_SYNC_CLK, /* run_resolution */ + h_start_width, /* offset */ + DI_SYNC_CLK, /* offset resolution */ + width, /* repeat count */ + 5, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + ipu_di_sync_config( + disp, /* display */ + 9, /* counter */ + v_total - 1, /* run count */ + DI_SYNC_INT_HSYNC,/* run_resolution */ + v_total / 2, /* offset */ + DI_SYNC_INT_HSYNC,/* offset resolution */ + 0, /* repeat count */ + DI_SYNC_HSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 4 /* COUNT DOWN */ + ); + + /* set gentime select and tag sel */ + reg = __raw_readl(DI_SW_GEN1(disp, 9)); + reg &= 0x1FFFFFFF; + reg |= (3 - 1)<<29 | 0x00008000; + __raw_writel(reg, DI_SW_GEN1(disp, 9)); + + __raw_writel(v_total / 2 - 1, DI_SCR_CONF(disp)); + + /* set y_sel = 1 */ + di_gen |= 0x10000000; + di_gen |= DI_GEN_POLARITY_5; + di_gen |= DI_GEN_POLARITY_8; + } else { + /* Setup internal HSYNC waveform */ + ipu_di_sync_config(disp, 1, h_total - 1, DI_SYNC_CLK, + 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, + 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + + /* Setup external (delayed) HSYNC waveform */ + ipu_di_sync_config(disp, DI_SYNC_HSYNC, h_total - 1, + DI_SYNC_CLK, div * v_to_h_sync, DI_SYNC_CLK, + 0, DI_SYNC_NONE, 1, DI_SYNC_NONE, + DI_SYNC_CLK, 0, h_sync_width * 2); + /* Setup VSYNC waveform */ + vsync_cnt = DI_SYNC_VSYNC; + ipu_di_sync_config(disp, DI_SYNC_VSYNC, v_total - 1, + DI_SYNC_INT_HSYNC, 0, DI_SYNC_NONE, 0, + DI_SYNC_NONE, 1, DI_SYNC_NONE, + DI_SYNC_INT_HSYNC, 0, v_sync_width * 2); + __raw_writel(v_total - 1, DI_SCR_CONF(disp)); + + /* Setup active data waveform to sync with DC */ + ipu_di_sync_config(disp, 4, 0, DI_SYNC_HSYNC, + v_sync_width + v_start_width, DI_SYNC_HSYNC, + height, + DI_SYNC_VSYNC, 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + ipu_di_sync_config(disp, 5, 0, DI_SYNC_CLK, + h_sync_width + h_start_width, DI_SYNC_CLK, + width, 4, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, + 0); + + /* reset all unused counters */ + __raw_writel(0, DI_SW_GEN0(disp, 6)); + __raw_writel(0, DI_SW_GEN1(disp, 6)); + __raw_writel(0, DI_SW_GEN0(disp, 7)); + __raw_writel(0, DI_SW_GEN1(disp, 7)); + __raw_writel(0, DI_SW_GEN0(disp, 8)); + __raw_writel(0, DI_SW_GEN1(disp, 8)); + __raw_writel(0, DI_SW_GEN0(disp, 9)); + __raw_writel(0, DI_SW_GEN1(disp, 9)); + + reg = __raw_readl(DI_STP_REP(disp, 6)); + reg &= 0x0000FFFF; + __raw_writel(reg, DI_STP_REP(disp, 6)); + __raw_writel(0, DI_STP_REP(disp, 7)); + __raw_writel(0, DI_STP_REP(disp, 9)); + + /* Init template microcode */ + if (disp) { + ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5); + ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5); + ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5); + } else { + ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5); + ipu_dc_write_tmpl(6, WROD(0), 0, map, SYNC_WAVE, 4, 5); + ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5); + } + + if (sig.Hsync_pol) + di_gen |= DI_GEN_POLARITY_2; + if (sig.Vsync_pol) + di_gen |= DI_GEN_POLARITY_3; + + if (sig.clk_pol) + di_gen |= DI_GEN_POL_CLK; + + } + + __raw_writel(di_gen, DI_GENERAL(disp)); + + __raw_writel((--vsync_cnt << DI_VSYNC_SEL_OFFSET) | + 0x00000002, DI_SYNC_AS_GEN(disp)); + + reg = __raw_readl(DI_POL(disp)); + reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); + if (sig.enable_pol) + reg |= DI_POL_DRDY_POLARITY_15; + if (sig.data_pol) + reg |= DI_POL_DRDY_DATA_POLARITY; + __raw_writel(reg, DI_POL(disp)); + + __raw_writel(width, DC_DISP_CONF2(DC_DISP_ID_SYNC(disp))); + + return 0; +} + +/* + * This function sets the foreground and background plane global alpha blending + * modes. This function also sets the DP graphic plane according to the + * parameter of IPUv3 DP channel. + * + * @param channel IPUv3 DP channel + * + * @param enable Boolean to enable or disable global alpha + * blending. If disabled, local blending is used. + * + * @param alpha Global alpha value. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, unsigned char enable, + uint8_t alpha) +{ + uint32_t reg; + uint32_t flow; + + unsigned char bg_chan; + + if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) + flow = DP_SYNC; + else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) + flow = DP_ASYNC0; + else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1) + flow = DP_ASYNC1; + else + return -EINVAL; + + if (channel == MEM_BG_SYNC || channel == MEM_BG_ASYNC0 || + channel == MEM_BG_ASYNC1) + bg_chan = 1; + else + bg_chan = 0; + + if (!g_ipu_clk_enabled) + clk_enable(g_ipu_clk); + + if (bg_chan) { + reg = __raw_readl(DP_COM_CONF(flow)); + __raw_writel(reg & ~DP_COM_CONF_GWSEL, DP_COM_CONF(flow)); + } else { + reg = __raw_readl(DP_COM_CONF(flow)); + __raw_writel(reg | DP_COM_CONF_GWSEL, DP_COM_CONF(flow)); + } + + if (enable) { + reg = __raw_readl(DP_GRAPH_WIND_CTRL(flow)) & 0x00FFFFFFL; + __raw_writel(reg | ((uint32_t) alpha << 24), + DP_GRAPH_WIND_CTRL(flow)); + + reg = __raw_readl(DP_COM_CONF(flow)); + __raw_writel(reg | DP_COM_CONF_GWAM, DP_COM_CONF(flow)); + } else { + reg = __raw_readl(DP_COM_CONF(flow)); + __raw_writel(reg & ~DP_COM_CONF_GWAM, DP_COM_CONF(flow)); + } + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + + if (!g_ipu_clk_enabled) + clk_disable(g_ipu_clk); + + return 0; +} + +/* + * This function sets the transparent color key for SDC graphic plane. + * + * @param channel Input parameter for the logical channel ID. + * + * @param enable Boolean to enable or disable color key + * + * @param colorKey 24-bit RGB color for transparent color key. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_disp_set_color_key(ipu_channel_t channel, unsigned char enable, + uint32_t color_key) +{ + uint32_t reg, flow; + int y, u, v; + int red, green, blue; + + if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) + flow = DP_SYNC; + else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) + flow = DP_ASYNC0; + else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1) + flow = DP_ASYNC1; + else + return -EINVAL; + + if (!g_ipu_clk_enabled) + clk_enable(g_ipu_clk); + + color_key_4rgb = 1; + /* Transform color key from rgb to yuv if CSC is enabled */ + if (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB))) { + + debug("color key 0x%x need change to yuv fmt\n", color_key); + + red = (color_key >> 16) & 0xFF; + green = (color_key >> 8) & 0xFF; + blue = color_key & 0xFF; + + y = rgb_to_yuv(0, red, green, blue); + u = rgb_to_yuv(1, red, green, blue); + v = rgb_to_yuv(2, red, green, blue); + color_key = (y << 16) | (u << 8) | v; + + color_key_4rgb = 0; + + debug("color key change to yuv fmt 0x%x\n", color_key); + } + + if (enable) { + reg = __raw_readl(DP_GRAPH_WIND_CTRL(flow)) & 0xFF000000L; + __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(flow)); + + reg = __raw_readl(DP_COM_CONF(flow)); + __raw_writel(reg | DP_COM_CONF_GWCKE, DP_COM_CONF(flow)); + } else { + reg = __raw_readl(DP_COM_CONF(flow)); + __raw_writel(reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF(flow)); + } + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + + if (!g_ipu_clk_enabled) + clk_disable(g_ipu_clk); + + return 0; +} diff --git a/u-boot/drivers/video/ipu_regs.h b/u-boot/drivers/video/ipu_regs.h new file mode 100644 index 0000000..36f07bb --- /dev/null +++ b/u-boot/drivers/video/ipu_regs.h @@ -0,0 +1,418 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2009 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 __IPU_REGS_INCLUDED__ +#define __IPU_REGS_INCLUDED__ + +#define IPU_DISP0_BASE 0x00000000 +#define IPU_MCU_T_DEFAULT 8 +#define IPU_DISP1_BASE (IPU_MCU_T_DEFAULT << 25) +#define IPU_CM_REG_BASE 0x1E000000 +#define IPU_STAT_REG_BASE 0x1E000200 +#define IPU_IDMAC_REG_BASE 0x1E008000 +#define IPU_ISP_REG_BASE 0x1E010000 +#define IPU_DP_REG_BASE 0x1E018000 +#define IPU_IC_REG_BASE 0x1E020000 +#define IPU_IRT_REG_BASE 0x1E028000 +#define IPU_CSI0_REG_BASE 0x1E030000 +#define IPU_CSI1_REG_BASE 0x1E038000 +#define IPU_DI0_REG_BASE 0x1E040000 +#define IPU_DI1_REG_BASE 0x1E048000 +#define IPU_SMFC_REG_BASE 0x1E050000 +#define IPU_DC_REG_BASE 0x1E058000 +#define IPU_DMFC_REG_BASE 0x1E060000 +#define IPU_CPMEM_REG_BASE 0x1F000000 +#define IPU_LUT_REG_BASE 0x1F020000 +#define IPU_SRM_REG_BASE 0x1F040000 +#define IPU_TPM_REG_BASE 0x1F060000 +#define IPU_DC_TMPL_REG_BASE 0x1F080000 +#define IPU_ISP_TBPR_REG_BASE 0x1F0C0000 +#define IPU_VDI_REG_BASE 0x1E068000 + + +extern u32 *ipu_dc_tmpl_reg; + +#define DC_EVT_NF 0 +#define DC_EVT_NL 1 +#define DC_EVT_EOF 2 +#define DC_EVT_NFIELD 3 +#define DC_EVT_EOL 4 +#define DC_EVT_EOFIELD 5 +#define DC_EVT_NEW_ADDR 6 +#define DC_EVT_NEW_CHAN 7 +#define DC_EVT_NEW_DATA 8 + +#define DC_EVT_NEW_ADDR_W_0 0 +#define DC_EVT_NEW_ADDR_W_1 1 +#define DC_EVT_NEW_CHAN_W_0 2 +#define DC_EVT_NEW_CHAN_W_1 3 +#define DC_EVT_NEW_DATA_W_0 4 +#define DC_EVT_NEW_DATA_W_1 5 +#define DC_EVT_NEW_ADDR_R_0 6 +#define DC_EVT_NEW_ADDR_R_1 7 +#define DC_EVT_NEW_CHAN_R_0 8 +#define DC_EVT_NEW_CHAN_R_1 9 +#define DC_EVT_NEW_DATA_R_0 10 +#define DC_EVT_NEW_DATA_R_1 11 + +/* Software reset for ipu */ +#define SW_IPU_RST 8 + +enum { + IPU_CONF_DP_EN = 0x00000020, + IPU_CONF_DI0_EN = 0x00000040, + IPU_CONF_DI1_EN = 0x00000080, + IPU_CONF_DMFC_EN = 0x00000400, + IPU_CONF_DC_EN = 0x00000200, + + DI0_COUNTER_RELEASE = 0x01000000, + DI1_COUNTER_RELEASE = 0x02000000, + + DI_DW_GEN_ACCESS_SIZE_OFFSET = 24, + DI_DW_GEN_COMPONENT_SIZE_OFFSET = 16, + + DI_GEN_DI_CLK_EXT = 0x100000, + DI_GEN_POLARITY_1 = 0x00000001, + DI_GEN_POLARITY_2 = 0x00000002, + DI_GEN_POLARITY_3 = 0x00000004, + DI_GEN_POLARITY_4 = 0x00000008, + DI_GEN_POLARITY_5 = 0x00000010, + DI_GEN_POLARITY_6 = 0x00000020, + DI_GEN_POLARITY_7 = 0x00000040, + DI_GEN_POLARITY_8 = 0x00000080, + DI_GEN_POL_CLK = 0x20000, + + DI_POL_DRDY_DATA_POLARITY = 0x00000080, + DI_POL_DRDY_POLARITY_15 = 0x00000010, + DI_VSYNC_SEL_OFFSET = 13, + + DC_WR_CH_CONF_FIELD_MODE = 0x00000200, + DC_WR_CH_CONF_PROG_TYPE_OFFSET = 5, + DC_WR_CH_CONF_PROG_TYPE_MASK = 0x000000E0, + DC_WR_CH_CONF_PROG_DI_ID = 0x00000004, + DC_WR_CH_CONF_PROG_DISP_ID_OFFSET = 3, + DC_WR_CH_CONF_PROG_DISP_ID_MASK = 0x00000018, + + DP_COM_CONF_FG_EN = 0x00000001, + DP_COM_CONF_GWSEL = 0x00000002, + DP_COM_CONF_GWAM = 0x00000004, + DP_COM_CONF_GWCKE = 0x00000008, + DP_COM_CONF_CSC_DEF_MASK = 0x00000300, + DP_COM_CONF_CSC_DEF_OFFSET = 8, + DP_COM_CONF_CSC_DEF_FG = 0x00000300, + DP_COM_CONF_CSC_DEF_BG = 0x00000200, + DP_COM_CONF_CSC_DEF_BOTH = 0x00000100, + DP_COM_CONF_GAMMA_EN = 0x00001000, + DP_COM_CONF_GAMMA_YUV_EN = 0x00002000, +}; + +enum di_pins { + DI_PIN11 = 0, + DI_PIN12 = 1, + DI_PIN13 = 2, + DI_PIN14 = 3, + DI_PIN15 = 4, + DI_PIN16 = 5, + DI_PIN17 = 6, + DI_PIN_CS = 7, + + DI_PIN_SER_CLK = 0, + DI_PIN_SER_RS = 1, +}; + +enum di_sync_wave { + DI_SYNC_NONE = -1, + DI_SYNC_CLK = 0, + DI_SYNC_INT_HSYNC = 1, + DI_SYNC_HSYNC = 2, + DI_SYNC_VSYNC = 3, + DI_SYNC_DE = 5, +}; + +struct ipu_cm { + u32 conf; + u32 sisg_ctrl0; + u32 sisg_ctrl1; + u32 sisg_set[6]; + u32 sisg_clear[6]; + u32 int_ctrl[15]; + u32 sdma_event[10]; + u32 srm_pri1; + u32 srm_pri2; + u32 fs_proc_flow[3]; + u32 fs_disp_flow[2]; + u32 skip; + u32 disp_alt_conf; + u32 disp_gen; + u32 disp_alt[4]; + u32 snoop; + u32 mem_rst; + u32 pm; + u32 gpr; + u32 reserved0[26]; + u32 ch_db_mode_sel[2]; + u32 reserved1[16]; + u32 alt_ch_db_mode_sel[2]; + u32 reserved2[2]; + u32 ch_trb_mode_sel[2]; +}; + +struct ipu_idmac { + u32 conf; + u32 ch_en[2]; + u32 sep_alpha; + u32 alt_sep_alpha; + u32 ch_pri[2]; + u32 wm_en[2]; + u32 lock_en[2]; + u32 sub_addr[5]; + u32 bndm_en[2]; + u32 sc_cord[2]; + u32 reserved[45]; + u32 ch_busy[2]; +}; + +struct ipu_com_async { + u32 com_conf_async; + u32 graph_wind_ctrl_async; + u32 fg_pos_async; + u32 cur_pos_async; + u32 cur_map_async; + u32 gamma_c_async[8]; + u32 gamma_s_async[4]; + u32 dp_csca_async[4]; + u32 dp_csc_async[2]; +}; + +struct ipu_dp { + u32 com_conf_sync; + u32 graph_wind_ctrl_sync; + u32 fg_pos_sync; + u32 cur_pos_sync; + u32 cur_map_sync; + u32 gamma_c_sync[8]; + u32 gamma_s_sync[4]; + u32 csca_sync[4]; + u32 csc_sync[2]; + u32 cur_pos_alt; + struct ipu_com_async async[2]; +}; + +struct ipu_di { + u32 general; + u32 bs_clkgen0; + u32 bs_clkgen1; + u32 sw_gen0[9]; + u32 sw_gen1[9]; + u32 sync_as; + u32 dw_gen[12]; + u32 dw_set[48]; + u32 stp_rep[4]; + u32 stp_rep9; + u32 ser_conf; + u32 ssc; + u32 pol; + u32 aw0; + u32 aw1; + u32 scr_conf; + u32 stat; +}; + +struct ipu_stat { + u32 int_stat[15]; + u32 cur_buf[2]; + u32 alt_cur_buf_0; + u32 alt_cur_buf_1; + u32 srm_stat; + u32 proc_task_stat; + u32 disp_task_stat; + u32 triple_cur_buf[4]; + u32 ch_buf0_rdy[2]; + u32 ch_buf1_rdy[2]; + u32 alt_ch_buf0_rdy[2]; + u32 alt_ch_buf1_rdy[2]; + u32 ch_buf2_rdy[2]; +}; + +struct ipu_dc_ch { + u32 wr_ch_conf; + u32 wr_ch_addr; + u32 rl[5]; +}; + +struct ipu_dc { + struct ipu_dc_ch dc_ch0_1_2[3]; + u32 cmd_ch_conf_3; + u32 cmd_ch_conf_4; + struct ipu_dc_ch dc_ch5_6[2]; + struct ipu_dc_ch dc_ch8; + u32 rl6_ch_8; + struct ipu_dc_ch dc_ch9; + u32 rl6_ch_9; + u32 gen; + u32 disp_conf1[4]; + u32 disp_conf2[4]; + u32 di0_conf[2]; + u32 di1_conf[2]; + u32 dc_map_ptr[15]; + u32 dc_map_val[12]; + u32 udge[16]; + u32 lla[2]; + u32 r_lla[2]; + u32 wr_ch_addr_5_alt; + u32 stat; +}; + +struct ipu_dmfc { + u32 rd_chan; + u32 wr_chan; + u32 wr_chan_def; + u32 dp_chan; + u32 dp_chan_def; + u32 general[2]; + u32 ic_ctrl; + u32 wr_chan_alt; + u32 wr_chan_def_alt; + u32 general1_alt; + u32 stat; +}; + +#define IPU_CM_REG ((struct ipu_cm *)(IPU_CTRL_BASE_ADDR + \ + IPU_CM_REG_BASE)) +#define IPU_CONF (&IPU_CM_REG->conf) +#define IPU_SRM_PRI1 (&IPU_CM_REG->srm_pri1) +#define IPU_SRM_PRI2 (&IPU_CM_REG->srm_pri2) +#define IPU_FS_PROC_FLOW1 (&IPU_CM_REG->fs_proc_flow[0]) +#define IPU_FS_PROC_FLOW2 (&IPU_CM_REG->fs_proc_flow[1]) +#define IPU_FS_PROC_FLOW3 (&IPU_CM_REG->fs_proc_flow[2]) +#define IPU_FS_DISP_FLOW1 (&IPU_CM_REG->fs_disp_flow[0]) +#define IPU_DISP_GEN (&IPU_CM_REG->disp_gen) +#define IPU_MEM_RST (&IPU_CM_REG->mem_rst) +#define IPU_GPR (&IPU_CM_REG->gpr) +#define IPU_CHA_DB_MODE_SEL(ch) (&IPU_CM_REG->ch_db_mode_sel[ch / 32]) + +#define IPU_STAT ((struct ipu_stat *)(IPU_CTRL_BASE_ADDR + \ + IPU_STAT_REG_BASE)) +#define IPU_CHA_CUR_BUF(ch) (&IPU_STAT->cur_buf[ch / 32]) +#define IPU_CHA_BUF0_RDY(ch) (&IPU_STAT->ch_buf0_rdy[ch / 32]) +#define IPU_CHA_BUF1_RDY(ch) (&IPU_STAT->ch_buf1_rdy[ch / 32]) + +#define IPU_INT_CTRL(n) (&IPU_CM_REG->int_ctrl[(n) - 1]) + +#define IDMAC_REG ((struct ipu_idmac *)(IPU_CTRL_BASE_ADDR + \ + IPU_IDMAC_REG_BASE)) +#define IDMAC_CONF (&IDMAC_REG->conf) +#define IDMAC_CHA_EN(ch) (&IDMAC_REG->ch_en[ch / 32]) +#define IDMAC_CHA_PRI(ch) (&IDMAC_REG->ch_pri[ch / 32]) + +#define DI_REG(di) ((struct ipu_di *)(IPU_CTRL_BASE_ADDR + \ + ((di == 1) ? IPU_DI1_REG_BASE : \ + IPU_DI0_REG_BASE))) +#define DI_GENERAL(di) (&DI_REG(di)->general) +#define DI_BS_CLKGEN0(di) (&DI_REG(di)->bs_clkgen0) +#define DI_BS_CLKGEN1(di) (&DI_REG(di)->bs_clkgen1) + +#define DI_SW_GEN0(di, gen) (&DI_REG(di)->sw_gen0[gen - 1]) +#define DI_SW_GEN1(di, gen) (&DI_REG(di)->sw_gen1[gen - 1]) +#define DI_STP_REP(di, gen) (&DI_REG(di)->stp_rep[(gen - 1) / 2]) +#define DI_SYNC_AS_GEN(di) (&DI_REG(di)->sync_as) +#define DI_DW_GEN(di, gen) (&DI_REG(di)->dw_gen[gen]) +#define DI_DW_SET(di, gen, set) (&DI_REG(di)->dw_set[gen + 12 * set]) +#define DI_POL(di) (&DI_REG(di)->pol) +#define DI_SCR_CONF(di) (&DI_REG(di)->scr_conf) + +#define DMFC_REG ((struct ipu_dmfc *)(IPU_CTRL_BASE_ADDR + \ + IPU_DMFC_REG_BASE)) +#define DMFC_WR_CHAN (&DMFC_REG->wr_chan) +#define DMFC_WR_CHAN_DEF (&DMFC_REG->wr_chan_def) +#define DMFC_DP_CHAN (&DMFC_REG->dp_chan) +#define DMFC_DP_CHAN_DEF (&DMFC_REG->dp_chan_def) +#define DMFC_GENERAL1 (&DMFC_REG->general[0]) +#define DMFC_IC_CTRL (&DMFC_REG->ic_ctrl) + + +#define DC_REG ((struct ipu_dc *)(IPU_CTRL_BASE_ADDR + \ + IPU_DC_REG_BASE)) +#define DC_MAP_CONF_PTR(n) (&DC_REG->dc_map_ptr[n / 2]) +#define DC_MAP_CONF_VAL(n) (&DC_REG->dc_map_val[n / 2]) + + +static inline struct ipu_dc_ch *dc_ch_offset(int ch) +{ + switch (ch) { + case 0: + case 1: + case 2: + return &DC_REG->dc_ch0_1_2[ch]; + case 5: + case 6: + return &DC_REG->dc_ch5_6[ch - 5]; + case 8: + return &DC_REG->dc_ch8; + case 9: + return &DC_REG->dc_ch9; + default: + printf("%s: invalid channel %d\n", __func__, ch); + return NULL; + } + +} + +#define DC_RL_CH(ch, evt) (&dc_ch_offset(ch)->rl[evt / 2]) + +#define DC_WR_CH_CONF(ch) (&dc_ch_offset(ch)->wr_ch_conf) +#define DC_WR_CH_ADDR(ch) (&dc_ch_offset(ch)->wr_ch_addr) + +#define DC_WR_CH_CONF_1 DC_WR_CH_CONF(1) +#define DC_WR_CH_CONF_5 DC_WR_CH_CONF(5) + +#define DC_GEN (&DC_REG->gen) +#define DC_DISP_CONF2(disp) (&DC_REG->disp_conf2[disp]) +#define DC_STAT (&DC_REG->stat) + +#define DP_SYNC 0 +#define DP_ASYNC0 0x60 +#define DP_ASYNC1 0xBC + +#define DP_REG ((struct ipu_dp *)(IPU_CTRL_BASE_ADDR + \ + IPU_DP_REG_BASE)) +#define DP_COM_CONF(flow) (&DP_REG->com_conf_sync) +#define DP_GRAPH_WIND_CTRL(flow) (&DP_REG->graph_wind_ctrl_sync) +#define DP_CSC_A_0(flow) (&DP_REG->csca_sync[0]) +#define DP_CSC_A_1(flow) (&DP_REG->csca_sync[1]) +#define DP_CSC_A_2(flow) (&DP_REG->csca_sync[2]) +#define DP_CSC_A_3(flow) (&DP_REG->csca_sync[3]) + +#define DP_CSC_0(flow) (&DP_REG->csc_sync[0]) +#define DP_CSC_1(flow) (&DP_REG->csc_sync[1]) + +/* DC template opcodes */ +#define WROD(lf) (0x18 | (lf << 1)) + +#endif diff --git a/u-boot/drivers/video/mb862xx.c b/u-boot/drivers/video/mb862xx.c new file mode 100644 index 0000000..edf34aa --- /dev/null +++ b/u-boot/drivers/video/mb862xx.c @@ -0,0 +1,478 @@ +/* + * (C) Copyright 2007 + * DENX Software Engineering, Anatolij Gustschin, agust@denx.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * mb862xx.c - Graphic interface for Fujitsu CoralP/Lime + * PCI and video mode code was derived from smiLynxEM driver. + */ + +#include <common.h> + +#include <asm/io.h> +#include <pci.h> +#include <video_fb.h> +#include "videomodes.h" +#include <mb862xx.h> + +#if defined(CONFIG_POST) +#include <post.h> +#endif + +/* + * Graphic Device + */ +GraphicDevice mb862xx; + +/* + * 32MB external RAM - 256K Chip MMIO = 0x1FC0000 ; + */ +#define VIDEO_MEM_SIZE 0x01FC0000 + +#if defined(CONFIG_PCI) +#if defined(CONFIG_VIDEO_CORALP) + +static struct pci_device_id supported[] = { + { PCI_VENDOR_ID_FUJITSU, PCI_DEVICE_ID_CORAL_P }, + { PCI_VENDOR_ID_FUJITSU, PCI_DEVICE_ID_CORAL_PA }, + { } +}; + +/* Internal clock frequency divider table, index is mode number */ +unsigned int fr_div[] = { 0x00000f00, 0x00000900, 0x00000500 }; +#endif +#endif + +#if defined(CONFIG_VIDEO_CORALP) +#define rd_io in32r +#define wr_io out32r +#else +#define rd_io(addr) in_be32((volatile unsigned *)(addr)) +#define wr_io(addr, val) out_be32((volatile unsigned *)(addr), (val)) +#endif + +#define HOST_RD_REG(off) rd_io((dev->frameAdrs + GC_HOST_BASE + (off))) +#define HOST_WR_REG(off, val) wr_io((dev->frameAdrs + GC_HOST_BASE + (off)), \ + (val)) +#define DISP_RD_REG(off) rd_io((dev->frameAdrs + GC_DISP_BASE + (off))) +#define DISP_WR_REG(off, val) wr_io((dev->frameAdrs + GC_DISP_BASE + (off)), \ + (val)) +#define DE_RD_REG(off) rd_io((dev->dprBase + (off))) +#define DE_WR_REG(off, val) wr_io((dev->dprBase + (off)), (val)) + +#if defined(CONFIG_VIDEO_CORALP) +#define DE_WR_FIFO(val) wr_io((dev->dprBase + (GC_GEO_FIFO)), (val)) +#else +#define DE_WR_FIFO(val) wr_io((dev->dprBase + (GC_FIFO)), (val)) +#endif + +#define L0PAL_WR_REG(idx, val) wr_io((dev->frameAdrs + \ + (GC_DISP_BASE | GC_L0PAL0) + \ + ((idx) << 2)), (val)) + +#if defined(CONFIG_VIDEO_MB862xx_ACCEL) +static void gdc_sw_reset (void) +{ + GraphicDevice *dev = &mb862xx; + + HOST_WR_REG (GC_SRST, 0x1); + udelay (500); + video_hw_init (); +} + + +static void de_wait (void) +{ + GraphicDevice *dev = &mb862xx; + int lc = 0x10000; + + /* + * Sync with software writes to framebuffer, + * try to reset if engine locked + */ + while (DE_RD_REG (GC_CTR) & 0x00000131) + if (lc-- < 0) { + gdc_sw_reset (); + puts ("gdc reset done after drawing engine lock.\n"); + break; + } +} + +static void de_wait_slots (int slots) +{ + GraphicDevice *dev = &mb862xx; + int lc = 0x10000; + + /* Wait for free fifo slots */ + while (DE_RD_REG (GC_IFCNT) < slots) + if (lc-- < 0) { + gdc_sw_reset (); + puts ("gdc reset done after drawing engine lock.\n"); + break; + } +} +#endif + +#if !defined(CONFIG_VIDEO_CORALP) +static void board_disp_init (void) +{ + GraphicDevice *dev = &mb862xx; + const gdc_regs *regs = board_get_regs (); + + while (regs->index) { + DISP_WR_REG (regs->index, regs->value); + regs++; + } +} +#endif + +/* + * Init drawing engine if accel enabled. + * Also clears visible framebuffer. + */ +static void de_init (void) +{ + GraphicDevice *dev = &mb862xx; +#if defined(CONFIG_VIDEO_MB862xx_ACCEL) + int cf = (dev->gdfBytesPP == 1) ? 0x0000 : 0x8000; + + dev->dprBase = dev->frameAdrs + GC_DRAW_BASE; + + /* Setup mode and fbbase, xres, fg, bg */ + de_wait_slots (2); + DE_WR_FIFO (0xf1010108); + DE_WR_FIFO (cf | 0x0300); + DE_WR_REG (GC_FBR, 0x0); + DE_WR_REG (GC_XRES, dev->winSizeX); + DE_WR_REG (GC_FC, 0x0); + DE_WR_REG (GC_BC, 0x0); + /* Reset clipping */ + DE_WR_REG (GC_CXMIN, 0x0); + DE_WR_REG (GC_CXMAX, dev->winSizeX); + DE_WR_REG (GC_CYMIN, 0x0); + DE_WR_REG (GC_CYMAX, dev->winSizeY); + + /* Clear framebuffer using drawing engine */ + de_wait_slots (3); + DE_WR_FIFO (0x09410000); + DE_WR_FIFO (0x00000000); + DE_WR_FIFO (dev->winSizeY << 16 | dev->winSizeX); + /* sync with SW access to framebuffer */ + de_wait (); +#else + unsigned int i, *p; + + i = dev->winSizeX * dev->winSizeY; + p = (unsigned int *)dev->frameAdrs; + while (i--) + *p++ = 0; +#endif +} + +#if defined(CONFIG_VIDEO_CORALP) +unsigned int pci_video_init (void) +{ + GraphicDevice *dev = &mb862xx; + pci_dev_t devbusfn; + + if ((devbusfn = pci_find_devices (supported, 0)) < 0) { + puts ("PCI video controller not found!\n"); + return 0; + } + + /* PCI setup */ + pci_write_config_dword (devbusfn, PCI_COMMAND, + (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)); + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &dev->frameAdrs); + dev->frameAdrs = pci_mem_to_phys (devbusfn, dev->frameAdrs); + + if (dev->frameAdrs == 0) { + puts ("PCI config: failed to get base address\n"); + return 0; + } + + dev->pciBase = dev->frameAdrs; + + /* Setup clocks and memory mode for Coral-P Eval. Board */ + HOST_WR_REG (GC_CCF, 0x00090000); + udelay (200); + HOST_WR_REG (GC_MMR, 0x11d7fa13); + udelay (100); + return dev->frameAdrs; +} + +unsigned int card_init (void) +{ + GraphicDevice *dev = &mb862xx; + unsigned int cf, videomode, div = 0; + unsigned long t1, hsync, vsync; + char *penv; + int tmp, i, bpp; + struct ctfb_res_modes *res_mode; + struct ctfb_res_modes var_mode; + + memset (dev, 0, sizeof (GraphicDevice)); + + if (!pci_video_init ()) + return 0; + + puts ("CoralP\n"); + + tmp = 0; + videomode = 0x310; + /* get video mode via environment */ + if ((penv = getenv ("videomode")) != NULL) { + /* decide if it is a string */ + if (penv[0] <= '9') { + videomode = (int) simple_strtoul (penv, NULL, 16); + tmp = 1; + } + } else { + tmp = 1; + } + + if (tmp) { + /* parameter are vesa modes, search params */ + for (i = 0; i < VESA_MODES_COUNT; i++) { + if (vesa_modes[i].vesanr == videomode) + break; + } + if (i == VESA_MODES_COUNT) { + printf ("\tno VESA Mode found, fallback to mode 0x%x\n", + videomode); + i = 0; + } + res_mode = (struct ctfb_res_modes *) + &res_mode_init[vesa_modes[i].resindex]; + if (vesa_modes[i].resindex > 2) { + puts ("\tUnsupported resolution, using default\n"); + bpp = vesa_modes[1].bits_per_pixel; + div = fr_div[1]; + } + bpp = vesa_modes[i].bits_per_pixel; + div = fr_div[vesa_modes[i].resindex]; + } else { + res_mode = (struct ctfb_res_modes *) &var_mode; + bpp = video_get_params (res_mode, penv); + } + + /* calculate hsync and vsync freq (info only) */ + t1 = (res_mode->left_margin + res_mode->xres + + res_mode->right_margin + res_mode->hsync_len) / 8; + t1 *= 8; + t1 *= res_mode->pixclock; + t1 /= 1000; + hsync = 1000000000L / t1; + t1 *= (res_mode->upper_margin + res_mode->yres + + res_mode->lower_margin + res_mode->vsync_len); + t1 /= 1000; + vsync = 1000000000L / t1; + + /* fill in Graphic device struct */ + sprintf (dev->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres, + res_mode->yres, bpp, (hsync / 1000), (vsync / 1000)); + printf ("\t%s\n", dev->modeIdent); + dev->winSizeX = res_mode->xres; + dev->winSizeY = res_mode->yres; + dev->memSize = VIDEO_MEM_SIZE; + + switch (bpp) { + case 8: + dev->gdfIndex = GDF__8BIT_INDEX; + dev->gdfBytesPP = 1; + break; + case 15: + case 16: + dev->gdfIndex = GDF_15BIT_555RGB; + dev->gdfBytesPP = 2; + break; + default: + printf ("\t%d bpp configured, but only 8,15 and 16 supported\n", + bpp); + puts ("\tfallback to 15bpp\n"); + dev->gdfIndex = GDF_15BIT_555RGB; + dev->gdfBytesPP = 2; + } + + /* Setup dot clock (internal pll, division rate) */ + DISP_WR_REG (GC_DCM1, div); + /* L0 init */ + cf = (dev->gdfBytesPP == 1) ? 0x00000000 : 0x80000000; + DISP_WR_REG (GC_L0M, ((dev->winSizeX * dev->gdfBytesPP) / 64) << 16 | + (dev->winSizeY - 1) | cf); + DISP_WR_REG (GC_L0OA0, 0x0); + DISP_WR_REG (GC_L0DA0, 0x0); + DISP_WR_REG (GC_L0DY_L0DX, 0x0); + DISP_WR_REG (GC_L0EM, 0x0); + DISP_WR_REG (GC_L0WY_L0WX, 0x0); + DISP_WR_REG (GC_L0WH_L0WW, (dev->winSizeY - 1) << 16 | dev->winSizeX); + + /* Display timing init */ + DISP_WR_REG (GC_HTP_A, (dev->winSizeX + + res_mode->left_margin + + res_mode->right_margin + + res_mode->hsync_len - 1) << 16); + DISP_WR_REG (GC_HDB_HDP_A, (dev->winSizeX - 1) << 16 | + (dev->winSizeX - 1)); + DISP_WR_REG (GC_VSW_HSW_HSP_A, (res_mode->vsync_len - 1) << 24 | + (res_mode->hsync_len - 1) << 16 | + (dev->winSizeX + + res_mode->right_margin - 1)); + DISP_WR_REG (GC_VTR_A, (dev->winSizeY + res_mode->lower_margin + + res_mode->upper_margin + + res_mode->vsync_len - 1) << 16); + DISP_WR_REG (GC_VDP_VSP_A, (dev->winSizeY-1) << 16 | + (dev->winSizeY + + res_mode->lower_margin - 1)); + DISP_WR_REG (GC_WY_WX, 0x0); + DISP_WR_REG (GC_WH_WW, dev->winSizeY << 16 | dev->winSizeX); + /* Display enable, L0 layer */ + DISP_WR_REG (GC_DCM1, 0x80010000 | div); + + return dev->frameAdrs; +} +#endif + + +#if !defined(CONFIG_VIDEO_CORALP) +int mb862xx_probe(unsigned int addr) +{ + GraphicDevice *dev = &mb862xx; + unsigned int reg; + + dev->frameAdrs = addr; + dev->dprBase = dev->frameAdrs + GC_DRAW_BASE; + + /* Try to access GDC ID/Revision registers */ + reg = HOST_RD_REG (GC_CID); + reg = HOST_RD_REG (GC_CID); + if (reg == 0x303) { + reg = DE_RD_REG(GC_REV); + reg = DE_RD_REG(GC_REV); + if ((reg & ~0xff) == 0x20050100) + return MB862XX_TYPE_LIME; + } + + return 0; +} +#endif + +void *video_hw_init (void) +{ + GraphicDevice *dev = &mb862xx; + + puts ("Video: Fujitsu "); + + memset (dev, 0, sizeof (GraphicDevice)); + +#if defined(CONFIG_VIDEO_CORALP) + if (card_init () == 0) + return NULL; +#else + /* + * Preliminary init of the onboard graphic controller, + * retrieve base address + */ + if ((dev->frameAdrs = board_video_init ()) == 0) { + puts ("Controller not found!\n"); + return NULL; + } else { + puts ("Lime\n"); + + /* Set Change of Clock Frequency Register */ + HOST_WR_REG (GC_CCF, CONFIG_SYS_MB862xx_CCF); + /* Delay required */ + udelay(300); + /* Set Memory I/F Mode Register) */ + HOST_WR_REG (GC_MMR, CONFIG_SYS_MB862xx_MMR); + } +#endif + + de_init (); + +#if !defined(CONFIG_VIDEO_CORALP) + board_disp_init (); +#endif + +#if (defined(CONFIG_LWMON5) || \ + defined(CONFIG_SOCRATES)) && !(CONFIG_POST & CONFIG_SYS_POST_SYSMON) + /* Lamp on */ + board_backlight_switch (1); +#endif + + return dev; +} + +/* + * Set a RGB color in the LUT + */ +void video_set_lut (unsigned int index, unsigned char r, + unsigned char g, unsigned char b) +{ + GraphicDevice *dev = &mb862xx; + + L0PAL_WR_REG (index, (r << 16) | (g << 8) | (b)); +} + +#if defined(CONFIG_VIDEO_MB862xx_ACCEL) +/* + * Drawing engine Fill and BitBlt screen region + */ +void video_hw_rectfill (unsigned int bpp, unsigned int dst_x, + unsigned int dst_y, unsigned int dim_x, + unsigned int dim_y, unsigned int color) +{ + GraphicDevice *dev = &mb862xx; + + de_wait_slots (3); + DE_WR_REG (GC_FC, color); + DE_WR_FIFO (0x09410000); + DE_WR_FIFO ((dst_y << 16) | dst_x); + DE_WR_FIFO ((dim_y << 16) | dim_x); + de_wait (); +} + +void video_hw_bitblt (unsigned int bpp, unsigned int src_x, + unsigned int src_y, unsigned int dst_x, + unsigned int dst_y, unsigned int width, + unsigned int height) +{ + GraphicDevice *dev = &mb862xx; + unsigned int ctrl = 0x0d000000L; + + if (src_x >= dst_x && src_y >= dst_y) + ctrl |= 0x00440000L; + else if (src_x >= dst_x && src_y <= dst_y) + ctrl |= 0x00460000L; + else if (src_x <= dst_x && src_y >= dst_y) + ctrl |= 0x00450000L; + else + ctrl |= 0x00470000L; + + de_wait_slots (4); + DE_WR_FIFO (ctrl); + DE_WR_FIFO ((src_y << 16) | src_x); + DE_WR_FIFO ((dst_y << 16) | dst_x); + DE_WR_FIFO ((height << 16) | width); + de_wait (); /* sync */ +} +#endif diff --git a/u-boot/drivers/video/mb86r0xgdc.c b/u-boot/drivers/video/mb86r0xgdc.c new file mode 100644 index 0000000..3bdc1db --- /dev/null +++ b/u-boot/drivers/video/mb86r0xgdc.c @@ -0,0 +1,186 @@ +/* + * (C) Copyright 2010 + * Matthias Weisser <weisserm@arcor.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * mb86r0xgdc.c - Graphic interface for Fujitsu MB86R0x integrated graphic + * controller. + */ + +#include <common.h> + +#include <malloc.h> +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <video_fb.h> +#include "videomodes.h" + +/* + * 4MB (at the end of system RAM) + */ +#define VIDEO_MEM_SIZE 0x400000 + +#define FB_SYNC_CLK_INV (1<<16) /* pixel clock inverted */ + +/* + * Graphic Device + */ +static GraphicDevice mb86r0x; + +static void dsp_init(struct mb86r0x_gdc_dsp *dsp, char *modestr, + u32 *videomem) +{ + struct ctfb_res_modes var_mode; + u32 dcm1, dcm2, dcm3; + u16 htp, hdp, hdb, hsp, vtr, vsp, vdp; + u8 hsw, vsw; + u32 l2m, l2em, l2oa0, l2da0, l2oa1, l2da1; + u16 l2dx, l2dy, l2wx, l2wy, l2ww, l2wh; + unsigned long div; + int bpp; + u32 i; + + bpp = video_get_params(&var_mode, modestr); + + if (bpp == 0) { + var_mode.xres = 640; + var_mode.yres = 480; + var_mode.pixclock = 39721; /* 25MHz */ + var_mode.left_margin = 48; + var_mode.right_margin = 16; + var_mode.upper_margin = 33; + var_mode.lower_margin = 10; + var_mode.hsync_len = 96; + var_mode.vsync_len = 2; + var_mode.sync = 0; + var_mode.vmode = 0; + bpp = 15; + } + + /* Fill memory with white */ + for (i = 0; i < var_mode.xres * var_mode.yres / 2; i++) + *videomem++ = 0xFFFFFFFF; + + mb86r0x.winSizeX = var_mode.xres; + mb86r0x.winSizeY = var_mode.yres; + + /* LCD base clock is ~ 660MHZ. We do calculations in kHz */ + div = 660000 / (1000000000L / var_mode.pixclock); + if (div > 64) + div = 64; + if (0 == div) + div = 1; + + dcm1 = (div - 1) << 8; + dcm2 = 0x00000000; + if (var_mode.sync & FB_SYNC_CLK_INV) + dcm3 = 0x00000100; + else + dcm3 = 0x00000000; + + htp = var_mode.left_margin + var_mode.xres + + var_mode.hsync_len + var_mode.right_margin; + hdp = var_mode.xres; + hdb = var_mode.xres; + hsp = var_mode.xres + var_mode.right_margin; + hsw = var_mode.hsync_len; + + vsw = var_mode.vsync_len; + vtr = var_mode.upper_margin + var_mode.yres + + var_mode.vsync_len + var_mode.lower_margin; + vsp = var_mode.yres + var_mode.lower_margin; + vdp = var_mode.yres; + + l2m = ((var_mode.yres - 1) << (0)) | + (((var_mode.xres * 2) / 64) << (16)) | + ((1) << (31)); + + l2em = (1 << 0) | (1 << 1); + + l2oa0 = mb86r0x.frameAdrs; + l2da0 = mb86r0x.frameAdrs; + l2oa1 = mb86r0x.frameAdrs; + l2da1 = mb86r0x.frameAdrs; + l2dx = 0; + l2dy = 0; + l2wx = 0; + l2wy = 0; + l2ww = var_mode.xres; + l2wh = var_mode.yres - 1; + + writel(dcm1, &dsp->dcm1); + writel(dcm2, &dsp->dcm2); + writel(dcm3, &dsp->dcm3); + + writew(htp, &dsp->htp); + writew(hdp, &dsp->hdp); + writew(hdb, &dsp->hdb); + writew(hsp, &dsp->hsp); + writeb(hsw, &dsp->hsw); + + writeb(vsw, &dsp->vsw); + writew(vtr, &dsp->vtr); + writew(vsp, &dsp->vsp); + writew(vdp, &dsp->vdp); + + writel(l2m, &dsp->l2m); + writel(l2em, &dsp->l2em); + writel(l2oa0, &dsp->l2oa0); + writel(l2da0, &dsp->l2da0); + writel(l2oa1, &dsp->l2oa1); + writel(l2da1, &dsp->l2da1); + writew(l2dx, &dsp->l2dx); + writew(l2dy, &dsp->l2dy); + writew(l2wx, &dsp->l2wx); + writew(l2wy, &dsp->l2wy); + writew(l2ww, &dsp->l2ww); + writew(l2wh, &dsp->l2wh); + + writel(dcm1 | (1 << 18) | (1 << 31), &dsp->dcm1); +} + +void *video_hw_init(void) +{ + struct mb86r0x_gdc *gdc = (struct mb86r0x_gdc *) MB86R0x_GDC_BASE; + GraphicDevice *pGD = &mb86r0x; + char *s; + u32 *vid; + + memset(pGD, 0, sizeof(GraphicDevice)); + + pGD->gdfIndex = GDF_15BIT_555RGB; + pGD->gdfBytesPP = 2; + pGD->memSize = VIDEO_MEM_SIZE; + pGD->frameAdrs = PHYS_SDRAM + PHYS_SDRAM_SIZE - VIDEO_MEM_SIZE; + + vid = (u32 *)pGD->frameAdrs; + + s = getenv("videomode"); + if (s != NULL) + dsp_init(&gdc->dsp0, s, vid); + + s = getenv("videomode1"); + if (s != NULL) + dsp_init(&gdc->dsp1, s, vid); + + return pGD; +} diff --git a/u-boot/drivers/video/mx3fb.c b/u-boot/drivers/video/mx3fb.c new file mode 100644 index 0000000..6dd952c --- /dev/null +++ b/u-boot/drivers/video/mx3fb.c @@ -0,0 +1,857 @@ +/* + * Copyright (C) 2009 + * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ +#include <common.h> +#include <lcd.h> +#include <asm/arch/mx31.h> +#include <asm/arch/mx31-regs.h> +#include <asm/errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +void *lcd_base; /* Start of framebuffer memory */ +void *lcd_console_address; /* Start of console buffer */ + +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; + +short console_col; +short console_row; + +void lcd_initcolregs(void) +{ +} + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ +} + +void lcd_disable(void) +{ +} + +void lcd_panel_disable(void) +{ +} + +#define msleep(a) udelay(a * 1000) + +#if defined(CONFIG_DISPLAY_VBEST_VGG322403) +#define XRES 320 +#define YRES 240 +#define PANEL_TYPE IPU_PANEL_TFT +#define PIXEL_CLK 156000 +#define PIXEL_FMT IPU_PIX_FMT_RGB666 +#define H_START_WIDTH 20 /* left_margin */ +#define H_SYNC_WIDTH 30 /* hsync_len */ +#define H_END_WIDTH (38 + 30) /* right_margin + hsync_len */ +#define V_START_WIDTH 7 /* upper_margin */ +#define V_SYNC_WIDTH 3 /* vsync_len */ +#define V_END_WIDTH (26 + 3) /* lower_margin + vsync_len */ +#define SIG_POL (DI_D3_DRDY_SHARP_POL | DI_D3_CLK_POL) +#define IF_CONF 0 +#define IF_CLK_DIV 0x175 +#elif defined(CONFIG_DISPLAY_COM57H5M10XRC) +#define XRES 640 +#define YRES 480 +#define PANEL_TYPE IPU_PANEL_TFT +#define PIXEL_CLK 40000 +#define PIXEL_FMT IPU_PIX_FMT_RGB666 +#define H_START_WIDTH 120 /* left_margin */ +#define H_SYNC_WIDTH 30 /* hsync_len */ +#define H_END_WIDTH (10 + 30) /* right_margin + hsync_len */ +#define V_START_WIDTH 35 /* upper_margin */ +#define V_SYNC_WIDTH 3 /* vsync_len */ +#define V_END_WIDTH (7 + 3) /* lower_margin + vsync_len */ +#define SIG_POL (DI_D3_DRDY_SHARP_POL | DI_D3_CLK_POL) +#define IF_CONF 0 +#define IF_CLK_DIV 0x55 +#else +#define XRES 240 +#define YRES 320 +#define PANEL_TYPE IPU_PANEL_TFT +#define PIXEL_CLK 185925 +#define PIXEL_FMT IPU_PIX_FMT_RGB666 +#define H_START_WIDTH 9 /* left_margin */ +#define H_SYNC_WIDTH 1 /* hsync_len */ +#define H_END_WIDTH (16 + 1) /* right_margin + hsync_len */ +#define V_START_WIDTH 7 /* upper_margin */ +#define V_SYNC_WIDTH 1 /* vsync_len */ +#define V_END_WIDTH (9 + 1) /* lower_margin + vsync_len */ +#define SIG_POL (DI_D3_DRDY_SHARP_POL | DI_D3_CLK_POL) +#define IF_CONF 0 +#define IF_CLK_DIV 0x175 +#endif + +#define LCD_COLOR_IPU LCD_COLOR16 + +static ushort colormap[256]; + +vidinfo_t panel_info = { + .vl_col = XRES, + .vl_row = YRES, + .vl_bpix = LCD_COLOR_IPU, + .cmap = colormap, +}; + +#define BIT_PER_PIXEL NBITS(LCD_COLOR_IPU) + +/* IPU DMA Controller channel definitions. */ +enum ipu_channel { + IDMAC_IC_0 = 0, /* IC (encoding task) to memory */ + IDMAC_IC_1 = 1, /* IC (viewfinder task) to memory */ + IDMAC_ADC_0 = 1, + IDMAC_IC_2 = 2, + IDMAC_ADC_1 = 2, + IDMAC_IC_3 = 3, + IDMAC_IC_4 = 4, + IDMAC_IC_5 = 5, + IDMAC_IC_6 = 6, + IDMAC_IC_7 = 7, /* IC (sensor data) to memory */ + IDMAC_IC_8 = 8, + IDMAC_IC_9 = 9, + IDMAC_IC_10 = 10, + IDMAC_IC_11 = 11, + IDMAC_IC_12 = 12, + IDMAC_IC_13 = 13, + IDMAC_SDC_0 = 14, /* Background synchronous display data */ + IDMAC_SDC_1 = 15, /* Foreground data (overlay) */ + IDMAC_SDC_2 = 16, + IDMAC_SDC_3 = 17, + IDMAC_ADC_2 = 18, + IDMAC_ADC_3 = 19, + IDMAC_ADC_4 = 20, + IDMAC_ADC_5 = 21, + IDMAC_ADC_6 = 22, + IDMAC_ADC_7 = 23, + IDMAC_PF_0 = 24, + IDMAC_PF_1 = 25, + IDMAC_PF_2 = 26, + IDMAC_PF_3 = 27, + IDMAC_PF_4 = 28, + IDMAC_PF_5 = 29, + IDMAC_PF_6 = 30, + IDMAC_PF_7 = 31, +}; + +/* More formats can be copied from the Linux driver if needed */ +enum pixel_fmt { + /* 2 bytes */ + IPU_PIX_FMT_RGB565, + IPU_PIX_FMT_RGB666, + IPU_PIX_FMT_BGR666, + /* 3 bytes */ + IPU_PIX_FMT_RGB24, +}; + +struct pixel_fmt_cfg { + u32 b0; + u32 b1; + u32 b2; + u32 acc; +}; + +static struct pixel_fmt_cfg fmt_cfg[] = { + [IPU_PIX_FMT_RGB24] = { + 0x1600AAAA, 0x00E05555, 0x00070000, 3, + }, + [IPU_PIX_FMT_RGB666] = { + 0x0005000F, 0x000B000F, 0x0011000F, 1, + }, + [IPU_PIX_FMT_BGR666] = { + 0x0011000F, 0x000B000F, 0x0005000F, 1, + }, + [IPU_PIX_FMT_RGB565] = { + 0x0004003F, 0x000A000F, 0x000F003F, 1, + } +}; + +enum ipu_panel { + IPU_PANEL_SHARP_TFT, + IPU_PANEL_TFT, +}; + +/* IPU Common registers */ +/* IPU_CONF and its bits already defined in mx31-regs.h */ +#define IPU_CHA_BUF0_RDY (0x04 + IPU_BASE) +#define IPU_CHA_BUF1_RDY (0x08 + IPU_BASE) +#define IPU_CHA_DB_MODE_SEL (0x0C + IPU_BASE) +#define IPU_CHA_CUR_BUF (0x10 + IPU_BASE) +#define IPU_FS_PROC_FLOW (0x14 + IPU_BASE) +#define IPU_FS_DISP_FLOW (0x18 + IPU_BASE) +#define IPU_TASKS_STAT (0x1C + IPU_BASE) +#define IPU_IMA_ADDR (0x20 + IPU_BASE) +#define IPU_IMA_DATA (0x24 + IPU_BASE) +#define IPU_INT_CTRL_1 (0x28 + IPU_BASE) +#define IPU_INT_CTRL_2 (0x2C + IPU_BASE) +#define IPU_INT_CTRL_3 (0x30 + IPU_BASE) +#define IPU_INT_CTRL_4 (0x34 + IPU_BASE) +#define IPU_INT_CTRL_5 (0x38 + IPU_BASE) +#define IPU_INT_STAT_1 (0x3C + IPU_BASE) +#define IPU_INT_STAT_2 (0x40 + IPU_BASE) +#define IPU_INT_STAT_3 (0x44 + IPU_BASE) +#define IPU_INT_STAT_4 (0x48 + IPU_BASE) +#define IPU_INT_STAT_5 (0x4C + IPU_BASE) +#define IPU_BRK_CTRL_1 (0x50 + IPU_BASE) +#define IPU_BRK_CTRL_2 (0x54 + IPU_BASE) +#define IPU_BRK_STAT (0x58 + IPU_BASE) +#define IPU_DIAGB_CTRL (0x5C + IPU_BASE) + +/* Image Converter Registers */ +#define IC_CONF (0x88 + IPU_BASE) +#define IC_PRP_ENC_RSC (0x8C + IPU_BASE) +#define IC_PRP_VF_RSC (0x90 + IPU_BASE) +#define IC_PP_RSC (0x94 + IPU_BASE) +#define IC_CMBP_1 (0x98 + IPU_BASE) +#define IC_CMBP_2 (0x9C + IPU_BASE) +#define PF_CONF (0xA0 + IPU_BASE) +#define IDMAC_CONF (0xA4 + IPU_BASE) +#define IDMAC_CHA_EN (0xA8 + IPU_BASE) +#define IDMAC_CHA_PRI (0xAC + IPU_BASE) +#define IDMAC_CHA_BUSY (0xB0 + IPU_BASE) + +/* Image Converter Register bits */ +#define IC_CONF_PRPENC_EN 0x00000001 +#define IC_CONF_PRPENC_CSC1 0x00000002 +#define IC_CONF_PRPENC_ROT_EN 0x00000004 +#define IC_CONF_PRPVF_EN 0x00000100 +#define IC_CONF_PRPVF_CSC1 0x00000200 +#define IC_CONF_PRPVF_CSC2 0x00000400 +#define IC_CONF_PRPVF_CMB 0x00000800 +#define IC_CONF_PRPVF_ROT_EN 0x00001000 +#define IC_CONF_PP_EN 0x00010000 +#define IC_CONF_PP_CSC1 0x00020000 +#define IC_CONF_PP_CSC2 0x00040000 +#define IC_CONF_PP_CMB 0x00080000 +#define IC_CONF_PP_ROT_EN 0x00100000 +#define IC_CONF_IC_GLB_LOC_A 0x10000000 +#define IC_CONF_KEY_COLOR_EN 0x20000000 +#define IC_CONF_RWS_EN 0x40000000 +#define IC_CONF_CSI_MEM_WR_EN 0x80000000 + +/* SDC Registers */ +#define SDC_COM_CONF (0xB4 + IPU_BASE) +#define SDC_GW_CTRL (0xB8 + IPU_BASE) +#define SDC_FG_POS (0xBC + IPU_BASE) +#define SDC_BG_POS (0xC0 + IPU_BASE) +#define SDC_CUR_POS (0xC4 + IPU_BASE) +#define SDC_PWM_CTRL (0xC8 + IPU_BASE) +#define SDC_CUR_MAP (0xCC + IPU_BASE) +#define SDC_HOR_CONF (0xD0 + IPU_BASE) +#define SDC_VER_CONF (0xD4 + IPU_BASE) +#define SDC_SHARP_CONF_1 (0xD8 + IPU_BASE) +#define SDC_SHARP_CONF_2 (0xDC + IPU_BASE) + +/* Register bits */ +#define SDC_COM_TFT_COLOR 0x00000001UL +#define SDC_COM_FG_EN 0x00000010UL +#define SDC_COM_GWSEL 0x00000020UL +#define SDC_COM_GLB_A 0x00000040UL +#define SDC_COM_KEY_COLOR_G 0x00000080UL +#define SDC_COM_BG_EN 0x00000200UL +#define SDC_COM_SHARP 0x00001000UL + +#define SDC_V_SYNC_WIDTH_L 0x00000001UL + +/* Display Interface registers */ +#define DI_DISP_IF_CONF (0x0124 + IPU_BASE) +#define DI_DISP_SIG_POL (0x0128 + IPU_BASE) +#define DI_SER_DISP1_CONF (0x012C + IPU_BASE) +#define DI_SER_DISP2_CONF (0x0130 + IPU_BASE) +#define DI_HSP_CLK_PER (0x0134 + IPU_BASE) +#define DI_DISP0_TIME_CONF_1 (0x0138 + IPU_BASE) +#define DI_DISP0_TIME_CONF_2 (0x013C + IPU_BASE) +#define DI_DISP0_TIME_CONF_3 (0x0140 + IPU_BASE) +#define DI_DISP1_TIME_CONF_1 (0x0144 + IPU_BASE) +#define DI_DISP1_TIME_CONF_2 (0x0148 + IPU_BASE) +#define DI_DISP1_TIME_CONF_3 (0x014C + IPU_BASE) +#define DI_DISP2_TIME_CONF_1 (0x0150 + IPU_BASE) +#define DI_DISP2_TIME_CONF_2 (0x0154 + IPU_BASE) +#define DI_DISP2_TIME_CONF_3 (0x0158 + IPU_BASE) +#define DI_DISP3_TIME_CONF (0x015C + IPU_BASE) +#define DI_DISP0_DB0_MAP (0x0160 + IPU_BASE) +#define DI_DISP0_DB1_MAP (0x0164 + IPU_BASE) +#define DI_DISP0_DB2_MAP (0x0168 + IPU_BASE) +#define DI_DISP0_CB0_MAP (0x016C + IPU_BASE) +#define DI_DISP0_CB1_MAP (0x0170 + IPU_BASE) +#define DI_DISP0_CB2_MAP (0x0174 + IPU_BASE) +#define DI_DISP1_DB0_MAP (0x0178 + IPU_BASE) +#define DI_DISP1_DB1_MAP (0x017C + IPU_BASE) +#define DI_DISP1_DB2_MAP (0x0180 + IPU_BASE) +#define DI_DISP1_CB0_MAP (0x0184 + IPU_BASE) +#define DI_DISP1_CB1_MAP (0x0188 + IPU_BASE) +#define DI_DISP1_CB2_MAP (0x018C + IPU_BASE) +#define DI_DISP2_DB0_MAP (0x0190 + IPU_BASE) +#define DI_DISP2_DB1_MAP (0x0194 + IPU_BASE) +#define DI_DISP2_DB2_MAP (0x0198 + IPU_BASE) +#define DI_DISP2_CB0_MAP (0x019C + IPU_BASE) +#define DI_DISP2_CB1_MAP (0x01A0 + IPU_BASE) +#define DI_DISP2_CB2_MAP (0x01A4 + IPU_BASE) +#define DI_DISP3_B0_MAP (0x01A8 + IPU_BASE) +#define DI_DISP3_B1_MAP (0x01AC + IPU_BASE) +#define DI_DISP3_B2_MAP (0x01B0 + IPU_BASE) +#define DI_DISP_ACC_CC (0x01B4 + IPU_BASE) +#define DI_DISP_LLA_CONF (0x01B8 + IPU_BASE) +#define DI_DISP_LLA_DATA (0x01BC + IPU_BASE) + +/* DI_DISP_SIG_POL bits */ +#define DI_D3_VSYNC_POL (1 << 28) +#define DI_D3_HSYNC_POL (1 << 27) +#define DI_D3_DRDY_SHARP_POL (1 << 26) +#define DI_D3_CLK_POL (1 << 25) +#define DI_D3_DATA_POL (1 << 24) + +/* DI_DISP_IF_CONF bits */ +#define DI_D3_CLK_IDLE (1 << 26) +#define DI_D3_CLK_SEL (1 << 25) +#define DI_D3_DATAMSK (1 << 24) + +#define IOMUX_PADNUM_MASK 0x1ff +#define IOMUX_GPIONUM_SHIFT 9 +#define IOMUX_GPIONUM_MASK (0xff << IOMUX_GPIONUM_SHIFT) + +#define IOMUX_PIN(gpionum, padnum) ((padnum) & IOMUX_PADNUM_MASK) + +#define IOMUX_MODE_L(pin, mode) IOMUX_MODE(((pin) + 0xc) ^ 3, mode) + +struct chan_param_mem_planar { + /* Word 0 */ + u32 xv:10; + u32 yv:10; + u32 xb:12; + + u32 yb:12; + u32 res1:2; + u32 nsb:1; + u32 lnpb:6; + u32 ubo_l:11; + + u32 ubo_h:15; + u32 vbo_l:17; + + u32 vbo_h:9; + u32 res2:3; + u32 fw:12; + u32 fh_l:8; + + u32 fh_h:4; + u32 res3:28; + + /* Word 1 */ + u32 eba0; + + u32 eba1; + + u32 bpp:3; + u32 sl:14; + u32 pfs:3; + u32 bam:3; + u32 res4:2; + u32 npb:6; + u32 res5:1; + + u32 sat:2; + u32 res6:30; +} __attribute__ ((packed)); + +struct chan_param_mem_interleaved { + /* Word 0 */ + u32 xv:10; + u32 yv:10; + u32 xb:12; + + u32 yb:12; + u32 sce:1; + u32 res1:1; + u32 nsb:1; + u32 lnpb:6; + u32 sx:10; + u32 sy_l:1; + + u32 sy_h:9; + u32 ns:10; + u32 sm:10; + u32 sdx_l:3; + + u32 sdx_h:2; + u32 sdy:5; + u32 sdrx:1; + u32 sdry:1; + u32 sdr1:1; + u32 res2:2; + u32 fw:12; + u32 fh_l:8; + + u32 fh_h:4; + u32 res3:28; + + /* Word 1 */ + u32 eba0; + + u32 eba1; + + u32 bpp:3; + u32 sl:14; + u32 pfs:3; + u32 bam:3; + u32 res4:2; + u32 npb:6; + u32 res5:1; + + u32 sat:2; + u32 scc:1; + u32 ofs0:5; + u32 ofs1:5; + u32 ofs2:5; + u32 ofs3:5; + u32 wid0:3; + u32 wid1:3; + u32 wid2:3; + + u32 wid3:3; + u32 dec_sel:1; + u32 res6:28; +} __attribute__ ((packed)); + +union chan_param_mem { + struct chan_param_mem_planar pp; + struct chan_param_mem_interleaved ip; +}; + +static inline u32 reg_read(unsigned long reg) +{ + return __REG(reg); +} + +static inline void reg_write(u32 value, unsigned long reg) +{ + __REG(reg) = value; +} + +/* + * sdc_init_panel() - initialize a synchronous LCD panel. + * @width: width of panel in pixels. + * @height: height of panel in pixels. + * @pixel_fmt: pixel format of buffer as FOURCC ASCII code. + * @return: 0 on success or negative error code on failure. + */ +static int sdc_init_panel(u16 width, u16 height, enum pixel_fmt pixel_fmt) +{ + u32 reg; + uint32_t old_conf; + + /* Init panel size and blanking periods */ + reg = ((H_SYNC_WIDTH - 1) << 26) | + ((u32)(width + H_START_WIDTH + H_END_WIDTH - 1) << 16); + reg_write(reg, SDC_HOR_CONF); + + reg = ((V_SYNC_WIDTH - 1) << 26) | SDC_V_SYNC_WIDTH_L | + ((u32)(height + V_START_WIDTH + V_END_WIDTH - 1) << 16); + reg_write(reg, SDC_VER_CONF); + + switch (PANEL_TYPE) { + case IPU_PANEL_SHARP_TFT: + reg_write(0x00FD0102L, SDC_SHARP_CONF_1); + reg_write(0x00F500F4L, SDC_SHARP_CONF_2); + reg_write(SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF); + break; + case IPU_PANEL_TFT: + reg_write(SDC_COM_TFT_COLOR, SDC_COM_CONF); + break; + default: + return -EINVAL; + } + + /* Init clocking */ + + /* + * Calculate divider: fractional part is 4 bits so simply multiple by + * 2^4 to get fractional part, as long as we stay under ~250MHz and on + * i.MX31 it (HSP_CLK) is <= 178MHz. Currently 128.267MHz + */ + + reg_write((((IF_CLK_DIV / 8) - 1) << 22) | + IF_CLK_DIV, DI_DISP3_TIME_CONF); + + /* DI settings */ + old_conf = reg_read(DI_DISP_IF_CONF) & 0x78FFFFFF; + reg_write(old_conf | IF_CONF, DI_DISP_IF_CONF); + + old_conf = reg_read(DI_DISP_SIG_POL) & 0xE0FFFFFF; + reg_write(old_conf | SIG_POL, DI_DISP_SIG_POL); + + reg_write(fmt_cfg[pixel_fmt].b0, DI_DISP3_B0_MAP); + reg_write(fmt_cfg[pixel_fmt].b1, DI_DISP3_B1_MAP); + reg_write(fmt_cfg[pixel_fmt].b2, DI_DISP3_B2_MAP); + reg_write(reg_read(DI_DISP_ACC_CC) | + ((fmt_cfg[pixel_fmt].acc - 1) << 12), DI_DISP_ACC_CC); + + return 0; +} + +static void ipu_ch_param_set_size(union chan_param_mem *params, + uint32_t pixel_fmt, uint16_t width, + uint16_t height, uint16_t stride) +{ + params->pp.fw = width - 1; + params->pp.fh_l = height - 1; + params->pp.fh_h = (height - 1) >> 8; + params->pp.sl = stride - 1; + + /* See above, for further formats see the Linux driver */ + switch (pixel_fmt) { + case IPU_PIX_FMT_RGB565: + params->ip.bpp = 2; + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 0; /* Red bit offset */ + params->ip.ofs1 = 5; /* Green bit offset */ + params->ip.ofs2 = 11; /* Blue bit offset */ + params->ip.ofs3 = 16; /* Alpha bit offset */ + params->ip.wid0 = 4; /* Red bit width - 1 */ + params->ip.wid1 = 5; /* Green bit width - 1 */ + params->ip.wid2 = 4; /* Blue bit width - 1 */ + break; + case IPU_PIX_FMT_RGB24: + params->ip.bpp = 1; /* 24 BPP & RGB PFS */ + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 16; /* Red bit offset */ + params->ip.ofs1 = 8; /* Green bit offset */ + params->ip.ofs2 = 0; /* Blue bit offset */ + params->ip.ofs3 = 24; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + break; + default: + break; + } + + params->pp.nsb = 1; +} + +static void ipu_ch_param_set_buffer(union chan_param_mem *params, + void *buf0, void *buf1) +{ + params->pp.eba0 = (u32)buf0; + params->pp.eba1 = (u32)buf1; +} + +static void ipu_write_param_mem(uint32_t addr, uint32_t *data, + uint32_t num_words) +{ + for (; num_words > 0; num_words--) { + reg_write(addr, IPU_IMA_ADDR); + reg_write(*data++, IPU_IMA_DATA); + addr++; + if ((addr & 0x7) == 5) { + addr &= ~0x7; /* set to word 0 */ + addr += 8; /* increment to next row */ + } + } +} + +static uint32_t bpp_to_pixfmt(int bpp) +{ + switch (bpp) { + case 16: + return IPU_PIX_FMT_RGB565; + default: + return 0; + } +} + +static uint32_t dma_param_addr(enum ipu_channel channel) +{ + /* Channel Parameter Memory */ + return 0x10000 | (channel << 4); +} + +static void ipu_init_channel_buffer(enum ipu_channel channel, void *fbmem) +{ + union chan_param_mem params = {}; + uint32_t reg; + uint32_t stride_bytes; + + stride_bytes = (XRES * ((BIT_PER_PIXEL + 7) / 8) + 3) & ~3; + + /* Build parameter memory data for DMA channel */ + ipu_ch_param_set_size(¶ms, bpp_to_pixfmt(BIT_PER_PIXEL), + XRES, YRES, stride_bytes); + ipu_ch_param_set_buffer(¶ms, fbmem, NULL); + params.pp.bam = 0; + /* Some channels (rotation) have restriction on burst length */ + + switch (channel) { + case IDMAC_SDC_0: + /* In original code only IPU_PIX_FMT_RGB565 was setting burst */ + params.pp.npb = 16 - 1; + break; + default: + break; + } + + ipu_write_param_mem(dma_param_addr(channel), (uint32_t *)¶ms, 10); + + /* Disable double-buffering */ + reg = reg_read(IPU_CHA_DB_MODE_SEL); + reg &= ~(1UL << channel); + reg_write(reg, IPU_CHA_DB_MODE_SEL); +} + +static void ipu_channel_set_priority(enum ipu_channel channel, + int prio) +{ + u32 reg = reg_read(IDMAC_CHA_PRI); + + if (prio) + reg |= 1UL << channel; + else + reg &= ~(1UL << channel); + + reg_write(reg, IDMAC_CHA_PRI); +} + +/* + * ipu_enable_channel() - enable an IPU channel. + * @channel: channel ID. + * @return: 0 on success or negative error code on failure. + */ +static int ipu_enable_channel(enum ipu_channel channel) +{ + uint32_t reg; + + /* Reset to buffer 0 */ + reg_write(1UL << channel, IPU_CHA_CUR_BUF); + + switch (channel) { + case IDMAC_SDC_0: + ipu_channel_set_priority(channel, 1); + break; + default: + break; + } + + reg = reg_read(IDMAC_CHA_EN); + reg_write(reg | (1UL << channel), IDMAC_CHA_EN); + + return 0; +} + +static int ipu_update_channel_buffer(enum ipu_channel channel, void *buf) +{ + uint32_t reg; + + reg = reg_read(IPU_CHA_BUF0_RDY); + if (reg & (1UL << channel)) + return -EACCES; + + /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 0) */ + reg_write(dma_param_addr(channel) + 0x0008UL, IPU_IMA_ADDR); + reg_write((u32)buf, IPU_IMA_DATA); + + return 0; +} + +static int idmac_tx_submit(enum ipu_channel channel, void *buf) +{ + int ret; + + ipu_init_channel_buffer(channel, buf); + + + /* ipu_idmac.c::ipu_submit_channel_buffers() */ + ret = ipu_update_channel_buffer(channel, buf); + if (ret < 0) + return ret; + + /* ipu_idmac.c::ipu_select_buffer() */ + /* Mark buffer 0 as ready. */ + reg_write(1UL << channel, IPU_CHA_BUF0_RDY); + + + ret = ipu_enable_channel(channel); + return ret; +} + +static void sdc_enable_channel(void *fbmem) +{ + int ret; + u32 reg; + + ret = idmac_tx_submit(IDMAC_SDC_0, fbmem); + + /* mx3fb.c::sdc_fb_init() */ + if (ret >= 0) { + reg = reg_read(SDC_COM_CONF); + reg_write(reg | SDC_COM_BG_EN, SDC_COM_CONF); + } + + /* + * Attention! Without this msleep the channel keeps generating + * interrupts. Next sdc_set_brightness() is going to be called + * from mx3fb_blank(). + */ + msleep(2); +} + +/* + * mx3fb_set_par() - set framebuffer parameters and change the operating mode. + * @return: 0 on success or negative error code on failure. + */ +static int mx3fb_set_par(void) +{ + int ret; + + ret = sdc_init_panel(XRES, YRES, PIXEL_FMT); + if (ret < 0) + return ret; + + reg_write((H_START_WIDTH << 16) | V_START_WIDTH, SDC_BG_POS); + + return 0; +} + +/* References in this function refer to respective Linux kernel sources */ +void lcd_enable(void) +{ + u32 reg; + + /* pcm037.c::mxc_board_init() */ + + /* Display Interface #3 */ + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD0, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD1, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD2, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD3, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD4, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD5, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD6, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD7, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD8, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD9, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD10, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD11, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD12, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD13, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD14, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD15, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD16, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_LD17, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_VSYNC3, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_HSYNC, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_FPSHIFT, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_DRDY0, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_D3_REV, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_CONTRAST, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_D3_SPL, MUX_CTL_FUNC)); + mx31_gpio_mux(IOMUX_MODE_L(MX31_PIN_D3_CLS, MUX_CTL_FUNC)); + + + /* ipu_idmac.c::ipu_probe() */ + + /* Start the clock */ + __REG(CCM_CGR1) = __REG(CCM_CGR1) | (3 << 22); + + + /* ipu_idmac.c::ipu_idmac_init() */ + + /* Service request counter to maximum - shouldn't be needed */ + reg_write(0x00000070, IDMAC_CONF); + + + /* ipu_idmac.c::ipu_init_channel() */ + + /* Enable IPU sub modules */ + reg = reg_read(IPU_CONF) | IPU_CONF_SDC_EN | IPU_CONF_DI_EN; + reg_write(reg, IPU_CONF); + + + /* mx3fb.c::init_fb_chan() */ + + /* set Display Interface clock period */ + reg_write(0x00100010L, DI_HSP_CLK_PER); + /* Might need to trigger HSP clock change - see 44.3.3.8.5 */ + + + /* mx3fb.c::sdc_set_brightness() */ + + /* This might be board-specific */ + reg_write(0x03000000UL | 255 << 16, SDC_PWM_CTRL); + + + /* mx3fb.c::sdc_set_global_alpha() */ + + /* Use global - not per-pixel - Alpha-blending */ + reg = reg_read(SDC_GW_CTRL) & 0x00FFFFFFL; + reg_write(reg | ((uint32_t) 0xff << 24), SDC_GW_CTRL); + + reg = reg_read(SDC_COM_CONF); + reg_write(reg | SDC_COM_GLB_A, SDC_COM_CONF); + + + /* mx3fb.c::sdc_set_color_key() */ + + /* Disable colour-keying for background */ + reg = reg_read(SDC_COM_CONF) & + ~(SDC_COM_GWSEL | SDC_COM_KEY_COLOR_G); + reg_write(reg, SDC_COM_CONF); + + + mx3fb_set_par(); + + sdc_enable_channel(lcd_base); + + /* + * Linux driver calls sdc_set_brightness() here again, + * once is enough for us + */ +} + +void lcd_ctrl_init(void *lcdbase) +{ + u32 mem_len = XRES * YRES * BIT_PER_PIXEL / 8; + /* + * We rely on lcdbase being a physical address, i.e., either MMU off, + * or 1-to-1 mapping. Might want to add some virt2phys here. + */ + if (!lcdbase) + return; + + memset(lcdbase, 0, mem_len); +} + +ulong calc_fbsize(void) +{ + return ((panel_info.vl_col * panel_info.vl_row * + NBITS(panel_info.vl_bpix)) / 8) + PAGE_SIZE; +} + +int overwrite_console(void) +{ + /* Keep stdout / stderr on serial, our LCD is for splashscreen only */ + return 1; +} diff --git a/u-boot/drivers/video/mxc_ipuv3_fb.c b/u-boot/drivers/video/mxc_ipuv3_fb.c new file mode 100644 index 0000000..a66981c --- /dev/null +++ b/u-boot/drivers/video/mxc_ipuv3_fb.c @@ -0,0 +1,642 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * MX51 Linux framebuffer: + * + * (C) Copyright 2004-2010 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* #define DEBUG */ +#include <common.h> +#include <asm/errno.h> +#include <linux/string.h> +#include <linux/list.h> +#include <linux/fb.h> +#include <asm/io.h> +#include <malloc.h> +#include <lcd.h> +#include "videomodes.h" +#include "ipu.h" +#include "mxcfb.h" + +DECLARE_GLOBAL_DATA_PTR; + +void *lcd_base; /* Start of framebuffer memory */ +void *lcd_console_address; /* Start of console buffer */ + +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; + +short console_col; +short console_row; + +vidinfo_t panel_info; + +static int mxcfb_map_video_memory(struct fb_info *fbi); +static int mxcfb_unmap_video_memory(struct fb_info *fbi); + +void lcd_initcolregs(void) +{ +} + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ +} + +void lcd_disable(void) +{ +} + +void lcd_panel_disable(void) +{ +} + +void fb_videomode_to_var(struct fb_var_screeninfo *var, + const struct fb_videomode *mode) +{ + var->xres = mode->xres; + var->yres = mode->yres; + var->xres_virtual = mode->xres; + var->yres_virtual = mode->yres; + var->xoffset = 0; + var->yoffset = 0; + var->pixclock = mode->pixclock; + var->left_margin = mode->left_margin; + var->right_margin = mode->right_margin; + var->upper_margin = mode->upper_margin; + var->lower_margin = mode->lower_margin; + var->hsync_len = mode->hsync_len; + var->vsync_len = mode->vsync_len; + var->sync = mode->sync; + var->vmode = mode->vmode & FB_VMODE_MASK; +} + +/* + * Structure containing the MXC specific framebuffer information. + */ +struct mxcfb_info { + int blank; + ipu_channel_t ipu_ch; + int ipu_di; + u32 ipu_di_pix_fmt; + unsigned char overlay; + unsigned char alpha_chan_en; + dma_addr_t alpha_phy_addr0; + dma_addr_t alpha_phy_addr1; + void *alpha_virt_addr0; + void *alpha_virt_addr1; + uint32_t alpha_mem_len; + uint32_t cur_ipu_buf; + uint32_t cur_ipu_alpha_buf; + + u32 pseudo_palette[16]; +}; + +enum { + BOTH_ON, + SRC_ON, + TGT_ON, + BOTH_OFF +}; + +static unsigned long default_bpp = 16; +static unsigned char g_dp_in_use; +static struct fb_info *mxcfb_info[3]; +static int ext_clk_used; + +static uint32_t bpp_to_pixfmt(struct fb_info *fbi) +{ + uint32_t pixfmt = 0; + + debug("bpp_to_pixfmt: %d\n", fbi->var.bits_per_pixel); + + if (fbi->var.nonstd) + return fbi->var.nonstd; + + switch (fbi->var.bits_per_pixel) { + case 24: + pixfmt = IPU_PIX_FMT_BGR24; + break; + case 32: + pixfmt = IPU_PIX_FMT_BGR32; + break; + case 16: + pixfmt = IPU_PIX_FMT_RGB565; + break; + } + return pixfmt; +} + +/* + * Set fixed framebuffer parameters based on variable settings. + * + * @param info framebuffer information pointer + */ +static int mxcfb_set_fix(struct fb_info *info) +{ + struct fb_fix_screeninfo *fix = &info->fix; + struct fb_var_screeninfo *var = &info->var; + + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + + fix->type = FB_TYPE_PACKED_PIXELS; + fix->accel = FB_ACCEL_NONE; + fix->visual = FB_VISUAL_TRUECOLOR; + fix->xpanstep = 1; + fix->ypanstep = 1; + + return 0; +} + +static int setup_disp_channel1(struct fb_info *fbi) +{ + ipu_channel_params_t params; + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + + memset(¶ms, 0, sizeof(params)); + params.mem_dp_bg_sync.di = mxc_fbi->ipu_di; + + debug("%s called\n", __func__); + /* + * Assuming interlaced means yuv output, below setting also + * valid for mem_dc_sync. FG should have the same vmode as BG. + */ + if (fbi->var.vmode & FB_VMODE_INTERLACED) { + params.mem_dp_bg_sync.interlaced = 1; + params.mem_dp_bg_sync.out_pixel_fmt = + IPU_PIX_FMT_YUV444; + } else { + if (mxc_fbi->ipu_di_pix_fmt) { + params.mem_dp_bg_sync.out_pixel_fmt = + mxc_fbi->ipu_di_pix_fmt; + } else { + params.mem_dp_bg_sync.out_pixel_fmt = + IPU_PIX_FMT_RGB666; + } + } + params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi); + if (mxc_fbi->alpha_chan_en) + params.mem_dp_bg_sync.alpha_chan_en = 1; + + ipu_init_channel(mxc_fbi->ipu_ch, ¶ms); + + return 0; +} + +static int setup_disp_channel2(struct fb_info *fbi) +{ + int retval = 0; + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + + mxc_fbi->cur_ipu_buf = 1; + if (mxc_fbi->alpha_chan_en) + mxc_fbi->cur_ipu_alpha_buf = 1; + + fbi->var.xoffset = fbi->var.yoffset = 0; + + debug("%s: %x %d %d %d %lx %lx\n", + __func__, + mxc_fbi->ipu_ch, + fbi->var.xres, + fbi->var.yres, + fbi->fix.line_length, + fbi->fix.smem_start, + fbi->fix.smem_start + + (fbi->fix.line_length * fbi->var.yres)); + + retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, + bpp_to_pixfmt(fbi), + fbi->var.xres, fbi->var.yres, + fbi->fix.line_length, + fbi->fix.smem_start + + (fbi->fix.line_length * fbi->var.yres), + fbi->fix.smem_start, + 0, 0); + if (retval) + printf("ipu_init_channel_buffer error %d\n", retval); + + return retval; +} + +/* + * Set framebuffer parameters and change the operating mode. + * + * @param info framebuffer information pointer + */ +static int mxcfb_set_par(struct fb_info *fbi) +{ + int retval = 0; + u32 mem_len; + ipu_di_signal_cfg_t sig_cfg; + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + uint32_t out_pixel_fmt; + + ipu_disable_channel(mxc_fbi->ipu_ch); + ipu_uninit_channel(mxc_fbi->ipu_ch); + mxcfb_set_fix(fbi); + + mem_len = fbi->var.yres_virtual * fbi->fix.line_length; + if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { + if (fbi->fix.smem_start) + mxcfb_unmap_video_memory(fbi); + + if (mxcfb_map_video_memory(fbi) < 0) + return -ENOMEM; + } + + setup_disp_channel1(fbi); + + memset(&sig_cfg, 0, sizeof(sig_cfg)); + if (fbi->var.vmode & FB_VMODE_INTERLACED) { + sig_cfg.interlaced = 1; + out_pixel_fmt = IPU_PIX_FMT_YUV444; + } else { + if (mxc_fbi->ipu_di_pix_fmt) + out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt; + else + out_pixel_fmt = IPU_PIX_FMT_RGB666; + } + if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */ + sig_cfg.odd_field_first = 1; + if ((fbi->var.sync & FB_SYNC_EXT) || ext_clk_used) + sig_cfg.ext_clk = 1; + if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) + sig_cfg.Hsync_pol = 1; + if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) + sig_cfg.Vsync_pol = 1; + if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL)) + sig_cfg.clk_pol = 1; + if (fbi->var.sync & FB_SYNC_DATA_INVERT) + sig_cfg.data_pol = 1; + if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT)) + sig_cfg.enable_pol = 1; + if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN) + sig_cfg.clkidle_en = 1; + + debug("pixclock = %ul Hz\n", + (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); + + if (ipu_init_sync_panel(mxc_fbi->ipu_di, + (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, + fbi->var.xres, fbi->var.yres, + out_pixel_fmt, + fbi->var.left_margin, + fbi->var.hsync_len, + fbi->var.right_margin, + fbi->var.upper_margin, + fbi->var.vsync_len, + fbi->var.lower_margin, + 0, sig_cfg) != 0) { + puts("mxcfb: Error initializing panel.\n"); + return -EINVAL; + } + + retval = setup_disp_channel2(fbi); + if (retval) + return retval; + + if (mxc_fbi->blank == FB_BLANK_UNBLANK) + ipu_enable_channel(mxc_fbi->ipu_ch); + + return retval; +} + +/* + * Check framebuffer variable parameters and adjust to valid values. + * + * @param var framebuffer variable parameters + * + * @param info framebuffer information pointer + */ +static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + u32 vtotal; + u32 htotal; + + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + + if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && + (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8)) + var->bits_per_pixel = default_bpp; + + switch (var->bits_per_pixel) { + case 8: + var->red.length = 3; + var->red.offset = 5; + var->red.msb_right = 0; + + var->green.length = 3; + var->green.offset = 2; + var->green.msb_right = 0; + + var->blue.length = 2; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 16: + var->red.length = 5; + var->red.offset = 11; + var->red.msb_right = 0; + + var->green.length = 6; + var->green.offset = 5; + var->green.msb_right = 0; + + var->blue.length = 5; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 24: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 32: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 8; + var->transp.offset = 24; + var->transp.msb_right = 0; + break; + } + + if (var->pixclock < 1000) { + htotal = var->xres + var->right_margin + var->hsync_len + + var->left_margin; + vtotal = var->yres + var->lower_margin + var->vsync_len + + var->upper_margin; + var->pixclock = (vtotal * htotal * 6UL) / 100UL; + var->pixclock = KHZ2PICOS(var->pixclock); + printf("pixclock set for 60Hz refresh = %u ps\n", + var->pixclock); + } + + var->height = -1; + var->width = -1; + var->grayscale = 0; + + return 0; +} + +static int mxcfb_map_video_memory(struct fb_info *fbi) +{ + if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length) { + fbi->fix.smem_len = fbi->var.yres_virtual * + fbi->fix.line_length; + } + + fbi->screen_base = (char *)lcd_base; + fbi->fix.smem_start = (unsigned long)lcd_base; + if (fbi->screen_base == 0) { + puts("Unable to allocate framebuffer memory\n"); + fbi->fix.smem_len = 0; + fbi->fix.smem_start = 0; + return -EBUSY; + } + + debug("allocated fb @ paddr=0x%08X, size=%d.\n", + (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len); + + fbi->screen_size = fbi->fix.smem_len; + + /* Clear the screen */ + memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); + + return 0; +} + +static int mxcfb_unmap_video_memory(struct fb_info *fbi) +{ + fbi->screen_base = 0; + fbi->fix.smem_start = 0; + fbi->fix.smem_len = 0; + return 0; +} + +/* + * Initializes the framebuffer information pointer. After allocating + * sufficient memory for the framebuffer structure, the fields are + * filled with custom information passed in from the configurable + * structures. This includes information such as bits per pixel, + * color maps, screen width/height and RGBA offsets. + * + * @return Framebuffer structure initialized with our information + */ +static struct fb_info *mxcfb_init_fbinfo(void) +{ +#define BYTES_PER_LONG 4 +#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) + struct fb_info *fbi; + struct mxcfb_info *mxcfbi; + char *p; + int size = sizeof(struct mxcfb_info) + PADDING + + sizeof(struct fb_info); + + debug("%s: %d %d %d %d\n", + __func__, + PADDING, + size, + sizeof(struct mxcfb_info), + sizeof(struct fb_info)); + /* + * Allocate sufficient memory for the fb structure + */ + + p = malloc(size); + if (!p) + return NULL; + + memset(p, 0, size); + + fbi = (struct fb_info *)p; + fbi->par = p + sizeof(struct fb_info) + PADDING; + + mxcfbi = (struct mxcfb_info *)fbi->par; + debug("Framebuffer structures at: fbi=0x%x mxcfbi=0x%x\n", + (unsigned int)fbi, (unsigned int)mxcfbi); + + fbi->var.activate = FB_ACTIVATE_NOW; + + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = mxcfbi->pseudo_palette; + + return fbi; +} + +/* + * Probe routine for the framebuffer driver. It is called during the + * driver binding process. The following functions are performed in + * this routine: Framebuffer initialization, Memory allocation and + * mapping, Framebuffer registration, IPU initialization. + * + * @return Appropriate error code to the kernel common code + */ +static int mxcfb_probe(u32 interface_pix_fmt, struct fb_videomode *mode) +{ + struct fb_info *fbi; + struct mxcfb_info *mxcfbi; + int ret = 0; + + /* + * Initialize FB structures + */ + fbi = mxcfb_init_fbinfo(); + if (!fbi) { + ret = -ENOMEM; + goto err0; + } + mxcfbi = (struct mxcfb_info *)fbi->par; + + if (!g_dp_in_use) { + mxcfbi->ipu_ch = MEM_BG_SYNC; + mxcfbi->blank = FB_BLANK_UNBLANK; + } else { + mxcfbi->ipu_ch = MEM_DC_SYNC; + mxcfbi->blank = FB_BLANK_POWERDOWN; + } + + mxcfbi->ipu_di = 0; + + ipu_disp_set_global_alpha(mxcfbi->ipu_ch, 1, 0x80); + ipu_disp_set_color_key(mxcfbi->ipu_ch, 0, 0); + strcpy(fbi->fix.id, "DISP3 BG"); + + g_dp_in_use = 1; + + mxcfb_info[mxcfbi->ipu_di] = fbi; + + /* Need dummy values until real panel is configured */ + fbi->var.xres = 640; + fbi->var.yres = 480; + fbi->var.bits_per_pixel = 16; + + mxcfbi->ipu_di_pix_fmt = interface_pix_fmt; + fb_videomode_to_var(&fbi->var, mode); + + mxcfb_check_var(&fbi->var, fbi); + + /* Default Y virtual size is 2x panel size */ + fbi->var.yres_virtual = fbi->var.yres * 2; + + mxcfb_set_fix(fbi); + + /* alocate fb first */ + if (mxcfb_map_video_memory(fbi) < 0) + return -ENOMEM; + + mxcfb_set_par(fbi); + + /* Setting panel_info for lcd */ + panel_info.cmap = NULL; + panel_info.vl_col = fbi->var.xres; + panel_info.vl_row = fbi->var.yres; + panel_info.vl_bpix = LCD_BPP; + + lcd_line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8; + + debug("MXC IPUV3 configured\n" + "XRES = %d YRES = %d BitsXpixel = %d\n", + panel_info.vl_col, + panel_info.vl_row, + panel_info.vl_bpix); + + ipu_dump_registers(); + + return 0; + +err0: + return ret; +} + +int overwrite_console(void) +{ + /* Keep stdout / stderr on serial, our LCD is for splashscreen only */ + return 1; +} + +void lcd_ctrl_init(void *lcdbase) +{ + u32 mem_len = panel_info.vl_col * + panel_info.vl_row * + NBITS(panel_info.vl_bpix) / 8; + + /* + * We rely on lcdbase being a physical address, i.e., either MMU off, + * or 1-to-1 mapping. Might want to add some virt2phys here. + */ + if (!lcdbase) + return; + + memset(lcdbase, 0, mem_len); +} + +int mx51_fb_init(struct fb_videomode *mode) +{ + int ret; + + ret = ipu_probe(); + if (ret) + puts("Error initializing IPU\n"); + + lcd_base += 56; + + debug("Framebuffer at 0x%x\n", (unsigned int)lcd_base); + ret = mxcfb_probe(IPU_PIX_FMT_RGB666, mode); + + return ret; +} diff --git a/u-boot/drivers/video/mxcfb.h b/u-boot/drivers/video/mxcfb.h new file mode 100644 index 0000000..d508196 --- /dev/null +++ b/u-boot/drivers/video/mxcfb.h @@ -0,0 +1,68 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2004-2009 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 __ASM_ARCH_MXCFB_H__ +#define __ASM_ARCH_MXCFB_H__ + +#define FB_SYNC_OE_LOW_ACT 0x80000000 +#define FB_SYNC_CLK_LAT_FALL 0x40000000 +#define FB_SYNC_DATA_INVERT 0x20000000 +#define FB_SYNC_CLK_IDLE_EN 0x10000000 +#define FB_SYNC_SHARP_MODE 0x08000000 +#define FB_SYNC_SWAP_RGB 0x04000000 + +struct mxcfb_gbl_alpha { + int enable; + int alpha; +}; + +struct mxcfb_loc_alpha { + int enable; + int alpha_in_pixel; + unsigned long alpha_phy_addr0; + unsigned long alpha_phy_addr1; +}; + +struct mxcfb_color_key { + int enable; + __u32 color_key; +}; + +struct mxcfb_pos { + __u16 x; + __u16 y; +}; + +struct mxcfb_gamma { + int enable; + int constk[16]; + int slopek[16]; +}; + +#endif diff --git a/u-boot/drivers/video/omap3_dss.c b/u-boot/drivers/video/omap3_dss.c new file mode 100644 index 0000000..69c705a --- /dev/null +++ b/u-boot/drivers/video/omap3_dss.c @@ -0,0 +1,130 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * Syed Mohammed Khasim <khasim@ti.com> + * + * Referred to Linux DSS driver files for OMAP3 + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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's version 2 of + * the License. + * + * 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 + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/dss.h> + +/* + * Configure VENC for a given Mode (NTSC / PAL) + */ +void omap3_dss_venc_config(const struct venc_regs *venc_cfg, + u32 height, u32 width) +{ + struct venc_regs *venc = (struct venc_regs *) OMAP3_VENC_BASE; + struct dss_regs *dss = (struct dss_regs *) OMAP3_DSS_BASE; + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + + writel(venc_cfg->status, &venc->status); + writel(venc_cfg->f_control, &venc->f_control); + writel(venc_cfg->vidout_ctrl, &venc->vidout_ctrl); + writel(venc_cfg->sync_ctrl, &venc->sync_ctrl); + writel(venc_cfg->llen, &venc->llen); + writel(venc_cfg->flens, &venc->flens); + writel(venc_cfg->hfltr_ctrl, &venc->hfltr_ctrl); + writel(venc_cfg->cc_carr_wss_carr, &venc->cc_carr_wss_carr); + writel(venc_cfg->c_phase, &venc->c_phase); + writel(venc_cfg->gain_u, &venc->gain_u); + writel(venc_cfg->gain_v, &venc->gain_v); + writel(venc_cfg->gain_y, &venc->gain_y); + writel(venc_cfg->black_level, &venc->black_level); + writel(venc_cfg->blank_level, &venc->blank_level); + writel(venc_cfg->x_color, &venc->x_color); + writel(venc_cfg->m_control, &venc->m_control); + writel(venc_cfg->bstamp_wss_data, &venc->bstamp_wss_data); + writel(venc_cfg->s_carr, &venc->s_carr); + writel(venc_cfg->line21, &venc->line21); + writel(venc_cfg->ln_sel, &venc->ln_sel); + writel(venc_cfg->l21__wc_ctl, &venc->l21__wc_ctl); + writel(venc_cfg->htrigger_vtrigger, &venc->htrigger_vtrigger); + writel(venc_cfg->savid__eavid, &venc->savid__eavid); + writel(venc_cfg->flen__fal, &venc->flen__fal); + writel(venc_cfg->lal__phase_reset, &venc->lal__phase_reset); + writel(venc_cfg->hs_int_start_stop_x, + &venc->hs_int_start_stop_x); + writel(venc_cfg->hs_ext_start_stop_x, + &venc->hs_ext_start_stop_x); + writel(venc_cfg->vs_int_start_x, &venc->vs_int_start_x); + writel(venc_cfg->vs_int_stop_x__vs_int_start_y, + &venc->vs_int_stop_x__vs_int_start_y); + writel(venc_cfg->vs_int_stop_y__vs_ext_start_x, + &venc->vs_int_stop_y__vs_ext_start_x); + writel(venc_cfg->vs_ext_stop_x__vs_ext_start_y, + &venc->vs_ext_stop_x__vs_ext_start_y); + writel(venc_cfg->vs_ext_stop_y, &venc->vs_ext_stop_y); + writel(venc_cfg->avid_start_stop_x, &venc->avid_start_stop_x); + writel(venc_cfg->avid_start_stop_y, &venc->avid_start_stop_y); + writel(venc_cfg->fid_int_start_x__fid_int_start_y, + &venc->fid_int_start_x__fid_int_start_y); + writel(venc_cfg->fid_int_offset_y__fid_ext_start_x, + &venc->fid_int_offset_y__fid_ext_start_x); + writel(venc_cfg->fid_ext_start_y__fid_ext_offset_y, + &venc->fid_ext_start_y__fid_ext_offset_y); + writel(venc_cfg->tvdetgp_int_start_stop_x, + &venc->tvdetgp_int_start_stop_x); + writel(venc_cfg->tvdetgp_int_start_stop_y, + &venc->tvdetgp_int_start_stop_y); + writel(venc_cfg->gen_ctrl, &venc->gen_ctrl); + writel(venc_cfg->output_control, &venc->output_control); + writel(venc_cfg->dac_b__dac_c, &venc->dac_b__dac_c); + + /* Configure DSS for VENC Settings */ + writel(VENC_DSS_CONFIG, &dss->control); + + /* Configure height and width for Digital out */ + writel(((height << DIG_LPP_SHIFT) | width), &dispc->size_dig); +} + +/* + * Configure Panel Specific Parameters + */ +void omap3_dss_panel_config(const struct panel_config *panel_cfg) +{ + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + + writel(panel_cfg->timing_h, &dispc->timing_h); + writel(panel_cfg->timing_v, &dispc->timing_v); + writel(panel_cfg->pol_freq, &dispc->pol_freq); + writel(panel_cfg->divisor, &dispc->divisor); + writel(panel_cfg->lcd_size, &dispc->size_lcd); + writel((panel_cfg->load_mode << FRAME_MODE_SHIFT), &dispc->config); + writel(((panel_cfg->panel_type << TFTSTN_SHIFT) | + (panel_cfg->data_lines << DATALINES_SHIFT)), &dispc->control); + writel(panel_cfg->panel_color, &dispc->default_color0); +} + +/* + * Enable LCD and DIGITAL OUT in DSS + */ +void omap3_dss_enable(void) +{ + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + u32 l = 0; + + l = readl(&dispc->control); + l |= DISPC_ENABLE; + writel(l, &dispc->control); +} diff --git a/u-boot/drivers/video/s6e63d6.c b/u-boot/drivers/video/s6e63d6.c new file mode 100644 index 0000000..d163506 --- /dev/null +++ b/u-boot/drivers/video/s6e63d6.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009 + * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ +#include <common.h> +#include <spi.h> +#include <s6e63d6.h> + +/* + * Each transfer is performed as: + * 1. chip-select active + * 2. send 8-bit start code + * 3. send 16-bit data + * 4. chip-select inactive + */ +static int send_word(struct s6e63d6 *data, u8 rs, u16 word) +{ + /* + * The start byte looks like (binary): + * 01110<ID><RS><R/W> + * RS is 0 for index or 1 for data, and R/W is 0 for write. + */ + u32 buf8 = 0x70 | data->id | (rs & 2); + u32 buf16 = cpu_to_le16(word); + u32 buf_in; + int err; + + err = spi_xfer(data->slave, 8, &buf8, &buf_in, SPI_XFER_BEGIN); + if (err) + return err; + + return spi_xfer(data->slave, 16, &buf16, &buf_in, SPI_XFER_END); +} + +/* Index and param differ in Register Select bit */ +int s6e63d6_index(struct s6e63d6 *data, u8 idx) +{ + return send_word(data, 0, idx); +} + +int s6e63d6_param(struct s6e63d6 *data, u16 param) +{ + return send_word(data, 2, param); +} + +int s6e63d6_init(struct s6e63d6 *data) +{ + if (data->id != 0 && data->id != 4) { + printf("s6e63d6: invalid ID %u\n", data->id); + return 1; + } + + data->slave = spi_setup_slave(data->bus, data->cs, 100000, SPI_MODE_3); + if (!data->slave) + return 1; + + return 0; +} diff --git a/u-boot/drivers/video/sed13806.c b/u-boot/drivers/video/sed13806.c new file mode 100644 index 0000000..9cd19b5 --- /dev/null +++ b/u-boot/drivers/video/sed13806.c @@ -0,0 +1,307 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - <www.staubli.com> + * Pierre AUBERT p.aubert@staubli.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ +/* Video support for Epson SED13806 chipset */ + +#include <common.h> + +#include <video_fb.h> +#include <sed13806.h> + +#define readByte(ptrReg) \ + *(volatile unsigned char *)(sed13806.isaBase + ptrReg) + +#define writeByte(ptrReg,value) \ + *(volatile unsigned char *)(sed13806.isaBase + ptrReg) = value + +#ifdef CONFIG_TOTAL5200 +#define writeWord(ptrReg,value) \ + (*(volatile unsigned short *)(sed13806.isaBase + ptrReg) = value) +#else +#define writeWord(ptrReg,value) \ + (*(volatile unsigned short *)(sed13806.isaBase + ptrReg) = ((value >> 8 ) & 0xff) | ((value << 8) & 0xff00)) +#endif + +GraphicDevice sed13806; + +/*----------------------------------------------------------------------------- + * EpsonSetRegs -- + *----------------------------------------------------------------------------- + */ +static void EpsonSetRegs (void) +{ + /* the content of the chipset register depends on the board (clocks, ...)*/ + const S1D_REGS *preg = board_get_regs (); + while (preg -> Index) { + writeByte (preg -> Index, preg -> Value); + preg ++; + } +} + +/*----------------------------------------------------------------------------- + * video_hw_init -- + *----------------------------------------------------------------------------- + */ +void *video_hw_init (void) +{ + unsigned int *vm, i; + + memset (&sed13806, 0, sizeof (GraphicDevice)); + + /* Initialization of the access to the graphic chipset + Retreive base address of the chipset + (see board/RPXClassic/eccx.c) */ + if ((sed13806.isaBase = board_video_init ()) == 0) { + return (NULL); + } + + sed13806.frameAdrs = sed13806.isaBase + FRAME_BUFFER_OFFSET; + sed13806.winSizeX = board_get_width (); + sed13806.winSizeY = board_get_height (); + +#if defined(CONFIG_VIDEO_SED13806_8BPP) + sed13806.gdfIndex = GDF__8BIT_INDEX; + sed13806.gdfBytesPP = 1; + +#elif defined(CONFIG_VIDEO_SED13806_16BPP) + sed13806.gdfIndex = GDF_16BIT_565RGB; + sed13806.gdfBytesPP = 2; + +#else +#error Unsupported SED13806 BPP +#endif + + sed13806.memSize = sed13806.winSizeX * sed13806.winSizeY * sed13806.gdfBytesPP; + + /* Load SED registers */ + EpsonSetRegs (); + + /* (see board/RPXClassic/RPXClassic.c) */ + board_validate_screen (sed13806.isaBase); + + /* Clear video memory */ + i = sed13806.memSize/4; + vm = (unsigned int *)sed13806.frameAdrs; + while(i--) + *vm++ = 0; + + + return (&sed13806); +} +/*----------------------------------------------------------------------------- + * Epson_wait_idle -- Wait for hardware to become idle + *----------------------------------------------------------------------------- + */ +static void Epson_wait_idle (void) +{ + while (readByte (BLT_CTRL0) & 0x80); + + /* Read a word in the BitBLT memory area to shutdown the BitBLT engine */ + *(volatile unsigned short *)(sed13806.isaBase + BLT_REG); +} + +/*----------------------------------------------------------------------------- + * video_hw_bitblt -- + *----------------------------------------------------------------------------- + */ +void video_hw_bitblt ( + unsigned int bpp, /* bytes per pixel */ + unsigned int src_x, /* source pos x */ + unsigned int src_y, /* source pos y */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y /* frame height */ + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&sed13806; + unsigned long srcAddr, dstAddr; + unsigned int stride = bpp * pGD -> winSizeX; + + srcAddr = (src_y * stride) + (src_x * bpp); + dstAddr = (dst_y * stride) + (dst_x * bpp); + + Epson_wait_idle (); + + writeByte(BLT_ROP,0x0C); /* source */ + writeByte(BLT_OP,0x02);/* move blit in positive direction with ROP */ + writeWord(BLT_MEM_OFF0, stride / 2); + if (pGD -> gdfIndex == GDF__8BIT_INDEX) { + writeByte(BLT_CTRL1,0x00); + } + else { + writeByte(BLT_CTRL1,0x01); + } + + writeWord(BLT_WIDTH0,(dim_x - 1)); + writeWord(BLT_HEIGHT0,(dim_y - 1)); + + /* set up blit registers */ + writeByte(BLT_SRC_ADDR0,srcAddr); + writeByte(BLT_SRC_ADDR1,srcAddr>>8); + writeByte(BLT_SRC_ADDR2,srcAddr>>16); + + writeByte(BLT_DST_ADDR0,dstAddr); + writeByte(BLT_DST_ADDR1,dstAddr>>8); + writeByte(BLT_DST_ADDR2,dstAddr>>16); + + /* Engage the blt engine */ + /* rectangular region for src and dst */ + writeByte(BLT_CTRL0,0x80); + + /* wait untill current blits finished */ + Epson_wait_idle (); +} +/*----------------------------------------------------------------------------- + * video_hw_rectfill -- + *----------------------------------------------------------------------------- + */ +void video_hw_rectfill ( + unsigned int bpp, /* bytes per pixel */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y, /* frame height */ + unsigned int color /* fill color */ + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&sed13806; + unsigned long dstAddr; + unsigned int stride = bpp * pGD -> winSizeX; + + dstAddr = (dst_y * stride) + (dst_x * bpp); + + Epson_wait_idle (); + + /* set up blit registers */ + writeByte(BLT_DST_ADDR0,dstAddr); + writeByte(BLT_DST_ADDR1,dstAddr>>8); + writeByte(BLT_DST_ADDR2,dstAddr>>16); + + writeWord(BLT_WIDTH0,(dim_x - 1)); + writeWord(BLT_HEIGHT0,(dim_y - 1)); + writeWord(BLT_FGCOLOR0,color); + + writeByte(BLT_OP,0x0C); /* solid fill */ + writeWord(BLT_MEM_OFF0,stride / 2); + + if (pGD -> gdfIndex == GDF__8BIT_INDEX) { + writeByte(BLT_CTRL1,0x00); + } + else { + writeByte(BLT_CTRL1,0x01); + } + + /* Engage the blt engine */ + /* rectangular region for src and dst */ + writeByte(BLT_CTRL0,0x80); + + /* wait untill current blits finished */ + Epson_wait_idle (); +} + +/*----------------------------------------------------------------------------- + * video_set_lut -- + *----------------------------------------------------------------------------- + */ +void video_set_lut ( + unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + writeByte(REG_LUT_ADDR, index ); + writeByte(REG_LUT_DATA, r); + writeByte(REG_LUT_DATA, g); + writeByte(REG_LUT_DATA, b); +} +#ifdef CONFIG_VIDEO_HW_CURSOR +/*----------------------------------------------------------------------------- + * video_set_hw_cursor -- + *----------------------------------------------------------------------------- + */ +void video_set_hw_cursor (int x, int y) +{ + writeByte (LCD_CURSOR_XL, (x & 0xff)); + writeByte (LCD_CURSOR_XM, (x >> 8)); + writeByte (LCD_CURSOR_YL, (y & 0xff)); + writeByte (LCD_CURSOR_YM, (y >> 8)); +} + +/*----------------------------------------------------------------------------- + * video_init_hw_cursor -- + *----------------------------------------------------------------------------- + */ +void video_init_hw_cursor (int font_width, int font_height) +{ + volatile unsigned char *ptr; + unsigned char pattern; + int i; + + + /* Init cursor content + Cursor size is 64x64 pixels + Start of the cursor memory depends on panel type (dual panel ...) */ + if ((i = readByte (LCD_CURSOR_START)) == 0) { + ptr = (unsigned char *)(sed13806.frameAdrs + DEFAULT_VIDEO_MEMORY_SIZE - HWCURSORSIZE); + } + else { + ptr = (unsigned char *)(sed13806.frameAdrs + DEFAULT_VIDEO_MEMORY_SIZE - (i * 8192)); + } + + /* Fill the first line and the first empty line after cursor */ + for (i = 0, pattern = 0; i < 64; i++) { + if (i < font_width) { + /* Invert background */ + pattern |= 0x3; + + } + else { + /* Background */ + pattern |= 0x2; + } + if ((i & 3) == 3) { + *ptr = pattern; + *(ptr + font_height * 16) = 0xaa; + ptr ++; + pattern = 0; + } + pattern <<= 2; + } + + /* Duplicate this line */ + for (i = 1; i < font_height; i++) { + memcpy ((void *)ptr, (void *)(ptr - 16), 16); + ptr += 16; + } + + for (; i < 64; i++) { + memcpy ((void *)(ptr + 16), (void *)ptr, 16); + ptr += 16; + } + + /* Select cursor mode */ + writeByte (LCD_CURSOR_CNTL, 1); +} +#endif diff --git a/u-boot/drivers/video/sed156x.c b/u-boot/drivers/video/sed156x.c new file mode 100644 index 0000000..707250d --- /dev/null +++ b/u-boot/drivers/video/sed156x.c @@ -0,0 +1,562 @@ +/* + * (C) Copyright 2003 + * + * Pantelis Antoniou <panto@intracom.gr> + * Intracom S.A. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#include <common.h> +#include <watchdog.h> + +#include <sed156x.h> + +/* configure according to the selected display */ +#if defined(CONFIG_SED156X_PG12864Q) +#define LCD_WIDTH 128 +#define LCD_HEIGHT 64 +#define LCD_LINES 64 +#define LCD_PAGES 9 +#define LCD_COLUMNS 132 +#else +#error Unsupported SED156x configuration +#endif + +/* include the font data */ +#include <video_font.h> + +#if VIDEO_FONT_WIDTH != 8 || VIDEO_FONT_HEIGHT != 16 +#error Expecting VIDEO_FONT_WIDTH == 8 && VIDEO_FONT_HEIGHT == 16 +#endif + +#define LCD_BYTE_WIDTH (LCD_WIDTH / 8) +#define VIDEO_FONT_BYTE_WIDTH (VIDEO_FONT_WIDTH / 8) + +#define LCD_TEXT_WIDTH (LCD_WIDTH / VIDEO_FONT_WIDTH) +#define LCD_TEXT_HEIGHT (LCD_HEIGHT / VIDEO_FONT_HEIGHT) + +#define LCD_BYTE_LINESZ (LCD_BYTE_WIDTH * VIDEO_FONT_HEIGHT) + +const int sed156x_text_width = LCD_TEXT_WIDTH; +const int sed156x_text_height = LCD_TEXT_HEIGHT; + +/**************************************************************************************/ + +#define SED156X_SPI_RXD() (SED156X_SPI_RXD_PORT & SED156X_SPI_RXD_MASK) + +#define SED156X_SPI_TXD(x) \ + do { \ + if (x) \ + SED156X_SPI_TXD_PORT |= SED156X_SPI_TXD_MASK; \ + else \ + SED156X_SPI_TXD_PORT &= ~SED156X_SPI_TXD_MASK; \ + } while(0) + +#define SED156X_SPI_CLK(x) \ + do { \ + if (x) \ + SED156X_SPI_CLK_PORT |= SED156X_SPI_CLK_MASK; \ + else \ + SED156X_SPI_CLK_PORT &= ~SED156X_SPI_CLK_MASK; \ + } while(0) + +#define SED156X_SPI_CLK_TOGGLE() (SED156X_SPI_CLK_PORT ^= SED156X_SPI_CLK_MASK) + +#define SED156X_SPI_BIT_DELAY() /* no delay */ + +#define SED156X_CS(x) \ + do { \ + if (x) \ + SED156X_CS_PORT |= SED156X_CS_MASK; \ + else \ + SED156X_CS_PORT &= ~SED156X_CS_MASK; \ + } while(0) + +#define SED156X_A0(x) \ + do { \ + if (x) \ + SED156X_A0_PORT |= SED156X_A0_MASK; \ + else \ + SED156X_A0_PORT &= ~SED156X_A0_MASK; \ + } while(0) + +/**************************************************************************************/ + +/*** LCD Commands ***/ + +#define LCD_ON 0xAF /* Display ON */ +#define LCD_OFF 0xAE /* Display OFF */ +#define LCD_LADDR 0x40 /* Display start line set + (6-bit) address */ +#define LCD_PADDR 0xB0 /* Page address set + (4-bit) page */ +#define LCD_CADRH 0x10 /* Column address set upper + (4-bit) column hi */ +#define LCD_CADRL 0x00 /* Column address set lower + (4-bit) column lo */ +#define LCD_ADC_NRM 0xA0 /* ADC select Normal */ +#define LCD_ADC_REV 0xA1 /* ADC select Reverse */ +#define LCD_DSP_NRM 0xA6 /* LCD display Normal */ +#define LCD_DSP_REV 0xA7 /* LCD display Reverse */ +#define LCD_DPT_NRM 0xA4 /* Display all points Normal */ +#define LCD_DPT_ALL 0xA5 /* Display all points ON */ +#define LCD_BIAS9 0xA2 /* LCD bias set 1/9 */ +#define LCD_BIAS7 0xA3 /* LCD bias set 1/7 */ +#define LCD_CAINC 0xE0 /* Read/modify/write */ +#define LCD_CAEND 0xEE /* End */ +#define LCD_RESET 0xE2 /* Reset */ +#define LCD_C_NRM 0xC0 /* Common output mode select Normal direction */ +#define LCD_C_RVS 0xC8 /* Common output mode select Reverse direction */ +#define LCD_PWRMD 0x28 /* Power control set + (3-bit) mode */ +#define LCD_RESRT 0x20 /* V5 v. reg. int. resistor ratio set + (3-bit) ratio */ +#define LCD_EVSET 0x81 /* Electronic volume mode set + byte = (6-bit) volume */ +#define LCD_SIOFF 0xAC /* Static indicator OFF */ +#define LCD_SION 0xAD /* Static indicator ON + byte = (2-bit) mode */ +#define LCD_NOP 0xE3 /* NOP */ +#define LCD_TEST 0xF0 /* Test/Test mode reset (Note: *DO NOT USE*) */ + +/*------------------------------------------------------------------------------- + Compound commands + ------------------------------------------------------------------------------- + Command Description Commands + ---------- ------------------------ ------------------------------------- + POWS_ON POWER SAVER ON command LCD_OFF, LCD_D_ALL + POWS_OFF POWER SAVER OFF command LCD_D_NRM + SLEEPON SLEEP mode LCD_SIOFF, POWS_ON + SLEEPOFF SLEEP mode cancel LCD_D_NRM, LCD_SION, LCD_SIS_??? + STDBYON STAND BY mode LCD_SION, POWS_ON + STDBYOFF STAND BY mode cancel LCD_D_NRM + -------------------------------------------------------------------------------*/ + +/*** LCD various parameters ***/ +#define LCD_PPB 8 /* Pixels per byte (display is B/W, 1 bit per pixel) */ + +/*** LCD Status byte masks ***/ +#define LCD_S_BUSY 0x80 /* Status Read - BUSY mask */ +#define LCD_S_ADC 0x40 /* Status Read - ADC mask */ +#define LCD_S_ONOFF 0x20 /* Status Read - ON/OFF mask */ +#define LCD_S_RESET 0x10 /* Status Read - RESET mask */ + +/*** LCD commands parameter masks ***/ +#define LCD_M_LADDR 0x3F /* Display start line (6-bit) address mask */ +#define LCD_M_PADDR 0x0F /* Page address (4-bit) page mask */ +#define LCD_M_CADRH 0x0F /* Column address upper (4-bit) column hi mask */ +#define LCD_M_CADRL 0x0F /* Column address lower (4-bit) column lo mask */ +#define LCD_M_PWRMD 0x07 /* Power control (3-bit) mode mask */ +#define LCD_M_RESRT 0x07 /* V5 v. reg. int. resistor ratio (3-bit) ratio mask */ +#define LCD_M_EVSET 0x3F /* Electronic volume mode byte (6-bit) volume mask */ +#define LCD_M_SION 0x03 /* Static indicator ON (2-bit) mode mask */ + +/*** LCD Power control cirquits control masks ***/ +#define LCD_PWRBSTR 0x04 /* Power control mode - Booster cirquit ON */ +#define LCD_PWRVREG 0x02 /* Power control mode - Voltage regulator cirquit ON */ +#define LCD_PWRVFOL 0x01 /* Power control mode - Voltage follower cirquit ON */ + +/*** LCD Static indicator states ***/ +#define LCD_SIS_OFF 0x00 /* Static indicator register set - OFF state */ +#define LCD_SIS_BL 0x01 /* Static indicator register set - 1s blink state */ +#define LCD_SIS_RBL 0x02 /* Static indicator register set - .5s rapid blink state */ +#define LCD_SIS_ON 0x03 /* Static indicator register set - constantly on state */ + +/*** LCD functions special parameters (commands) ***/ +#define LCD_PREVP 0x80 /* Page number for moving to previous */ +#define LCD_NEXTP 0x81 /* or next page */ +#define LCD_ERR_P 0xFF /* Error in page number */ + +/*** LCD initialization settings ***/ +#define LCD_BIAS LCD_BIAS9 /* Bias: 1/9 */ +#define LCD_ADCMODE LCD_ADC_NRM /* ADC mode: normal */ +#define LCD_COMDIR LCD_C_NRM /* Common output mode: normal */ +#define LCD_RRATIO 0 /* Resistor ratio: 0 */ +#define LCD_CNTRST 0x1C /* electronic volume: 1Ch */ +#define LCD_POWERM (LCD_PWRBSTR | LCD_PWRVREG | LCD_PWRVFOL) /* Power mode: All on */ + +/**************************************************************************************/ + +static inline unsigned int sed156x_transfer(unsigned int val) +{ + unsigned int rx; + int b; + + rx = 0; b = 8; + while (--b >= 0) { + SED156X_SPI_TXD(val & 0x80); + val <<= 1; + SED156X_SPI_CLK_TOGGLE(); + SED156X_SPI_BIT_DELAY(); + rx <<= 1; + if (SED156X_SPI_RXD()) + rx |= 1; + SED156X_SPI_CLK_TOGGLE(); + SED156X_SPI_BIT_DELAY(); + } + + return rx; +} + +unsigned int sed156x_data_transfer(unsigned int val) +{ + unsigned int rx; + + SED156X_SPI_CLK(1); + SED156X_CS(0); + SED156X_A0(1); + + rx = sed156x_transfer(val); + + SED156X_CS(1); + + return rx; +} + +void sed156x_data_block_transfer(const u8 *p, int size) +{ + SED156X_SPI_CLK(1); + SED156X_CS(0); + SED156X_A0(1); + + while (--size >= 0) + sed156x_transfer(*p++); + + SED156X_CS(1); +} + +unsigned int sed156x_cmd_transfer(unsigned int val) +{ + unsigned int rx; + + SED156X_SPI_CLK(1); + SED156X_CS(0); + SED156X_A0(0); + + rx = sed156x_transfer(val); + + SED156X_CS(1); + SED156X_A0(1); + + return rx; +} + +/******************************************************************************/ + +static u8 hw_screen[LCD_PAGES][LCD_COLUMNS]; +static u8 last_hw_screen[LCD_PAGES][LCD_COLUMNS]; +static u8 sw_screen[LCD_BYTE_WIDTH * LCD_HEIGHT]; + +void sed156x_sync(void) +{ + int i, j, last_page; + u8 *d; + const u8 *s, *e, *b, *r; + u8 v0, v1, v2, v3, v4, v5, v6, v7; + + /* copy and rotate sw_screen to hw_screen */ + for (i = 0; i < LCD_HEIGHT / 8; i++) { + + d = &hw_screen[i][0]; + s = &sw_screen[LCD_BYTE_WIDTH * 8 * i + LCD_BYTE_WIDTH - 1]; + + for (j = 0; j < LCD_WIDTH / 8; j++) { + + v0 = s[0 * LCD_BYTE_WIDTH]; + v1 = s[1 * LCD_BYTE_WIDTH]; + v2 = s[2 * LCD_BYTE_WIDTH]; + v3 = s[3 * LCD_BYTE_WIDTH]; + v4 = s[4 * LCD_BYTE_WIDTH]; + v5 = s[5 * LCD_BYTE_WIDTH]; + v6 = s[6 * LCD_BYTE_WIDTH]; + v7 = s[7 * LCD_BYTE_WIDTH]; + + d[0] = ((v7 & 0x01) << 7) | + ((v6 & 0x01) << 6) | + ((v5 & 0x01) << 5) | + ((v4 & 0x01) << 4) | + ((v3 & 0x01) << 3) | + ((v2 & 0x01) << 2) | + ((v1 & 0x01) << 1) | + (v0 & 0x01) ; + + d[1] = ((v7 & 0x02) << 6) | + ((v6 & 0x02) << 5) | + ((v5 & 0x02) << 4) | + ((v4 & 0x02) << 3) | + ((v3 & 0x02) << 2) | + ((v2 & 0x02) << 1) | + ((v1 & 0x02) << 0) | + ((v0 & 0x02) >> 1) ; + + d[2] = ((v7 & 0x04) << 5) | + ((v6 & 0x04) << 4) | + ((v5 & 0x04) << 3) | + ((v4 & 0x04) << 2) | + ((v3 & 0x04) << 1) | + (v2 & 0x04) | + ((v1 & 0x04) >> 1) | + ((v0 & 0x04) >> 2) ; + + d[3] = ((v7 & 0x08) << 4) | + ((v6 & 0x08) << 3) | + ((v5 & 0x08) << 2) | + ((v4 & 0x08) << 1) | + (v3 & 0x08) | + ((v2 & 0x08) >> 1) | + ((v1 & 0x08) >> 2) | + ((v0 & 0x08) >> 3) ; + + d[4] = ((v7 & 0x10) << 3) | + ((v6 & 0x10) << 2) | + ((v5 & 0x10) << 1) | + (v4 & 0x10) | + ((v3 & 0x10) >> 1) | + ((v2 & 0x10) >> 2) | + ((v1 & 0x10) >> 3) | + ((v0 & 0x10) >> 4) ; + + d[5] = ((v7 & 0x20) << 2) | + ((v6 & 0x20) << 1) | + (v5 & 0x20) | + ((v4 & 0x20) >> 1) | + ((v3 & 0x20) >> 2) | + ((v2 & 0x20) >> 3) | + ((v1 & 0x20) >> 4) | + ((v0 & 0x20) >> 5) ; + + d[6] = ((v7 & 0x40) << 1) | + (v6 & 0x40) | + ((v5 & 0x40) >> 1) | + ((v4 & 0x40) >> 2) | + ((v3 & 0x40) >> 3) | + ((v2 & 0x40) >> 4) | + ((v1 & 0x40) >> 5) | + ((v0 & 0x40) >> 6) ; + + d[7] = (v7 & 0x80) | + ((v6 & 0x80) >> 1) | + ((v5 & 0x80) >> 2) | + ((v4 & 0x80) >> 3) | + ((v3 & 0x80) >> 4) | + ((v2 & 0x80) >> 5) | + ((v1 & 0x80) >> 6) | + ((v0 & 0x80) >> 7) ; + + d += 8; + s--; + } + } + + /* and now output only the differences */ + for (i = 0; i < LCD_PAGES; i++) { + + b = &hw_screen[i][0]; + e = &hw_screen[i][LCD_COLUMNS]; + + d = &last_hw_screen[i][0]; + s = b; + + last_page = -1; + + /* update only the differences */ + do { + while (s < e && *s == *d) { + s++; + d++; + } + if (s == e) + break; + r = s; + while (s < e && *s != *d) + *d++ = *s++; + + j = r - b; + + if (i != last_page) { + sed156x_cmd_transfer(LCD_PADDR | i); + last_page = i; + } + + sed156x_cmd_transfer(LCD_CADRH | ((j >> 4) & 0x0F)); + sed156x_cmd_transfer(LCD_CADRL | (j & 0x0F)); + sed156x_data_block_transfer(r, s - r); + + } while (s < e); + } + +/******** + for (i = 0; i < LCD_PAGES; i++) { + sed156x_cmd_transfer(LCD_PADDR | i); + sed156x_cmd_transfer(LCD_CADRH | 0); + sed156x_cmd_transfer(LCD_CADRL | 0); + sed156x_data_block_transfer(&hw_screen[i][0], LCD_COLUMNS); + } + memcpy(last_hw_screen, hw_screen, sizeof(last_hw_screen)); +********/ +} + +void sed156x_clear(void) +{ + memset(sw_screen, 0, sizeof(sw_screen)); +} + +void sed156x_output_at(int x, int y, const char *str, int size) +{ + int i, j; + u8 *p; + const u8 *s; + + if ((unsigned int)y >= LCD_TEXT_HEIGHT || (unsigned int)x >= LCD_TEXT_WIDTH) + return; + + p = &sw_screen[y * VIDEO_FONT_HEIGHT * LCD_BYTE_WIDTH + x * VIDEO_FONT_BYTE_WIDTH]; + + while (--size >= 0) { + + s = &video_fontdata[((int)*str++ & 0xff) * VIDEO_FONT_BYTE_WIDTH * VIDEO_FONT_HEIGHT]; + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) { + for (j = 0; j < VIDEO_FONT_BYTE_WIDTH; j++) + *p++ = *s++; + p += LCD_BYTE_WIDTH - VIDEO_FONT_BYTE_WIDTH; + } + p -= (LCD_BYTE_LINESZ - VIDEO_FONT_BYTE_WIDTH); + + if (x >= LCD_TEXT_WIDTH) + break; + x++; + } +} + +void sed156x_reverse_at(int x, int y, int size) +{ + int i, j; + u8 *p; + + if ((unsigned int)y >= LCD_TEXT_HEIGHT || (unsigned int)x >= LCD_TEXT_WIDTH) + return; + + p = &sw_screen[y * VIDEO_FONT_HEIGHT * LCD_BYTE_WIDTH + x * VIDEO_FONT_BYTE_WIDTH]; + + while (--size >= 0) { + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) { + for (j = 0; j < VIDEO_FONT_BYTE_WIDTH; j++, p++) + *p = ~*p; + p += LCD_BYTE_WIDTH - VIDEO_FONT_BYTE_WIDTH; + } + p -= (LCD_BYTE_LINESZ - VIDEO_FONT_BYTE_WIDTH); + + if (x >= LCD_TEXT_WIDTH) + break; + x++; + } +} + +void sed156x_scroll_line(void) +{ + memmove(&sw_screen[0], + &sw_screen[LCD_BYTE_LINESZ], + LCD_BYTE_WIDTH * (LCD_HEIGHT - VIDEO_FONT_HEIGHT)); +} + +void sed156x_scroll(int dx, int dy) +{ + u8 *p1 = NULL, *p2 = NULL, *p3 = NULL; /* pacify gcc */ + int adx, ady, i, sz; + + adx = dx > 0 ? dx : -dx; + ady = dy > 0 ? dy : -dy; + + /* overscroll? erase everything */ + if (adx >= LCD_TEXT_WIDTH || ady >= LCD_TEXT_HEIGHT) { + memset(sw_screen, 0, sizeof(sw_screen)); + return; + } + + sz = LCD_BYTE_LINESZ * ady; + if (dy > 0) { + p1 = &sw_screen[0]; + p2 = &sw_screen[sz]; + p3 = &sw_screen[LCD_BYTE_WIDTH * LCD_HEIGHT - sz]; + } else if (dy < 0) { + p1 = &sw_screen[sz]; + p2 = &sw_screen[0]; + p3 = &sw_screen[0]; + } + + if (ady > 0) { + memmove(p1, p2, LCD_BYTE_WIDTH * LCD_HEIGHT - sz); + memset(p3, 0, sz); + } + + sz = VIDEO_FONT_BYTE_WIDTH * adx; + if (dx > 0) { + p1 = &sw_screen[0]; + p2 = &sw_screen[0] + sz; + p3 = &sw_screen[0] + LCD_BYTE_WIDTH - sz; + } else if (dx < 0) { + p1 = &sw_screen[0] + sz; + p2 = &sw_screen[0]; + p3 = &sw_screen[0]; + } + + /* xscroll */ + if (adx > 0) { + for (i = 0; i < LCD_HEIGHT; i++) { + memmove(p1, p2, LCD_BYTE_WIDTH - sz); + memset(p3, 0, sz); + p1 += LCD_BYTE_WIDTH; + p2 += LCD_BYTE_WIDTH; + p3 += LCD_BYTE_WIDTH; + } + } +} + +void sed156x_init(void) +{ + int i; + + SED156X_CS(1); + SED156X_A0(1); + + /* Send initialization commands to the LCD */ + sed156x_cmd_transfer(LCD_OFF); /* Turn display OFF */ + sed156x_cmd_transfer(LCD_BIAS); /* set the LCD Bias, */ + sed156x_cmd_transfer(LCD_ADCMODE); /* ADC mode, */ + sed156x_cmd_transfer(LCD_COMDIR); /* common output mode, */ + sed156x_cmd_transfer(LCD_RESRT | LCD_RRATIO); /* resistor ratio, */ + sed156x_cmd_transfer(LCD_EVSET); /* electronic volume, */ + sed156x_cmd_transfer(LCD_CNTRST); + sed156x_cmd_transfer(LCD_PWRMD | LCD_POWERM); /* and power mode */ + sed156x_cmd_transfer(LCD_PADDR | 0); /* cursor home */ + sed156x_cmd_transfer(LCD_CADRH | 0); + sed156x_cmd_transfer(LCD_CADRL | 0); + sed156x_cmd_transfer(LCD_LADDR | 0); /* and display start line */ + sed156x_cmd_transfer(LCD_DSP_NRM); /* LCD display Normal */ + + /* clear everything */ + memset(sw_screen, 0, sizeof(sw_screen)); + memset(hw_screen, 0, sizeof(hw_screen)); + memset(last_hw_screen, 0, sizeof(last_hw_screen)); + + for (i = 0; i < LCD_PAGES; i++) { + sed156x_cmd_transfer(LCD_PADDR | i); + sed156x_cmd_transfer(LCD_CADRH | 0); + sed156x_cmd_transfer(LCD_CADRL | 0); + sed156x_data_block_transfer(&hw_screen[i][0], LCD_COLUMNS); + } + + sed156x_clear(); + sed156x_sync(); + sed156x_cmd_transfer(LCD_ON); /* Turn display ON */ +} diff --git a/u-boot/drivers/video/sm501.c b/u-boot/drivers/video/sm501.c new file mode 100644 index 0000000..6a1e010 --- /dev/null +++ b/u-boot/drivers/video/sm501.c @@ -0,0 +1,240 @@ +/* + * (C) Copyright 2002 + * Stäubli Faverges - <www.staubli.com> + * Pierre AUBERT p.aubert@staubli.com + * + * (C) Copyright 2005 + * Martin Krause TQ-Systems GmbH martin.krause@tqs.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * Basic video support for SMI SM501 "Voyager" graphic controller + */ + +#include <common.h> + +#include <asm/io.h> +#include <video_fb.h> +#include <sm501.h> + +#define read8(ptrReg) \ + *(volatile unsigned char *)(sm501.isaBase + ptrReg) + +#define write8(ptrReg,value) \ + *(volatile unsigned char *)(sm501.isaBase + ptrReg) = value + +#define read16(ptrReg) \ + (*(volatile unsigned short *)(sm501.isaBase + ptrReg)) + +#define write16(ptrReg,value) \ + (*(volatile unsigned short *)(sm501.isaBase + ptrReg) = value) + +#define read32(ptrReg) \ + (*(volatile unsigned int *)(sm501.isaBase + ptrReg)) + +#define write32(ptrReg, value) \ + (*(volatile unsigned int *)(sm501.isaBase + ptrReg) = value) + +GraphicDevice sm501; + +void write_be32(int off, unsigned int val) +{ + out_be32((unsigned __iomem *)(sm501.isaBase + off), val); +} + +void write_le32(int off, unsigned int val) +{ + out_le32((unsigned __iomem *)(sm501.isaBase + off), val); +} + +void (*write_reg32)(int off, unsigned int val) = write_be32; + +/*----------------------------------------------------------------------------- + * SmiSetRegs -- + *----------------------------------------------------------------------------- + */ +static void SmiSetRegs (void) +{ + /* + * The content of the chipset register depends on the board (clocks, + * ...) + */ + const SMI_REGS *preg = board_get_regs (); + while (preg->Index) { + write_reg32 (preg->Index, preg->Value); + /* + * Insert a delay between + */ + udelay (1000); + preg ++; + } +} + +#ifdef CONFIG_VIDEO_SM501_PCI +static struct pci_device_id sm501_pci_tbl[] = { + { PCI_VENDOR_ID_SMI, PCI_DEVICE_ID_SMI_501 }, + {} +}; +#endif + +/* + * We do not enforce board code to provide empty/unused + * functions for this driver and define weak default + * functions here. + */ +unsigned int __board_video_init (void) +{ + return 0; +} + +unsigned int board_video_init (void) + __attribute__((weak, alias("__board_video_init"))); + +unsigned int __board_video_get_fb (void) +{ + return 0; +} + +unsigned int board_video_get_fb (void) + __attribute__((weak, alias("__board_video_get_fb"))); + +void __board_validate_screen (unsigned int base) +{ +} + +void board_validate_screen (unsigned int base) + __attribute__((weak, alias("__board_validate_screen"))); + +/*----------------------------------------------------------------------------- + * video_hw_init -- + *----------------------------------------------------------------------------- + */ +void *video_hw_init (void) +{ +#ifdef CONFIG_VIDEO_SM501_PCI + unsigned int pci_mem_base, pci_mmio_base; + unsigned int id; + unsigned short device_id; + pci_dev_t devbusfn; + int mem; +#endif + unsigned int *vm, i; + + memset (&sm501, 0, sizeof (GraphicDevice)); + +#ifdef CONFIG_VIDEO_SM501_PCI + printf("Video: "); + + /* Look for SM501/SM502 chips */ + devbusfn = pci_find_devices(sm501_pci_tbl, 0); + if (devbusfn < 0) { + printf ("PCI Controller not found.\n"); + goto not_pci; + } + + /* Setup */ + pci_write_config_dword (devbusfn, PCI_COMMAND, + (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)); + pci_read_config_word (devbusfn, PCI_DEVICE_ID, &device_id); + pci_read_config_dword (devbusfn, PCI_REVISION_ID, &id); + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base); + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_1, &pci_mmio_base); + sm501.frameAdrs = pci_mem_to_phys (devbusfn, pci_mem_base); + sm501.isaBase = pci_mem_to_phys (devbusfn, pci_mmio_base); + + if (sm501.isaBase) + write_reg32 = write_le32; + + mem = in_le32 ((unsigned __iomem *)(sm501.isaBase + 0x10)); + mem = (mem & 0x0000e000) >> 13; + switch (mem) { + case 1: + mem = 8; + break; + case 2: + mem = 16; + break; + case 3: + mem = 32; + break; + case 4: + mem = 64; + break; + case 5: + mem = 2; + break; + case 0: + default: + mem = 4; + } + printf ("PCI SM50%d %d MB\n", ((id & 0xff) == 0xC0) ? 2 : 1, mem); +not_pci: +#endif + /* + * Initialization of the access to the graphic chipset Retreive base + * address of the chipset (see board/RPXClassic/eccx.c) + */ + if (!sm501.isaBase) { + sm501.isaBase = board_video_init (); + if (!sm501.isaBase) + return NULL; + } + + if (!sm501.frameAdrs) { + sm501.frameAdrs = board_video_get_fb (); + if (!sm501.frameAdrs) + return NULL; + } + + sm501.winSizeX = board_get_width (); + sm501.winSizeY = board_get_height (); + +#if defined(CONFIG_VIDEO_SM501_8BPP) + sm501.gdfIndex = GDF__8BIT_INDEX; + sm501.gdfBytesPP = 1; + +#elif defined(CONFIG_VIDEO_SM501_16BPP) + sm501.gdfIndex = GDF_16BIT_565RGB; + sm501.gdfBytesPP = 2; + +#elif defined(CONFIG_VIDEO_SM501_32BPP) + sm501.gdfIndex = GDF_32BIT_X888RGB; + sm501.gdfBytesPP = 4; +#else +#error Unsupported SM501 BPP +#endif + + sm501.memSize = sm501.winSizeX * sm501.winSizeY * sm501.gdfBytesPP; + + /* Load Smi registers */ + SmiSetRegs (); + + /* (see board/RPXClassic/RPXClassic.c) */ + board_validate_screen (sm501.isaBase); + + /* Clear video memory */ + i = sm501.memSize/4; + vm = (unsigned int *)sm501.frameAdrs; + while(i--) + *vm++ = 0; + + return (&sm501); +} diff --git a/u-boot/drivers/video/smiLynxEM.c b/u-boot/drivers/video/smiLynxEM.c new file mode 100644 index 0000000..2001e9c --- /dev/null +++ b/u-boot/drivers/video/smiLynxEM.c @@ -0,0 +1,854 @@ +/* + * (C) Copyright 1997-2002 ELTEC Elektronik AG + * Frank Gottschling <fgottschling@eltec.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* + * smiLynxEM.c + * + * Silicon Motion graphic interface for sm810/sm710/sm712 accelerator + * + * modification history + * -------------------- + * 04-18-2002 Rewritten for U-Boot <fgottschling@eltec.de>. + * + * 18-03-2004 - Unify videomodes handling with the ct69000 + * - The video output can be set via the variable "videoout" + * in the environment. + * videoout=1 output on LCD + * videoout=2 output on CRT (default value) + * <p.aubert@staubli.com> + */ + +#include <common.h> + +#include <pci.h> +#include <video_fb.h> +#include "videomodes.h" +/* + * Export Graphic Device + */ +GraphicDevice smi; + +/* + * SMI 710/712 have 4MB internal RAM; SMI 810 2MB internal + 2MB external + */ +#define VIDEO_MEM_SIZE 0x400000 + + +/* + * ISA mapped regs + */ +#define SMI_INDX_C4 (pGD->isaBase + 0x03c4) /* index reg */ +#define SMI_DATA_C5 (pGD->isaBase + 0x03c5) /* data reg */ +#define SMI_INDX_D4 (pGD->isaBase + 0x03d4) /* index reg */ +#define SMI_DATA_D5 (pGD->isaBase + 0x03d5) /* data reg */ +#define SMI_ISR1 (pGD->isaBase + 0x03ca) +#define SMI_INDX_CE (pGD->isaBase + 0x03ce) /* index reg */ +#define SMI_DATA_CF (pGD->isaBase + 0x03cf) /* data reg */ +#define SMI_LOCK_REG (pGD->isaBase + 0x03c3) /* unlock/lock ext crt reg */ +#define SMI_MISC_REG (pGD->isaBase + 0x03c2) /* misc reg */ +#define SMI_LUT_MASK (pGD->isaBase + 0x03c6) /* lut mask reg */ +#define SMI_LUT_START (pGD->isaBase + 0x03c8) /* lut start index */ +#define SMI_LUT_RGB (pGD->isaBase + 0x03c9) /* lut colors auto incr.*/ +#define SMI_INDX_ATTR (pGD->isaBase + 0x03c0) /* attributes index reg */ + +/* + * Video processor control + */ +typedef struct { + unsigned int control; + unsigned int colorKey; + unsigned int colorKeyMask; + unsigned int start; + unsigned short offset; + unsigned short width; + unsigned int fifoPrio; + unsigned int fifoERL; + unsigned int YUVtoRGB; +} SmiVideoProc; + +/* + * Video window control + */ +typedef struct { + unsigned short top; + unsigned short left; + unsigned short bottom; + unsigned short right; + unsigned int srcStart; + unsigned short width; + unsigned short offset; + unsigned char hStretch; + unsigned char vStretch; +} SmiVideoWin; + +/* + * Capture port control + */ +typedef struct { + unsigned int control; + unsigned short topClip; + unsigned short leftClip; + unsigned short srcHeight; + unsigned short srcWidth; + unsigned int srcBufStart1; + unsigned int srcBufStart2; + unsigned short srcOffset; + unsigned short fifoControl; +} SmiCapturePort; + + +/* + * Register values for common video modes + */ +static char SMI_SCR[] = { + /* all modes */ + 0x10, 0xff, 0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x15, 0x90, + 0x17, 0x20, 0x18, 0xb1, 0x19, 0x00, +}; +static char SMI_EXT_CRT[] = { + 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, + 0x36, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00, +}; +static char SMI_ATTR [] = { + 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x41, 0x11, 0x00, + 0x12, 0x0f, 0x13, 0x00, 0x14, 0x00, +}; +static char SMI_GCR[18] = { + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x40, + 0x06, 0x05, 0x07, 0x0f, 0x08, 0xff, +}; +static char SMI_SEQR[] = { + 0x00, 0x00, 0x01, 0x01, 0x02, 0x0f, 0x03, 0x03, 0x04, 0x0e, 0x00, 0x03, +}; +static char SMI_PCR [] = { + 0x20, 0x04, 0x21, 0x30, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, +}; +static char SMI_MCR[] = { + 0x60, 0x01, 0x61, 0x00, +#ifdef CONFIG_HMI1001 + 0x62, 0x74, /* Memory type is not configured by pins on HMI1001 */ +#endif +}; + +static char SMI_HCR[] = { + 0x80, 0xff, 0x81, 0x07, 0x82, 0x00, 0x83, 0xff, 0x84, 0xff, 0x88, 0x00, + 0x89, 0x02, 0x8a, 0x80, 0x8b, 0x01, 0x8c, 0xff, 0x8d, 0x00, +}; + + +/******************************************************************************* + * + * Write SMI ISA register + */ +static void smiWrite (unsigned short index, char reg, char val) +{ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + + out8 ((pGD->isaBase + index), reg); + out8 ((pGD->isaBase + index + 1), val); +} + +/******************************************************************************* + * + * Write a table of SMI ISA register + */ +static void smiLoadRegs ( + unsigned int iReg, + unsigned int dReg, + char *regTab, + unsigned int tabSize + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + register int i; + + for (i=0; i<tabSize; i+=2) { + if (iReg == SMI_INDX_ATTR) { + /* Reset the Flip Flop */ + in8 (SMI_ISR1); + out8 (iReg, regTab[i]); + out8 (iReg, regTab[i+1]); + } else { + out8 (iReg, regTab[i]); + out8 (dReg, regTab[i+1]); + } + } +} + +/******************************************************************************* + * + * Init capture port registers + */ +static void smiInitCapturePort (void) +{ + SmiCapturePort smiCP = { 0x01400600, 0x30, 0x40, 480, 640, 0, 0, 2560, 6 }; + register GraphicDevice *pGD = (GraphicDevice *)&smi; + register SmiCapturePort *pCP = (SmiCapturePort *)&smiCP; + + out32r ((pGD->cprBase + 0x0004), ((pCP->topClip<<16) | pCP->leftClip)); + out32r ((pGD->cprBase + 0x0008), ((pCP->srcHeight<<16) | pCP->srcWidth)); + out32r ((pGD->cprBase + 0x000c), pCP->srcBufStart1/8); + out32r ((pGD->cprBase + 0x0010), pCP->srcBufStart2/8); + out32r ((pGD->cprBase + 0x0014), pCP->srcOffset/8); + out32r ((pGD->cprBase + 0x0018), pCP->fifoControl); + out32r ((pGD->cprBase + 0x0000), pCP->control); +} + + +/******************************************************************************* + * + * Init video processor registers + */ +static void smiInitVideoProcessor (void) +{ + SmiVideoProc smiVP = { 0x100000, 0, 0, 0, 0, 1600, 0x1200543, 4, 0xededed }; + SmiVideoWin smiVW = { 0, 0, 599, 799, 0, 1600, 0, 0, 0 }; + register GraphicDevice *pGD = (GraphicDevice *)&smi; + register SmiVideoProc *pVP = (SmiVideoProc *)&smiVP; + register SmiVideoWin *pVWin = (SmiVideoWin *)&smiVW; + + pVP->width = pGD->plnSizeX * pGD->gdfBytesPP; + pVP->control |= pGD->gdfIndex << 16; + pVWin->bottom = pGD->winSizeY - 1; + pVWin->right = pGD->winSizeX - 1; + pVWin->width = pVP->width; + + /* color key */ + out32r ((pGD->vprBase + 0x0004), pVP->colorKey); + + /* color key mask */ + out32r ((pGD->vprBase + 0x0008), pVP->colorKeyMask); + + /* data src start adrs */ + out32r ((pGD->vprBase + 0x000c), pVP->start / 8); + + /* data width and offset */ + out32r ((pGD->vprBase + 0x0010), + ((pVP->offset / 8 * pGD->gdfBytesPP) << 16) | + (pGD->plnSizeX / 8 * pGD->gdfBytesPP)); + + /* video window 1 */ + out32r ((pGD->vprBase + 0x0014), + ((pVWin->top << 16) | pVWin->left)); + + out32r ((pGD->vprBase + 0x0018), + ((pVWin->bottom << 16) | pVWin->right)); + + out32r ((pGD->vprBase + 0x001c), pVWin->srcStart / 8); + + out32r ((pGD->vprBase + 0x0020), + (((pVWin->offset / 8) << 16) | (pVWin->width / 8))); + + out32r ((pGD->vprBase + 0x0024), + (((pVWin->hStretch) << 8) | pVWin->vStretch)); + + /* video window 2 */ + out32r ((pGD->vprBase + 0x0028), + ((pVWin->top << 16) | pVWin->left)); + + out32r ((pGD->vprBase + 0x002c), + ((pVWin->bottom << 16) | pVWin->right)); + + out32r ((pGD->vprBase + 0x0030), + pVWin->srcStart / 8); + + out32r ((pGD->vprBase + 0x0034), + (((pVWin->offset / 8) << 16) | (pVWin->width / 8))); + + out32r ((pGD->vprBase + 0x0038), + (((pVWin->hStretch) << 8) | pVWin->vStretch)); + + /* fifo prio control */ + out32r ((pGD->vprBase + 0x0054), pVP->fifoPrio); + + /* fifo empty request levell */ + out32r ((pGD->vprBase + 0x0058), pVP->fifoERL); + + /* conversion constant */ + out32r ((pGD->vprBase + 0x005c), pVP->YUVtoRGB); + + /* vpr control word */ + out32r ((pGD->vprBase + 0x0000), pVP->control); +} + +/****************************************************************************** + * + * Init drawing engine registers + */ +static void smiInitDrawingEngine (void) +{ + GraphicDevice *pGD = (GraphicDevice *)&smi; + unsigned int val; + + /* don't start now */ + out32r ((pGD->dprBase + 0x000c), 0x000f0000); + + /* set rop2 to copypen */ + val = 0xffff3ff0 & in32r ((pGD->dprBase + 0x000c)); + out32r ((pGD->dprBase + 0x000c), (val | 0x8000 | 0x0c)); + + /* set clip rect */ + out32r ((pGD->dprBase + 0x002c), 0); + out32r ((pGD->dprBase + 0x0030), + ((pGD->winSizeY<<16) | pGD->winSizeX * pGD->gdfBytesPP )); + + /* src row pitch */ + val = 0xffff0000 & (in32r ((pGD->dprBase + 0x0010))); + out32r ((pGD->dprBase + 0x0010), + (val | pGD->plnSizeX * pGD->gdfBytesPP)); + + /* dst row pitch */ + val = 0x0000ffff & (in32r ((pGD->dprBase + 0x0010))); + out32r ((pGD->dprBase + 0x0010), + (((pGD->plnSizeX * pGD->gdfBytesPP)<<16) | val)); + + /* window width src/dst */ + out32r ((pGD->dprBase + 0x003c), + (((pGD->plnSizeX * pGD->gdfBytesPP & 0x0fff)<<16) | + (pGD->plnSizeX * pGD->gdfBytesPP & 0x0fff))); + out16r ((pGD->dprBase + 0x001e), 0x0000); + + /* src base adrs */ + out32r ((pGD->dprBase + 0x0040), + (((pGD->frameAdrs/8) & 0x000fffff))); + + /* dst base adrs */ + out32r ((pGD->dprBase + 0x0044), + (((pGD->frameAdrs/8) & 0x000fffff))); + + /* foreground color */ + out32r ((pGD->dprBase + 0x0014), pGD->fg); + + /* background color */ + out32r ((pGD->dprBase + 0x0018), pGD->bg); + + /* xcolor */ + out32r ((pGD->dprBase + 0x0020), 0x00ffffff); + + /* xcolor mask */ + out32r ((pGD->dprBase + 0x0024), 0x00ffffff); + + /* bit mask */ + out32r ((pGD->dprBase + 0x0028), 0x00ffffff); + + /* load mono pattern */ + out32r ((pGD->dprBase + 0x0034), 0); + out32r ((pGD->dprBase + 0x0038), 0); +} + +static struct pci_device_id supported[] = { + { PCI_VENDOR_ID_SMI, PCI_DEVICE_ID_SMI_710 }, + { PCI_VENDOR_ID_SMI, PCI_DEVICE_ID_SMI_712 }, + { PCI_VENDOR_ID_SMI, PCI_DEVICE_ID_SMI_810 }, + { } +}; + +/*****************************************************************************/ +static void smiLoadMsr (struct ctfb_res_modes *mode) +{ + unsigned char h_synch_high, v_synch_high; + register GraphicDevice *pGD = (GraphicDevice *)&smi; + + h_synch_high = (mode->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 0x40; /* horizontal Synch High active */ + v_synch_high = (mode->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 0x80; /* vertical Synch High active */ + out8 (SMI_MISC_REG, (h_synch_high | v_synch_high | 0x29)); + /* upper64K==0x20, CLC2select==0x08, RAMenable==0x02!(todo), CGA==0x01 + * Selects the upper 64KB page.Bit5=1 + * CLK2 (left reserved in standard VGA) Bit3|2=1|0 + * Disables CPU access to frame buffer. Bit1=0 + * Sets the I/O address decode for ST01, FCR, and all CR registers + * to the 3Dx I/O address range (CGA emulation). Bit0=1 + */ +} +/*****************************************************************************/ +static void smiLoadCrt (struct ctfb_res_modes *var, int bits_per_pixel) +{ + unsigned char cr[0x7a]; + int i; + unsigned int hd, hs, he, ht, hbs, hbe; /* Horizontal. */ + unsigned int vd, vs, ve, vt, vbs, vbe; /* vertical */ + unsigned int bpp, wd, dblscan, interlaced; + + const int LineCompare = 0x3ff; + unsigned int TextScanLines = 1; /* this is in fact a vertical zoom factor */ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + + /* Horizontal */ + hd = (var->xres) / 8; /* HDisp. */ + hs = (var->xres + var->right_margin) / 8; /* HsStrt */ + he = (var->xres + var->right_margin + var->hsync_len) / 8; /* HsEnd */ + ht = (var->left_margin + var->xres + var->right_margin + var->hsync_len) / 8; /* HTotal */ + /* Blank */ + hbs = hd; + hbe = 0; /* Blank end at 0 */ + + /* Vertical */ + vd = var->yres; /* VDisplay */ + vs = var->yres + var->lower_margin; /* VSyncStart */ + ve = var->yres + var->lower_margin + var->vsync_len; /* VSyncEnd */ + vt = var->upper_margin + var->yres + var->lower_margin + var->vsync_len; /* VTotal */ + vbs = vd; + vbe = 0; + + bpp = bits_per_pixel; + dblscan = (var->vmode & FB_VMODE_DOUBLE) ? 1 : 0; + interlaced = var->vmode & FB_VMODE_INTERLACED; + + + if (bpp == 15) + bpp = 16; + wd = var->xres * bpp / 64; /* double words per line */ + if (interlaced) { /* we divide all vertical timings, exept vd */ + vs >>= 1; + vbs >>= 1; + ve >>= 1; + vt >>= 1; + } + + memset (cr, 0, sizeof (cr)); + cr[0x00] = ht - 5; + cr[0x01] = hd - 1; + cr[0x02] = hbs - 1; + cr[0x03] = (hbe & 0x1F); + cr[0x04] = hs; + cr[0x05] = ((hbe & 0x20) << 2) | (he & 0x1f); + + cr[0x06] = (vt - 2) & 0xFF; + cr[0x07] = (((vt - 2) & 0x100) >> 8) + | (((vd - 1) & 0x100) >> 7) + | ((vs & 0x100) >> 6) + | (((vbs - 1) & 0x100) >> 5) + | ((LineCompare & 0x100) >> 4) + | (((vt - 2) & 0x200) >> 4) + | (((vd - 1) & 0x200) >> 3) + | ((vs & 0x200) >> 2); + + cr[0x30] = ((vt - 2) & 0x400) >> 7 + | (((vd - 1) & 0x400) >> 8) + | (((vbs - 1) & 0x400) >> 9) + | ((vs & 0x400) >> 10) + | (interlaced) ? 0x80 : 0; + + + cr[0x08] = 0x00; + cr[0x09] = (dblscan << 7) + | ((LineCompare & 0x200) >> 3) + | (((vbs - 1) & 0x200) >> 4) + | (TextScanLines - 1); + + cr[0x10] = vs & 0xff; /* VSyncPulseStart */ + cr[0x11] = (ve & 0x0f); + cr[0x12] = (vd - 1) & 0xff; /* LineCount */ + cr[0x13] = wd & 0xff; + cr[0x14] = 0x40; + cr[0x15] = (vbs - 1) & 0xff; + cr[0x16] = vbe & 0xff; + cr[0x17] = 0xe3; /* but it does not work */ + cr[0x18] = 0xff & LineCompare; + cr[0x22] = 0x00; /* todo? */ + + + /* now set the registers */ + for (i = 0; i <= 0x18; i++) { /*CR00 .. CR18 */ + smiWrite (SMI_INDX_D4, i, cr[i]); + } + i = 0x22; /*CR22 */ + smiWrite (SMI_INDX_D4, i, cr[i]); + i = 0x30; /*CR30 */ + smiWrite (SMI_INDX_D4, i, cr[i]); +} + +/*****************************************************************************/ +#define REF_FREQ 14318180 +#define PMIN 1 +#define PMAX 255 +#define QMIN 1 +#define QMAX 63 + +static unsigned int FindPQ (unsigned int freq, unsigned int *pp, unsigned int *pq) +{ + unsigned int n = QMIN, m = 0; + long long int L = 0, P = freq, Q = REF_FREQ, H = P >> 1; + long long int D = 0x7ffffffffffffffLL; + + for (n = QMIN; n <= QMAX; n++) { + m = PMIN; /* p/q ~ freq/ref -> p*ref-freq*q ~ 0 */ + L = P * n - m * Q; + while (L > 0 && m < PMAX) { + L -= REF_FREQ; /* difference is greater as 0 subtract fref */ + m++; /* and increment m */ + } + /* difference is less or equal than 0 or m > maximum */ + if (m > PMAX) + break; /* no solution: if we increase n we get the same situation */ + /* L is <= 0 now */ + if (-L > H && m > PMIN) { /* if difference > the half fref */ + L += REF_FREQ; /* we take the situation before */ + m--; /* because its closer to 0 */ + } + L = (L < 0) ? -L : +L; /* absolute value */ + if (D < L) /* if last difference was better take next n */ + continue; + D = L; + *pp = m; + *pq = n; /* keep improved data */ + if (D == 0) + break; /* best result we can get */ + } + return (unsigned int) (0xffffffff & D); +} + +/*****************************************************************************/ +static void smiLoadCcr (struct ctfb_res_modes *var, unsigned short device_id) +{ + unsigned int p = 0; + unsigned int q = 0; + long long freq; + register GraphicDevice *pGD = (GraphicDevice *)&smi; + + smiWrite (SMI_INDX_C4, 0x65, 0); + smiWrite (SMI_INDX_C4, 0x66, 0); + smiWrite (SMI_INDX_C4, 0x68, 0x50); + if (device_id == PCI_DEVICE_ID_SMI_810) { + smiWrite (SMI_INDX_C4, 0x69, 0x3); + } else { + smiWrite (SMI_INDX_C4, 0x69, 0x0); + } + + /* Memory clock */ + switch (device_id) { + case PCI_DEVICE_ID_SMI_710 : + smiWrite (SMI_INDX_C4, 0x6a, 0x75); + break; + case PCI_DEVICE_ID_SMI_712 : + smiWrite (SMI_INDX_C4, 0x6a, 0x80); + break; + default : + smiWrite (SMI_INDX_C4, 0x6a, 0x53); + break; + } + smiWrite (SMI_INDX_C4, 0x6b, 0x15); + + /* VCLK */ + freq = 1000000000000LL / var -> pixclock; + + FindPQ ((unsigned int)freq, &p, &q); + + smiWrite (SMI_INDX_C4, 0x6c, p); + smiWrite (SMI_INDX_C4, 0x6d, q); + +} + +/******************************************************************************* + * + * Init video chip with common Linux graphic modes (lilo) + */ +void *video_hw_init (void) +{ + GraphicDevice *pGD = (GraphicDevice *)&smi; + unsigned short device_id; + pci_dev_t devbusfn; + int videomode; + unsigned long t1, hsynch, vsynch; + unsigned int pci_mem_base, *vm; + char *penv; + int tmp, i, bits_per_pixel; + struct ctfb_res_modes *res_mode; + struct ctfb_res_modes var_mode; + unsigned char videoout; + + /* Search for video chip */ + printf("Video: "); + + if ((devbusfn = pci_find_devices(supported, 0)) < 0) + { + printf ("Controller not found !\n"); + return (NULL); + } + + /* PCI setup */ + pci_write_config_dword (devbusfn, PCI_COMMAND, (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)); + pci_read_config_word (devbusfn, PCI_DEVICE_ID, &device_id); + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base); + pci_mem_base = pci_mem_to_phys (devbusfn, pci_mem_base); + + tmp = 0; + + videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE; + /* get video mode via environment */ + if ((penv = getenv ("videomode")) != NULL) { + /* deceide if it is a string */ + if (penv[0] <= '9') { + videomode = (int) simple_strtoul (penv, NULL, 16); + tmp = 1; + } + } else { + tmp = 1; + } + if (tmp) { + /* parameter are vesa modes */ + /* search params */ + for (i = 0; i < VESA_MODES_COUNT; i++) { + if (vesa_modes[i].vesanr == videomode) + break; + } + if (i == VESA_MODES_COUNT) { + printf ("no VESA Mode found, switching to mode 0x%x ", CONFIG_SYS_DEFAULT_VIDEO_MODE); + i = 0; + } + res_mode = + (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i]. + resindex]; + bits_per_pixel = vesa_modes[i].bits_per_pixel; + } else { + + res_mode = (struct ctfb_res_modes *) &var_mode; + bits_per_pixel = video_get_params (res_mode, penv); + } + + /* calculate hsynch and vsynch freq (info only) */ + t1 = (res_mode->left_margin + res_mode->xres + + res_mode->right_margin + res_mode->hsync_len) / 8; + t1 *= 8; + t1 *= res_mode->pixclock; + t1 /= 1000; + hsynch = 1000000000L / t1; + t1 *= + (res_mode->upper_margin + res_mode->yres + + res_mode->lower_margin + res_mode->vsync_len); + t1 /= 1000; + vsynch = 1000000000L / t1; + + /* fill in Graphic device struct */ + sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres, + res_mode->yres, bits_per_pixel, (hsynch / 1000), + (vsynch / 1000)); + printf ("%s\n", pGD->modeIdent); + pGD->winSizeX = res_mode->xres; + pGD->winSizeY = res_mode->yres; + pGD->plnSizeX = res_mode->xres; + pGD->plnSizeY = res_mode->yres; + switch (bits_per_pixel) { + case 8: + pGD->gdfBytesPP = 1; + pGD->gdfIndex = GDF__8BIT_INDEX; + break; + case 15: + pGD->gdfBytesPP = 2; + pGD->gdfIndex = GDF_15BIT_555RGB; + break; + case 16: + pGD->gdfBytesPP = 2; + pGD->gdfIndex = GDF_16BIT_565RGB; + break; + case 24: + pGD->gdfBytesPP = 3; + pGD->gdfIndex = GDF_24BIT_888RGB; + break; + } + + pGD->isaBase = CONFIG_SYS_ISA_IO; + pGD->pciBase = pci_mem_base; + pGD->dprBase = (pci_mem_base + 0x400000 + 0x8000); + pGD->vprBase = (pci_mem_base + 0x400000 + 0xc000); + pGD->cprBase = (pci_mem_base + 0x400000 + 0xe000); + pGD->frameAdrs = pci_mem_base; + pGD->memSize = VIDEO_MEM_SIZE; + + /* Set up hardware : select color mode, + set Register base to isa 3dx for 3?x regs*/ + out8 (SMI_MISC_REG, 0x01); + + /* Turn off display */ + smiWrite (SMI_INDX_C4, 0x01, 0x20); + + /* Unlock ext. crt regs */ + out8 (SMI_LOCK_REG, 0x40); + + /* Unlock crt regs 0-7 */ + smiWrite (SMI_INDX_D4, 0x11, 0x0e); + + /* Sytem Control Register */ + smiLoadRegs (SMI_INDX_C4, SMI_DATA_C5, SMI_SCR, sizeof(SMI_SCR)); + + /* extented CRT Register */ + smiLoadRegs (SMI_INDX_D4, SMI_DATA_D5, SMI_EXT_CRT, sizeof(SMI_EXT_CRT)); + + /* Attributes controller registers */ + smiLoadRegs (SMI_INDX_ATTR, SMI_INDX_ATTR, SMI_ATTR, sizeof(SMI_ATTR)); + + /* Graphics Controller Register */ + smiLoadRegs (SMI_INDX_CE, SMI_DATA_CF, SMI_GCR, sizeof(SMI_GCR)); + + /* Sequencer Register */ + smiLoadRegs (SMI_INDX_C4, SMI_DATA_C5, SMI_SEQR, sizeof(SMI_SEQR)); + + /* Power Control Register */ + smiLoadRegs (SMI_INDX_C4, SMI_DATA_C5, SMI_PCR, sizeof(SMI_PCR)); + + /* Memory Control Register */ + /* Register MSR62 is a power on configurable register. We don't */ + /* modify it */ + smiLoadRegs (SMI_INDX_C4, SMI_DATA_C5, SMI_MCR, sizeof(SMI_MCR)); + + /* Set misc output register */ + smiLoadMsr (res_mode); + + /* Set CRT and Clock control registers */ + smiLoadCrt (res_mode, bits_per_pixel); + + smiLoadCcr (res_mode, device_id); + + /* Hardware Cusor Register */ + smiLoadRegs (SMI_INDX_C4, SMI_DATA_C5, SMI_HCR, sizeof(SMI_HCR)); + + /* Enable Display */ + videoout = 2; /* Default output is CRT */ + if ((penv = getenv ("videoout")) != NULL) { + /* deceide if it is a string */ + videoout = (int) simple_strtoul (penv, NULL, 16); + } + smiWrite (SMI_INDX_C4, 0x31, videoout); + + /* Video processor default setup */ + smiInitVideoProcessor (); + + /* Capture port default setup */ + smiInitCapturePort (); + + /* Drawing engine default setup */ + smiInitDrawingEngine (); + + /* Turn on display */ + smiWrite (0x3c4, 0x01, 0x01); + + /* Clear video memory */ + i = pGD->memSize/4; + vm = (unsigned int *)pGD->pciBase; + while(i--) + *vm++ = 0; + return ((void*)&smi); +} + +/******************************************************************************* + * + * Drawing engine fill on screen region + */ +void video_hw_rectfill ( + unsigned int bpp, /* bytes per pixel */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y, /* frame height */ + unsigned int color /* fill color */ + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + register unsigned int control; + + dim_x *= bpp; + + out32r ((pGD->dprBase + 0x0014), color); + out32r ((pGD->dprBase + 0x0004), ((dst_x<<16) | dst_y)); + out32r ((pGD->dprBase + 0x0008), ((dim_x<<16) | dim_y)); + + control = 0x0000ffff & in32r ((pGD->dprBase + 0x000c)); + + control |= 0x80010000; + + out32r ((pGD->dprBase + 0x000c), control); + + /* Wait for drawing processor */ + do + { + out8 ((pGD->isaBase + 0x3c4), 0x16); + } while (in8 (pGD->isaBase + 0x3c5) & 0x08); +} + +/******************************************************************************* + * + * Drawing engine bitblt with screen region + */ +void video_hw_bitblt ( + unsigned int bpp, /* bytes per pixel */ + unsigned int src_x, /* source pos x */ + unsigned int src_y, /* source pos y */ + unsigned int dst_x, /* dest pos x */ + unsigned int dst_y, /* dest pos y */ + unsigned int dim_x, /* frame width */ + unsigned int dim_y /* frame height */ + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + register unsigned int control; + + dim_x *= bpp; + + if ((src_y<dst_y) || ((src_y==dst_y) && (src_x<dst_x))) + { + out32r ((pGD->dprBase + 0x0000), (((src_x+dim_x-1)<<16) | (src_y+dim_y-1))); + out32r ((pGD->dprBase + 0x0004), (((dst_x+dim_x-1)<<16) | (dst_y+dim_y-1))); + control = 0x88000000; + } else { + out32r ((pGD->dprBase + 0x0000), ((src_x<<16) | src_y)); + out32r ((pGD->dprBase + 0x0004), ((dst_x<<16) | dst_y)); + control = 0x80000000; + } + + out32r ((pGD->dprBase + 0x0008), ((dim_x<<16) | dim_y)); + control |= (0x0000ffff & in32r ((pGD->dprBase + 0x000c))); + out32r ((pGD->dprBase + 0x000c), control); + + /* Wait for drawing processor */ + do + { + out8 ((pGD->isaBase + 0x3c4), 0x16); + } while (in8 (pGD->isaBase + 0x3c5) & 0x08); +} + +/******************************************************************************* + * + * Set a RGB color in the LUT (8 bit index) + */ +void video_set_lut ( + unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + register GraphicDevice *pGD = (GraphicDevice *)&smi; + + out8 (SMI_LUT_MASK, 0xff); + + out8 (SMI_LUT_START, (char)index); + + out8 (SMI_LUT_RGB, r>>2); /* red */ + udelay (10); + out8 (SMI_LUT_RGB, g>>2); /* green */ + udelay (10); + out8 (SMI_LUT_RGB, b>>2); /* blue */ + udelay (10); +} diff --git a/u-boot/drivers/video/videomodes.c b/u-boot/drivers/video/videomodes.c new file mode 100644 index 0000000..d27ce1d --- /dev/null +++ b/u-boot/drivers/video/videomodes.c @@ -0,0 +1,208 @@ +/* + * (C) Copyright 2004 + * Pierre Aubert, Staubli Faverges , <p.aubert@staubli.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/************************************************************************ + Get Parameters for the video mode: + The default video mode can be defined in CONFIG_SYS_DEFAULT_VIDEO_MODE. + If undefined, default video mode is set to 0x301 + Parameters can be set via the variable "videomode" in the environment. + 2 diferent ways are possible: + "videomode=301" - 301 is a hexadecimal number describing the VESA + mode. Following modes are implemented: + + Colors 640x480 800x600 1024x768 1152x864 1280x1024 + --------+--------------------------------------------- + 8 bits | 0x301 0x303 0x305 0x161 0x307 + 15 bits | 0x310 0x313 0x316 0x162 0x319 + 16 bits | 0x311 0x314 0x317 0x163 0x31A + 24 bits | 0x312 0x315 0x318 ? 0x31B + --------+--------------------------------------------- + "videomode=bootargs" + - the parameters are parsed from the bootargs. + The format is "NAME:VALUE,NAME:VALUE" etc. + Ex.: + "bootargs=video=ctfb:x:800,y:600,depth:16,pclk:25000" + Parameters not included in the list will be taken from + the default mode, which is one of the following: + mode:0 640x480x24 + mode:1 800x600x16 + mode:2 1024x768x8 + mode:3 960x720x24 + mode:4 1152x864x16 + mode:5 1280x1024x8 + + if "mode" is not provided within the parameter list, + mode:0 is assumed. + Following parameters are supported: + x xres = visible resolution horizontal + y yres = visible resolution vertical + pclk pixelclocks in pico sec + le left_marging time from sync to picture in pixelclocks + ri right_marging time from picture to sync in pixelclocks + up upper_margin time from sync to picture + lo lower_margin + hs hsync_len length of horizontal sync + vs vsync_len length of vertical sync + sync see FB_SYNC_* + vmode see FB_VMODE_* + depth Color depth in bits per pixel + All other parameters in the variable bootargs are ignored. + It is also possible to set the parameters direct in the + variable "videomode", or in another variable i.e. + "myvideo" and setting the variable "videomode=myvideo".. +****************************************************************************/ + +#include <common.h> +#include "videomodes.h" + +const struct ctfb_vesa_modes vesa_modes[VESA_MODES_COUNT] = { + {0x301, RES_MODE_640x480, 8}, + {0x310, RES_MODE_640x480, 15}, + {0x311, RES_MODE_640x480, 16}, + {0x312, RES_MODE_640x480, 24}, + {0x303, RES_MODE_800x600, 8}, + {0x313, RES_MODE_800x600, 15}, + {0x314, RES_MODE_800x600, 16}, + {0x315, RES_MODE_800x600, 24}, + {0x305, RES_MODE_1024x768, 8}, + {0x316, RES_MODE_1024x768, 15}, + {0x317, RES_MODE_1024x768, 16}, + {0x318, RES_MODE_1024x768, 24}, + {0x161, RES_MODE_1152x864, 8}, + {0x162, RES_MODE_1152x864, 15}, + {0x163, RES_MODE_1152x864, 16}, + {0x307, RES_MODE_1280x1024, 8}, + {0x319, RES_MODE_1280x1024, 15}, + {0x31A, RES_MODE_1280x1024, 16}, + {0x31B, RES_MODE_1280x1024, 24}, +}; +const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = { + /* x y pixclk le ri up lo hs vs s vmode */ + {640, 480, 39721, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED}, + {800, 600, 27778, 64, 24, 22, 1, 72, 2, 0, FB_VMODE_NONINTERLACED}, + {1024, 768, 15384, 168, 8, 29, 3, 144, 4, 0, FB_VMODE_NONINTERLACED}, + {960, 720, 13100, 160, 40, 32, 8, 80, 4, 0, FB_VMODE_NONINTERLACED}, + {1152, 864, 12004, 200, 64, 32, 16, 80, 4, 0, FB_VMODE_NONINTERLACED}, + {1280, 1024, 9090, 200, 48, 26, 1, 184, 3, 0, FB_VMODE_NONINTERLACED}, +}; + +/************************************************************************ + * Get Parameters for the video mode: + */ +/********************************************************************* + * returns the length to the next seperator + */ +static int +video_get_param_len (char *start, char sep) +{ + int i = 0; + while ((*start != 0) && (*start != sep)) { + start++; + i++; + } + return i; +} + +static int +video_search_param (char *start, char *param) +{ + int len, totallen, i; + char *p = start; + len = strlen (param); + totallen = len + strlen (start); + for (i = 0; i < totallen; i++) { + if (strncmp (p++, param, len) == 0) + return (i); + } + return -1; +} + +/*************************************************************** + * Get parameter via the environment as it is done for the + * linux kernel i.e: + * video=ctfb:x:800,xv:1280,y:600,yv:1024,depth:16,mode:0,pclk:25000, + * le:56,ri:48,up:26,lo:5,hs:152,vs:2,sync:0,vmode:0,accel:0 + * + * penv is a pointer to the environment, containing the string, or the name of + * another environment variable. It could even be the term "bootargs" + */ + +#define GET_OPTION(name,var) \ + if(strncmp(p,name,strlen(name))==0) { \ + val_s=p+strlen(name); \ + var=simple_strtoul(val_s, NULL, 10); \ + } + +int video_get_params (struct ctfb_res_modes *pPar, char *penv) +{ + char *p, *s, *val_s; + int i = 0, t; + int bpp; + int mode; + /* first search for the environment containing the real param string */ + s = penv; + if ((p = getenv (s)) != NULL) { + s = p; + } + /* in case of the bootargs line, we have to start + * after "video=ctfb:" + */ + i = video_search_param (s, "video=ctfb:"); + if (i >= 0) { + s += i; + s += strlen ("video=ctfb:"); + } + /* search for mode as a default value */ + p = s; + t = 0; + mode = 0; /* default */ + while ((i = video_get_param_len (p, ',')) != 0) { + GET_OPTION ("mode:", mode) + p += i; + if (*p != 0) + p++; /* skip ',' */ + } + if (mode >= RES_MODES_COUNT) + mode = 0; + *pPar = res_mode_init[mode]; /* copy default values */ + bpp = 24 - ((mode % 3) * 8); + p = s; /* restart */ + while ((i = video_get_param_len (p, ',')) != 0) { + GET_OPTION ("x:", pPar->xres) + GET_OPTION ("y:", pPar->yres) + GET_OPTION ("le:", pPar->left_margin) + GET_OPTION ("ri:", pPar->right_margin) + GET_OPTION ("up:", pPar->upper_margin) + GET_OPTION ("lo:", pPar->lower_margin) + GET_OPTION ("hs:", pPar->hsync_len) + GET_OPTION ("vs:", pPar->vsync_len) + GET_OPTION ("sync:", pPar->sync) + GET_OPTION ("vmode:", pPar->vmode) + GET_OPTION ("pclk:", pPar->pixclock) + GET_OPTION ("depth:", bpp) + p += i; + if (*p != 0) + p++; /* skip ',' */ + } + return bpp; +} diff --git a/u-boot/drivers/video/videomodes.h b/u-boot/drivers/video/videomodes.h new file mode 100644 index 0000000..0d7c335 --- /dev/null +++ b/u-boot/drivers/video/videomodes.h @@ -0,0 +1,88 @@ +/* + * (C) Copyright 2004 + * Pierre Aubert, Staubli Faverges , <p.aubert@staubli.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 CONFIG_SYS_DEFAULT_VIDEO_MODE +#define CONFIG_SYS_DEFAULT_VIDEO_MODE 0x301 +#endif + +/* Some mode definitions */ +#define FB_SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */ +#define FB_SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */ +#define FB_SYNC_EXT 4 /* external sync */ +#define FB_SYNC_COMP_HIGH_ACT 8 /* composite sync high active */ +#define FB_SYNC_BROADCAST 16 /* broadcast video timings */ + /* vtotal = 144d/288n/576i => PAL */ + /* vtotal = 121d/242n/484i => NTSC */ +#define FB_SYNC_ON_GREEN 32 /* sync on green */ +#define FB_VMODE_NONINTERLACED 0 /* non interlaced */ +#define FB_VMODE_INTERLACED 1 /* interlaced */ +#define FB_VMODE_DOUBLE 2 /* double scan */ +#define FB_VMODE_MASK 255 + +#define FB_VMODE_YWRAP 256 /* ywrap instead of panning */ +#define FB_VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */ +#define FB_VMODE_CONUPDATE 512 /* don't update x/yoffset */ + + +/****************************************************************** + * Resolution Struct + ******************************************************************/ +struct ctfb_res_modes { + int xres; /* visible resolution */ + int yres; + /* Timing: All values in pixclocks, except pixclock (of course) */ + int pixclock; /* pixel clock in ps (pico seconds) */ + int left_margin; /* time from sync to picture */ + int right_margin; /* time from picture to sync */ + int upper_margin; /* time from sync to picture */ + int lower_margin; + int hsync_len; /* length of horizontal sync */ + int vsync_len; /* length of vertical sync */ + int sync; /* see FB_SYNC_* */ + int vmode; /* see FB_VMODE_* */ +}; + +/****************************************************************** + * Vesa Mode Struct + ******************************************************************/ +struct ctfb_vesa_modes { + int vesanr; /* Vesa number as in LILO (VESA Nr + 0x200} */ + int resindex; /* index to resolution struct */ + int bits_per_pixel; /* bpp */ +}; + +#define RES_MODE_640x480 0 +#define RES_MODE_800x600 1 +#define RES_MODE_1024x768 2 +#define RES_MODE_960_720 3 +#define RES_MODE_1152x864 4 +#define RES_MODE_1280x1024 5 +#define RES_MODES_COUNT 6 + +#define VESA_MODES_COUNT 19 + +extern const struct ctfb_vesa_modes vesa_modes[]; +extern const struct ctfb_res_modes res_mode_init[]; + +int video_get_params (struct ctfb_res_modes *pPar, char *penv); |