diff options
Diffstat (limited to 'drivers/staging/sm750fb/sm750_hw.c')
-rw-r--r-- | drivers/staging/sm750fb/sm750_hw.c | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c new file mode 100644 index 0000000..9f0d06d --- /dev/null +++ b/drivers/staging/sm750fb/sm750_hw.c @@ -0,0 +1,642 @@ +#include <linux/version.h> +#include<linux/module.h> +#include<linux/kernel.h> +#include<linux/errno.h> +#include<linux/string.h> +#include<linux/mm.h> +#include<linux/slab.h> +#include<linux/delay.h> +#include<linux/fb.h> +#include<linux/ioport.h> +#include<linux/init.h> +#include<linux/pci.h> +#include<linux/vmalloc.h> +#include<linux/pagemap.h> +#include <linux/console.h> +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif +#include<linux/platform_device.h> +#include<linux/screen_info.h> + +#include "sm750.h" +#include "sm750_hw.h" +#include "ddk750.h" +#include "sm750_accel.h" + +int hw_sm750_map(struct lynx_share* share, struct pci_dev* pdev) +{ + int ret; + struct sm750_share * spec_share; + + + spec_share = container_of(share, struct sm750_share,share); + ret = 0; + + share->vidreg_start = pci_resource_start(pdev, 1); + share->vidreg_size = MB(2); + + pr_info("mmio phyAddr = %lx\n", share->vidreg_start); + + /* reserve the vidreg space of smi adaptor + * if you do this, u need to add release region code + * in lynxfb_remove, or memory will not be mapped again + * successfully + * */ + + if((ret = pci_request_region(pdev, 1, "sm750fb"))) + { + pr_err("Can not request PCI regions.\n"); + goto exit; + } + + /* now map mmio and vidmem*/ + share->pvReg = ioremap_nocache(share->vidreg_start, share->vidreg_size); + if(!share->pvReg){ + pr_err("mmio failed\n"); + ret = -EFAULT; + goto exit; + }else{ + pr_info("mmio virtual addr = %p\n", share->pvReg); + } + + + share->accel.dprBase = share->pvReg + DE_BASE_ADDR_TYPE1; + share->accel.dpPortBase = share->pvReg + DE_PORT_ADDR_TYPE1; + + ddk750_set_mmio(share->pvReg,share->devid, share->revid); + + share->vidmem_start = pci_resource_start(pdev, 0); + /* don't use pdev_resource[x].end - resource[x].start to + * calculate the resource size,its only the maximum available + * size but not the actual size,use + * @hw_sm750_getVMSize function can be safe. + * */ + share->vidmem_size = hw_sm750_getVMSize(share); + pr_info("video memory phyAddr = %lx, size = %u bytes\n", + share->vidmem_start, share->vidmem_size); + + /* reserve the vidmem space of smi adaptor */ +#if 0 + if((ret = pci_request_region(pdev,0,_moduleName_))) + { + pr_err("Can not request PCI regions.\n"); + goto exit; + } +#endif + + share->pvMem = ioremap(share->vidmem_start, + share->vidmem_size); + + if(!share->pvMem){ + pr_err("Map video memory failed\n"); + ret = -EFAULT; + goto exit; + }else{ + pr_info("video memory vaddr = %p\n", share->pvMem); + } +exit: + return ret; +} + + + +int hw_sm750_inithw(struct lynx_share* share, struct pci_dev * pdev) +{ + struct sm750_share * spec_share; + struct init_status * parm; + + spec_share = container_of(share, struct sm750_share,share); + parm = &spec_share->state.initParm; + if(parm->chip_clk == 0) + parm->chip_clk = (getChipType() == SM750LE)? + DEFAULT_SM750LE_CHIP_CLOCK : + DEFAULT_SM750_CHIP_CLOCK; + + if(parm->mem_clk == 0) + parm->mem_clk = parm->chip_clk; + if(parm->master_clk == 0) + parm->master_clk = parm->chip_clk/3; + + ddk750_initHw((initchip_param_t *)&spec_share->state.initParm); + /* for sm718,open pci burst */ + if(share->devid == 0x718){ + POKE32(SYSTEM_CTRL, + FIELD_SET(PEEK32(SYSTEM_CTRL), SYSTEM_CTRL, PCI_BURST, ON)); + } + + /* sm750 use sii164, it can be setup with default value + * by on power, so initDVIDisp can be skipped */ +#if 0 + ddk750_initDVIDisp(); +#endif + + if(getChipType() != SM750LE) + { + /* does user need CRT ?*/ + if(spec_share->state.nocrt){ + POKE32(MISC_CTRL, + FIELD_SET(PEEK32(MISC_CTRL), + MISC_CTRL, + DAC_POWER, OFF)); + /* shut off dpms */ + POKE32(SYSTEM_CTRL, + FIELD_SET(PEEK32(SYSTEM_CTRL), + SYSTEM_CTRL, + DPMS, VNHN)); + }else{ + POKE32(MISC_CTRL, + FIELD_SET(PEEK32(MISC_CTRL), + MISC_CTRL, + DAC_POWER, ON)); + /* turn on dpms */ + POKE32(SYSTEM_CTRL, + FIELD_SET(PEEK32(SYSTEM_CTRL), + SYSTEM_CTRL, + DPMS, VPHP)); + } + + switch (spec_share->state.pnltype){ + case sm750_doubleTFT: + case sm750_24TFT: + case sm750_dualTFT: + POKE32(PANEL_DISPLAY_CTRL, + FIELD_VALUE(PEEK32(PANEL_DISPLAY_CTRL), + PANEL_DISPLAY_CTRL, + TFT_DISP, + spec_share->state.pnltype)); + break; + } + }else{ + /* for 750LE ,no DVI chip initilization makes Monitor no signal */ + /* Set up GPIO for software I2C to program DVI chip in the + Xilinx SP605 board, in order to have video signal. + */ + swI2CInit(0,1); + + + /* Customer may NOT use CH7301 DVI chip, which has to be + initialized differently. + */ + if (swI2CReadReg(0xec, 0x4a) == 0x95) + { + /* The following register values for CH7301 are from + Chrontel app note and our experiment. + */ + pr_info("yes,CH7301 DVI chip found\n"); + swI2CWriteReg(0xec, 0x1d, 0x16); + swI2CWriteReg(0xec, 0x21, 0x9); + swI2CWriteReg(0xec, 0x49, 0xC0); + pr_info("okay,CH7301 DVI chip setup done\n"); + } + } + + /* init 2d engine */ + if(!share->accel_off){ + hw_sm750_initAccel(share); +// share->accel.de_wait = hw_sm750_deWait; + } + + return 0; +} + + +resource_size_t hw_sm750_getVMSize(struct lynx_share * share) +{ + resource_size_t ret; + + ret = ddk750_getVMSize(); + return ret; +} + + + +int hw_sm750_output_checkMode(struct lynxfb_output* output, struct fb_var_screeninfo* var) +{ + + return 0; +} + + +int hw_sm750_output_setMode(struct lynxfb_output* output, + struct fb_var_screeninfo* var, struct fb_fix_screeninfo* fix) +{ + int ret; + disp_output_t dispSet; + int channel; + + ret = 0; + dispSet = 0; + channel = *output->channel; + + + if(getChipType() != SM750LE){ + if(channel == sm750_primary){ + pr_info("primary channel\n"); + if(output->paths & sm750_panel) + dispSet |= do_LCD1_PRI; + if(output->paths & sm750_crt) + dispSet |= do_CRT_PRI; + + }else{ + pr_info("secondary channel\n"); + if(output->paths & sm750_panel) + dispSet |= do_LCD1_SEC; + if(output->paths & sm750_crt) + dispSet |= do_CRT_SEC; + + } + ddk750_setLogicalDispOut(dispSet); + }else{ + /* just open DISPLAY_CONTROL_750LE register bit 3:0*/ + u32 reg; + reg = PEEK32(DISPLAY_CONTROL_750LE); + reg |= 0xf; + POKE32(DISPLAY_CONTROL_750LE, reg); + } + + pr_info("ddk setlogicdispout done \n"); + return ret; +} + +void hw_sm750_output_clear(struct lynxfb_output* output) +{ + + return; +} + +int hw_sm750_crtc_checkMode(struct lynxfb_crtc* crtc, struct fb_var_screeninfo* var) +{ + struct lynx_share * share; + + + share = container_of(crtc, struct lynxfb_par,crtc)->share; + + switch (var->bits_per_pixel){ + case 8: + case 16: + break; + case 32: + if (share->revid == SM750LE_REVISION_ID) { + pr_debug("750le do not support 32bpp\n"); + return -EINVAL; + } + break; + default: + return -EINVAL; + + } + + return 0; +} + + +/* + set the controller's mode for @crtc charged with @var and @fix parameters +*/ +int hw_sm750_crtc_setMode(struct lynxfb_crtc* crtc, + struct fb_var_screeninfo* var, + struct fb_fix_screeninfo* fix) +{ + int ret,fmt; + u32 reg; + mode_parameter_t modparm; + clock_type_t clock; + struct lynx_share * share; + struct lynxfb_par * par; + + + ret = 0; + par = container_of(crtc, struct lynxfb_par, crtc); + share = par->share; +#if 1 + if(!share->accel_off){ + /* set 2d engine pixel format according to mode bpp */ + switch(var->bits_per_pixel){ + case 8: + fmt = 0; + break; + case 16: + fmt = 1; + break; + case 32: + default: + fmt = 2; + break; + } + hw_set2dformat(&share->accel, fmt); + } +#endif + + /* set timing */ +// modparm.pixel_clock = PS_TO_HZ(var->pixclock); + modparm.pixel_clock = ps_to_hz(var->pixclock); + modparm.vertical_sync_polarity = (var->sync & FB_SYNC_HOR_HIGH_ACT) ? POS:NEG; + modparm.horizontal_sync_polarity = (var->sync & FB_SYNC_VERT_HIGH_ACT) ? POS:NEG; + modparm.clock_phase_polarity = (var->sync& FB_SYNC_COMP_HIGH_ACT) ? POS:NEG; + modparm.horizontal_display_end = var->xres; + modparm.horizontal_sync_width = var->hsync_len; + modparm.horizontal_sync_start = var->xres + var->right_margin; + modparm.horizontal_total = var->xres + var->left_margin + var->right_margin + var->hsync_len; + modparm.vertical_display_end = var->yres; + modparm.vertical_sync_height = var->vsync_len; + modparm.vertical_sync_start = var->yres + var->lower_margin; + modparm.vertical_total = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; + + /* choose pll */ + if(crtc->channel != sm750_secondary) + clock = PRIMARY_PLL; + else + clock = SECONDARY_PLL; + + pr_debug("Request pixel clock = %lu\n", modparm.pixel_clock); + ret = ddk750_setModeTiming(&modparm, clock); + if(ret){ + pr_err("Set mode timing failed\n"); + goto exit; + } + + if(crtc->channel != sm750_secondary){ + /* set pitch, offset ,width,start address ,etc... */ + POKE32(PANEL_FB_ADDRESS, + FIELD_SET(0, PANEL_FB_ADDRESS, STATUS, CURRENT)| + FIELD_SET(0, PANEL_FB_ADDRESS, EXT, LOCAL)| + FIELD_VALUE(0, PANEL_FB_ADDRESS, ADDRESS, crtc->oScreen)); + + reg = var->xres * (var->bits_per_pixel >> 3); + /* crtc->channel is not equal to par->index on numeric,be aware of that */ + reg = PADDING(crtc->line_pad,reg); + + POKE32(PANEL_FB_WIDTH, + FIELD_VALUE(0, PANEL_FB_WIDTH, WIDTH, reg)| + FIELD_VALUE(0, PANEL_FB_WIDTH, OFFSET, fix->line_length)); + + POKE32(PANEL_WINDOW_WIDTH, + FIELD_VALUE(0, PANEL_WINDOW_WIDTH, WIDTH, var->xres -1)| + FIELD_VALUE(0, PANEL_WINDOW_WIDTH, X, var->xoffset)); + + POKE32(PANEL_WINDOW_HEIGHT, + FIELD_VALUE(0, PANEL_WINDOW_HEIGHT, HEIGHT, var->yres_virtual - 1)| + FIELD_VALUE(0, PANEL_WINDOW_HEIGHT, Y, var->yoffset)); + + POKE32(PANEL_PLANE_TL, 0); + + POKE32(PANEL_PLANE_BR, + FIELD_VALUE(0, PANEL_PLANE_BR, BOTTOM, var->yres - 1)| + FIELD_VALUE(0, PANEL_PLANE_BR,RIGHT, var->xres - 1)); + + /* set pixel format */ + reg = PEEK32(PANEL_DISPLAY_CTRL); + POKE32(PANEL_DISPLAY_CTRL, + FIELD_VALUE(reg, + PANEL_DISPLAY_CTRL, FORMAT, + (var->bits_per_pixel >> 4) + )); + }else{ + /* not implemented now */ + POKE32(CRT_FB_ADDRESS, crtc->oScreen); + reg = var->xres * (var->bits_per_pixel >> 3); + /* crtc->channel is not equal to par->index on numeric,be aware of that */ + reg = PADDING(crtc->line_pad, reg); + + POKE32(CRT_FB_WIDTH, + FIELD_VALUE(0, CRT_FB_WIDTH, WIDTH, reg)| + FIELD_VALUE(0, CRT_FB_WIDTH, OFFSET, fix->line_length)); + + /* SET PIXEL FORMAT */ + reg = PEEK32(CRT_DISPLAY_CTRL); + reg = FIELD_VALUE(reg, CRT_DISPLAY_CTRL, FORMAT, var->bits_per_pixel >> 4); + POKE32(CRT_DISPLAY_CTRL, reg); + + } + + +exit: + return ret; +} + +void hw_sm750_crtc_clear(struct lynxfb_crtc* crtc) +{ + + return; +} + +int hw_sm750_setColReg(struct lynxfb_crtc* crtc, ushort index, + ushort red, ushort green, ushort blue) +{ + static unsigned int add[]={PANEL_PALETTE_RAM,CRT_PALETTE_RAM}; + POKE32(add[crtc->channel] + index*4, (red<<16)|(green<<8)|blue); + return 0; +} + +int hw_sm750le_setBLANK(struct lynxfb_output * output, int blank){ + int dpms,crtdb; + + switch(blank) + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + case FB_BLANK_UNBLANK: +#else + case VESA_NO_BLANKING: +#endif + dpms = CRT_DISPLAY_CTRL_DPMS_0; + crtdb = CRT_DISPLAY_CTRL_BLANK_OFF; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + case FB_BLANK_NORMAL: + dpms = CRT_DISPLAY_CTRL_DPMS_0; + crtdb = CRT_DISPLAY_CTRL_BLANK_ON; + break; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + case FB_BLANK_VSYNC_SUSPEND: +#else + case VESA_VSYNC_SUSPEND: +#endif + dpms = CRT_DISPLAY_CTRL_DPMS_2; + crtdb = CRT_DISPLAY_CTRL_BLANK_ON; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + case FB_BLANK_HSYNC_SUSPEND: +#else + case VESA_HSYNC_SUSPEND: +#endif + dpms = CRT_DISPLAY_CTRL_DPMS_1; + crtdb = CRT_DISPLAY_CTRL_BLANK_ON; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + case FB_BLANK_POWERDOWN: +#else + case VESA_POWERDOWN: +#endif + dpms = CRT_DISPLAY_CTRL_DPMS_3; + crtdb = CRT_DISPLAY_CTRL_BLANK_ON; + break; + default: + return -EINVAL; + } + + if(output->paths & sm750_crt){ + POKE32(CRT_DISPLAY_CTRL, FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL, DPMS, dpms)); + POKE32(CRT_DISPLAY_CTRL, FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL, BLANK, crtdb)); + } + return 0; +} + +int hw_sm750_setBLANK(struct lynxfb_output* output,int blank) +{ + unsigned int dpms, pps, crtdb; + + dpms = pps = crtdb = 0; + + switch (blank) + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + case FB_BLANK_UNBLANK: +#else + case VESA_NO_BLANKING: +#endif + pr_info("flag = FB_BLANK_UNBLANK \n"); + dpms = SYSTEM_CTRL_DPMS_VPHP; + pps = PANEL_DISPLAY_CTRL_DATA_ENABLE; + crtdb = CRT_DISPLAY_CTRL_BLANK_OFF; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + case FB_BLANK_NORMAL: + pr_info("flag = FB_BLANK_NORMAL \n"); + dpms = SYSTEM_CTRL_DPMS_VPHP; + pps = PANEL_DISPLAY_CTRL_DATA_DISABLE; + crtdb = CRT_DISPLAY_CTRL_BLANK_ON; + break; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + case FB_BLANK_VSYNC_SUSPEND: +#else + case VESA_VSYNC_SUSPEND: +#endif + dpms = SYSTEM_CTRL_DPMS_VNHP; + pps = PANEL_DISPLAY_CTRL_DATA_DISABLE; + crtdb = CRT_DISPLAY_CTRL_BLANK_ON; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + case FB_BLANK_HSYNC_SUSPEND: +#else + case VESA_HSYNC_SUSPEND: +#endif + dpms = SYSTEM_CTRL_DPMS_VPHN; + pps = PANEL_DISPLAY_CTRL_DATA_DISABLE; + crtdb = CRT_DISPLAY_CTRL_BLANK_ON; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + case FB_BLANK_POWERDOWN: +#else + case VESA_POWERDOWN: +#endif + dpms = SYSTEM_CTRL_DPMS_VNHN; + pps = PANEL_DISPLAY_CTRL_DATA_DISABLE; + crtdb = CRT_DISPLAY_CTRL_BLANK_ON; + break; + } + + if(output->paths & sm750_crt){ + + POKE32(SYSTEM_CTRL,FIELD_VALUE(PEEK32(SYSTEM_CTRL), SYSTEM_CTRL, DPMS, dpms)); + POKE32(CRT_DISPLAY_CTRL,FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL,BLANK, crtdb)); + } + + if(output->paths & sm750_panel){ + POKE32(PANEL_DISPLAY_CTRL, FIELD_VALUE(PEEK32(PANEL_DISPLAY_CTRL), PANEL_DISPLAY_CTRL, DATA, pps)); + } + + return 0; +} + + +void hw_sm750_initAccel(struct lynx_share * share) +{ + u32 reg; + enable2DEngine(1); + + if(getChipType() == SM750LE){ + reg = PEEK32(DE_STATE1); + reg = FIELD_SET(reg, DE_STATE1, DE_ABORT,ON); + POKE32(DE_STATE1,reg); + + reg = PEEK32(DE_STATE1); + reg = FIELD_SET(reg, DE_STATE1, DE_ABORT,OFF); + POKE32(DE_STATE1, reg); + + }else{ + /* engine reset */ + reg = PEEK32(SYSTEM_CTRL); + reg = FIELD_SET(reg, SYSTEM_CTRL, DE_ABORT,ON); + POKE32(SYSTEM_CTRL, reg); + + reg = PEEK32(SYSTEM_CTRL); + reg = FIELD_SET(reg, SYSTEM_CTRL, DE_ABORT,OFF); + POKE32(SYSTEM_CTRL, reg); + } + + /* call 2d init */ + share->accel.de_init(&share->accel); +} + +int hw_sm750le_deWait(void) +{ + int i=0x10000000; + while(i--){ + unsigned int dwVal = PEEK32(DE_STATE2); + if((FIELD_GET(dwVal, DE_STATE2, DE_STATUS) == DE_STATE2_DE_STATUS_IDLE) && + (FIELD_GET(dwVal, DE_STATE2, DE_FIFO) == DE_STATE2_DE_FIFO_EMPTY) && + (FIELD_GET(dwVal, DE_STATE2, DE_MEM_FIFO) == DE_STATE2_DE_MEM_FIFO_EMPTY)) + { + return 0; + } + } + /* timeout error */ + return -1; +} + + +int hw_sm750_deWait(void) +{ + int i=0x10000000; + while(i--){ + unsigned int dwVal = PEEK32(SYSTEM_CTRL); + if((FIELD_GET(dwVal,SYSTEM_CTRL,DE_STATUS) == SYSTEM_CTRL_DE_STATUS_IDLE) && + (FIELD_GET(dwVal,SYSTEM_CTRL,DE_FIFO) == SYSTEM_CTRL_DE_FIFO_EMPTY) && + (FIELD_GET(dwVal,SYSTEM_CTRL,DE_MEM_FIFO) == SYSTEM_CTRL_DE_MEM_FIFO_EMPTY)) + { + return 0; + } + } + /* timeout error */ + return -1; +} + +int hw_sm750_pan_display(struct lynxfb_crtc *crtc, + const struct fb_var_screeninfo *var, + const struct fb_info *info) +{ + uint32_t total; + //check params + if ((var->xoffset + var->xres > var->xres_virtual) || + (var->yoffset + var->yres > var->yres_virtual)) { + return -EINVAL; + } + + total = var->yoffset * info->fix.line_length + + ((var->xoffset * var->bits_per_pixel) >> 3); + total += crtc->oScreen; + if (crtc->channel == sm750_primary) { + POKE32(PANEL_FB_ADDRESS, + FIELD_VALUE(PEEK32(PANEL_FB_ADDRESS), + PANEL_FB_ADDRESS, ADDRESS, total)); + } else { + POKE32(CRT_FB_ADDRESS, + FIELD_VALUE(PEEK32(CRT_FB_ADDRESS), + CRT_FB_ADDRESS, ADDRESS, total)); + } + return 0; +} + |