// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_MTRR #include #endif #include #include #include #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_nocache(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) { 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->oScreen & 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->oScreen); 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->oScreen; 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; }