diff options
Diffstat (limited to 'drivers/staging/sm750fb/sm750_hw.c')
-rw-r--r-- | drivers/staging/sm750fb/sm750_hw.c | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c new file mode 100644 index 000000000..55cb00e8b --- /dev/null +++ b/drivers/staging/sm750fb/sm750_hw.c @@ -0,0 +1,569 @@ +// SPDX-License-Identifier: GPL-2.0 +#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 <linux/sizes.h> + +#include "sm750.h" +#include "ddk750.h" +#include "sm750_accel.h" + +void __iomem *mmio750; + +int hw_sm750_map(struct sm750_dev *sm750_dev, struct pci_dev *pdev) +{ + int ret; + + ret = 0; + + sm750_dev->vidreg_start = pci_resource_start(pdev, 1); + sm750_dev->vidreg_size = SZ_2M; + + pr_info("mmio phyAddr = %lx\n", sm750_dev->vidreg_start); + + /* + * reserve the vidreg space of smi adaptor + * if you do this, you need to add release region code + * in lynxfb_remove, or memory will not be mapped again + * successfully + */ + ret = pci_request_region(pdev, 1, "sm750fb"); + if (ret) { + pr_err("Can not request PCI regions.\n"); + goto exit; + } + + /* now map mmio and vidmem */ + sm750_dev->pvReg = + ioremap(sm750_dev->vidreg_start, sm750_dev->vidreg_size); + if (!sm750_dev->pvReg) { + pr_err("mmio failed\n"); + ret = -EFAULT; + goto exit; + } else { + pr_info("mmio virtual addr = %p\n", sm750_dev->pvReg); + } + + sm750_dev->accel.dprBase = sm750_dev->pvReg + DE_BASE_ADDR_TYPE1; + sm750_dev->accel.dpPortBase = sm750_dev->pvReg + DE_PORT_ADDR_TYPE1; + + mmio750 = sm750_dev->pvReg; + sm750_set_chip_type(sm750_dev->devid, sm750_dev->revid); + + sm750_dev->vidmem_start = pci_resource_start(pdev, 0); + /* + * don't use pdev_resource[x].end - resource[x].start to + * calculate the resource size, it's only the maximum available + * size but not the actual size, using + * @ddk750_get_vm_size function can be safe. + */ + sm750_dev->vidmem_size = ddk750_get_vm_size(); + pr_info("video memory phyAddr = %lx, size = %u bytes\n", + sm750_dev->vidmem_start, sm750_dev->vidmem_size); + + /* reserve the vidmem space of smi adaptor */ + sm750_dev->pvMem = + ioremap_wc(sm750_dev->vidmem_start, sm750_dev->vidmem_size); + if (!sm750_dev->pvMem) { + iounmap(sm750_dev->pvReg); + pr_err("Map video memory failed\n"); + ret = -EFAULT; + goto exit; + } else { + pr_info("video memory vaddr = %p\n", sm750_dev->pvMem); + } +exit: + return ret; +} + +int hw_sm750_inithw(struct sm750_dev *sm750_dev, struct pci_dev *pdev) +{ + struct init_status *parm; + + parm = &sm750_dev->initParm; + if (parm->chip_clk == 0) + parm->chip_clk = (sm750_get_chip_type() == 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_init_hw((struct initchip_param *)&sm750_dev->initParm); + /* for sm718, open pci burst */ + if (sm750_dev->devid == 0x718) { + poke32(SYSTEM_CTRL, + peek32(SYSTEM_CTRL) | SYSTEM_CTRL_PCI_BURST); + } + + if (sm750_get_chip_type() != SM750LE) { + unsigned int val; + /* does user need CRT? */ + if (sm750_dev->nocrt) { + poke32(MISC_CTRL, + peek32(MISC_CTRL) | MISC_CTRL_DAC_POWER_OFF); + /* shut off dpms */ + val = peek32(SYSTEM_CTRL) & ~SYSTEM_CTRL_DPMS_MASK; + val |= SYSTEM_CTRL_DPMS_VPHN; + poke32(SYSTEM_CTRL, val); + } else { + poke32(MISC_CTRL, + peek32(MISC_CTRL) & ~MISC_CTRL_DAC_POWER_OFF); + /* turn on dpms */ + val = peek32(SYSTEM_CTRL) & ~SYSTEM_CTRL_DPMS_MASK; + val |= SYSTEM_CTRL_DPMS_VPHP; + poke32(SYSTEM_CTRL, val); + } + + val = peek32(PANEL_DISPLAY_CTRL) & + ~(PANEL_DISPLAY_CTRL_DUAL_DISPLAY | + PANEL_DISPLAY_CTRL_DOUBLE_PIXEL); + switch (sm750_dev->pnltype) { + case sm750_24TFT: + break; + case sm750_doubleTFT: + val |= PANEL_DISPLAY_CTRL_DOUBLE_PIXEL; + break; + case sm750_dualTFT: + val |= PANEL_DISPLAY_CTRL_DUAL_DISPLAY; + break; + } + poke32(PANEL_DISPLAY_CTRL, val); + } else { + /* + * for 750LE, no DVI chip initialization + * 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. + */ + sm750_sw_i2c_init(0, 1); + + /* + * Customer may NOT use CH7301 DVI chip, which has to be + * initialized differently. + */ + if (sm750_sw_i2c_read_reg(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"); + sm750_sw_i2c_write_reg(0xec, 0x1d, 0x16); + sm750_sw_i2c_write_reg(0xec, 0x21, 0x9); + sm750_sw_i2c_write_reg(0xec, 0x49, 0xC0); + pr_info("okay,CH7301 DVI chip setup done\n"); + } + } + + /* init 2d engine */ + if (!sm750_dev->accel_off) + hw_sm750_initAccel(sm750_dev); + + return 0; +} + +int hw_sm750_output_setMode(struct lynxfb_output *output, + struct fb_var_screeninfo *var, + struct fb_fix_screeninfo *fix) +{ + int ret; + enum disp_output disp_set; + int channel; + + ret = 0; + disp_set = 0; + channel = *output->channel; + + if (sm750_get_chip_type() != SM750LE) { + if (channel == sm750_primary) { + pr_info("primary channel\n"); + if (output->paths & sm750_panel) + disp_set |= do_LCD1_PRI; + if (output->paths & sm750_crt) + disp_set |= do_CRT_PRI; + + } else { + pr_info("secondary channel\n"); + if (output->paths & sm750_panel) + disp_set |= do_LCD1_SEC; + if (output->paths & sm750_crt) + disp_set |= do_CRT_SEC; + } + ddk750_set_logical_disp_out(disp_set); + } 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; +} + +int hw_sm750_crtc_checkMode(struct lynxfb_crtc *crtc, + struct fb_var_screeninfo *var) +{ + struct sm750_dev *sm750_dev; + struct lynxfb_par *par = container_of(crtc, struct lynxfb_par, crtc); + + sm750_dev = par->dev; + + switch (var->bits_per_pixel) { + case 8: + case 16: + break; + case 32: + if (sm750_dev->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; + struct mode_parameter modparm; + enum clock_type clock; + struct sm750_dev *sm750_dev; + struct lynxfb_par *par; + + ret = 0; + par = container_of(crtc, struct lynxfb_par, crtc); + sm750_dev = par->dev; + + if (!sm750_dev->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; + } + sm750_hw_set2dformat(&sm750_dev->accel, fmt); + } + + /* set timing */ + 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, + crtc->o_screen & PANEL_FB_ADDRESS_ADDRESS_MASK); + + reg = var->xres * (var->bits_per_pixel >> 3); + /* + * crtc->channel is not equal to par->index on numeric, + * be aware of that + */ + reg = ALIGN(reg, crtc->line_pad); + reg = (reg << PANEL_FB_WIDTH_WIDTH_SHIFT) & + PANEL_FB_WIDTH_WIDTH_MASK; + reg |= (fix->line_length & PANEL_FB_WIDTH_OFFSET_MASK); + poke32(PANEL_FB_WIDTH, reg); + + reg = ((var->xres - 1) << PANEL_WINDOW_WIDTH_WIDTH_SHIFT) & + PANEL_WINDOW_WIDTH_WIDTH_MASK; + reg |= (var->xoffset & PANEL_WINDOW_WIDTH_X_MASK); + poke32(PANEL_WINDOW_WIDTH, reg); + + reg = (var->yres_virtual - 1) + << PANEL_WINDOW_HEIGHT_HEIGHT_SHIFT; + reg &= PANEL_WINDOW_HEIGHT_HEIGHT_MASK; + reg |= (var->yoffset & PANEL_WINDOW_HEIGHT_Y_MASK); + poke32(PANEL_WINDOW_HEIGHT, reg); + + poke32(PANEL_PLANE_TL, 0); + + reg = ((var->yres - 1) << PANEL_PLANE_BR_BOTTOM_SHIFT) & + PANEL_PLANE_BR_BOTTOM_MASK; + reg |= ((var->xres - 1) & PANEL_PLANE_BR_RIGHT_MASK); + poke32(PANEL_PLANE_BR, reg); + + /* set pixel format */ + reg = peek32(PANEL_DISPLAY_CTRL); + poke32(PANEL_DISPLAY_CTRL, reg | (var->bits_per_pixel >> 4)); + } else { + /* not implemented now */ + poke32(CRT_FB_ADDRESS, crtc->o_screen); + reg = var->xres * (var->bits_per_pixel >> 3); + /* + * crtc->channel is not equal to par->index on numeric, + * be aware of that + */ + reg = ALIGN(reg, crtc->line_pad) << CRT_FB_WIDTH_WIDTH_SHIFT; + reg &= CRT_FB_WIDTH_WIDTH_MASK; + reg |= (fix->line_length & CRT_FB_WIDTH_OFFSET_MASK); + poke32(CRT_FB_WIDTH, reg); + + /* SET PIXEL FORMAT */ + reg = peek32(CRT_DISPLAY_CTRL); + reg |= ((var->bits_per_pixel >> 4) & + CRT_DISPLAY_CTRL_FORMAT_MASK); + poke32(CRT_DISPLAY_CTRL, reg); + } + +exit: + return ret; +} + +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) { + case FB_BLANK_UNBLANK: + dpms = CRT_DISPLAY_CTRL_DPMS_0; + crtdb = 0; + break; + case FB_BLANK_NORMAL: + dpms = CRT_DISPLAY_CTRL_DPMS_0; + crtdb = CRT_DISPLAY_CTRL_BLANK; + break; + case FB_BLANK_VSYNC_SUSPEND: + dpms = CRT_DISPLAY_CTRL_DPMS_2; + crtdb = CRT_DISPLAY_CTRL_BLANK; + break; + case FB_BLANK_HSYNC_SUSPEND: + dpms = CRT_DISPLAY_CTRL_DPMS_1; + crtdb = CRT_DISPLAY_CTRL_BLANK; + break; + case FB_BLANK_POWERDOWN: + dpms = CRT_DISPLAY_CTRL_DPMS_3; + crtdb = CRT_DISPLAY_CTRL_BLANK; + break; + default: + return -EINVAL; + } + + if (output->paths & sm750_crt) { + unsigned int val; + + val = peek32(CRT_DISPLAY_CTRL) & ~CRT_DISPLAY_CTRL_DPMS_MASK; + poke32(CRT_DISPLAY_CTRL, val | dpms); + + val = peek32(CRT_DISPLAY_CTRL) & ~CRT_DISPLAY_CTRL_BLANK; + poke32(CRT_DISPLAY_CTRL, val | crtdb); + } + return 0; +} + +int hw_sm750_setBLANK(struct lynxfb_output *output, int blank) +{ + unsigned int dpms, pps, crtdb; + + dpms = 0; + pps = 0; + crtdb = 0; + + switch (blank) { + case FB_BLANK_UNBLANK: + pr_debug("flag = FB_BLANK_UNBLANK\n"); + dpms = SYSTEM_CTRL_DPMS_VPHP; + pps = PANEL_DISPLAY_CTRL_DATA; + break; + case FB_BLANK_NORMAL: + pr_debug("flag = FB_BLANK_NORMAL\n"); + dpms = SYSTEM_CTRL_DPMS_VPHP; + crtdb = CRT_DISPLAY_CTRL_BLANK; + break; + case FB_BLANK_VSYNC_SUSPEND: + dpms = SYSTEM_CTRL_DPMS_VNHP; + crtdb = CRT_DISPLAY_CTRL_BLANK; + break; + case FB_BLANK_HSYNC_SUSPEND: + dpms = SYSTEM_CTRL_DPMS_VPHN; + crtdb = CRT_DISPLAY_CTRL_BLANK; + break; + case FB_BLANK_POWERDOWN: + dpms = SYSTEM_CTRL_DPMS_VNHN; + crtdb = CRT_DISPLAY_CTRL_BLANK; + break; + } + + if (output->paths & sm750_crt) { + unsigned int val = peek32(SYSTEM_CTRL) & ~SYSTEM_CTRL_DPMS_MASK; + + poke32(SYSTEM_CTRL, val | dpms); + + val = peek32(CRT_DISPLAY_CTRL) & ~CRT_DISPLAY_CTRL_BLANK; + poke32(CRT_DISPLAY_CTRL, val | crtdb); + } + + if (output->paths & sm750_panel) { + unsigned int val = peek32(PANEL_DISPLAY_CTRL); + + val &= ~PANEL_DISPLAY_CTRL_DATA; + val |= pps; + poke32(PANEL_DISPLAY_CTRL, val); + } + + return 0; +} + +void hw_sm750_initAccel(struct sm750_dev *sm750_dev) +{ + u32 reg; + + sm750_enable_2d_engine(1); + + if (sm750_get_chip_type() == SM750LE) { + reg = peek32(DE_STATE1); + reg |= DE_STATE1_DE_ABORT; + poke32(DE_STATE1, reg); + + reg = peek32(DE_STATE1); + reg &= ~DE_STATE1_DE_ABORT; + poke32(DE_STATE1, reg); + + } else { + /* engine reset */ + reg = peek32(SYSTEM_CTRL); + reg |= SYSTEM_CTRL_DE_ABORT; + poke32(SYSTEM_CTRL, reg); + + reg = peek32(SYSTEM_CTRL); + reg &= ~SYSTEM_CTRL_DE_ABORT; + poke32(SYSTEM_CTRL, reg); + } + + /* call 2d init */ + sm750_dev->accel.de_init(&sm750_dev->accel); +} + +int hw_sm750le_deWait(void) +{ + int i = 0x10000000; + unsigned int mask = DE_STATE2_DE_STATUS_BUSY | DE_STATE2_DE_FIFO_EMPTY | + DE_STATE2_DE_MEM_FIFO_EMPTY; + + while (i--) { + unsigned int val = peek32(DE_STATE2); + + if ((val & mask) == + (DE_STATE2_DE_FIFO_EMPTY | DE_STATE2_DE_MEM_FIFO_EMPTY)) + return 0; + } + /* timeout error */ + return -1; +} + +int hw_sm750_deWait(void) +{ + int i = 0x10000000; + unsigned int mask = SYSTEM_CTRL_DE_STATUS_BUSY | + SYSTEM_CTRL_DE_FIFO_EMPTY | + SYSTEM_CTRL_DE_MEM_FIFO_EMPTY; + + while (i--) { + unsigned int val = peek32(SYSTEM_CTRL); + + if ((val & mask) == + (SYSTEM_CTRL_DE_FIFO_EMPTY | 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) +{ + u32 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->o_screen; + if (crtc->channel == sm750_primary) { + poke32(PANEL_FB_ADDRESS, + peek32(PANEL_FB_ADDRESS) | + (total & PANEL_FB_ADDRESS_ADDRESS_MASK)); + } else { + poke32(CRT_FB_ADDRESS, + peek32(CRT_FB_ADDRESS) | + (total & CRT_FB_ADDRESS_ADDRESS_MASK)); + } + return 0; +} |