Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * drivers/video/geode/display_gx1.c
0004  *   -- Geode GX1 display controller
0005  *
0006  * Copyright (C) 2005 Arcom Control Systems Ltd.
0007  *
0008  * Based on AMD's original 2.4 driver:
0009  *   Copyright (C) 2004 Advanced Micro Devices, Inc.
0010  */
0011 #include <linux/spinlock.h>
0012 #include <linux/fb.h>
0013 #include <linux/delay.h>
0014 #include <asm/io.h>
0015 #include <asm/div64.h>
0016 #include <asm/delay.h>
0017 
0018 #include "geodefb.h"
0019 #include "display_gx1.h"
0020 
0021 static DEFINE_SPINLOCK(gx1_conf_reg_lock);
0022 
0023 static u8 gx1_read_conf_reg(u8 reg)
0024 {
0025     u8 val, ccr3;
0026     unsigned long flags;
0027 
0028     spin_lock_irqsave(&gx1_conf_reg_lock, flags);
0029 
0030     outb(CONFIG_CCR3, 0x22);
0031     ccr3 = inb(0x23);
0032     outb(CONFIG_CCR3, 0x22);
0033     outb(ccr3 | CONFIG_CCR3_MAPEN, 0x23);
0034     outb(reg, 0x22);
0035     val = inb(0x23);
0036     outb(CONFIG_CCR3, 0x22);
0037     outb(ccr3, 0x23);
0038 
0039     spin_unlock_irqrestore(&gx1_conf_reg_lock, flags);
0040 
0041     return val;
0042 }
0043 
0044 unsigned gx1_gx_base(void)
0045 {
0046     return (gx1_read_conf_reg(CONFIG_GCR) & 0x03) << 30;
0047 }
0048 
0049 int gx1_frame_buffer_size(void)
0050 {
0051     void __iomem *mc_regs;
0052     u32 bank_cfg;
0053     int d;
0054     unsigned dram_size = 0, fb_base;
0055 
0056     mc_regs = ioremap(gx1_gx_base() + 0x8400, 0x100);
0057     if (!mc_regs)
0058         return -ENOMEM;
0059 
0060 
0061     /* Calculate the total size of both DIMM0 and DIMM1. */
0062     bank_cfg = readl(mc_regs + MC_BANK_CFG);
0063 
0064     for (d = 0; d < 2; d++) {
0065         if ((bank_cfg & MC_BCFG_DIMM0_PG_SZ_MASK) != MC_BCFG_DIMM0_PG_SZ_NO_DIMM)
0066             dram_size += 0x400000 << ((bank_cfg & MC_BCFG_DIMM0_SZ_MASK) >> 8);
0067         bank_cfg >>= 16; /* look at DIMM1 next */
0068     }
0069 
0070     fb_base = (readl(mc_regs + MC_GBASE_ADD) & MC_GADD_GBADD_MASK) << 19;
0071 
0072     iounmap(mc_regs);
0073 
0074     return dram_size - fb_base;
0075 }
0076 
0077 static void gx1_set_mode(struct fb_info *info)
0078 {
0079     struct geodefb_par *par = info->par;
0080     u32 gcfg, tcfg, ocfg, dclk_div, val;
0081     int hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal;
0082     int vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal;
0083 
0084     /* Unlock the display controller registers. */
0085     readl(par->dc_regs + DC_UNLOCK);
0086     writel(DC_UNLOCK_CODE, par->dc_regs + DC_UNLOCK);
0087 
0088     gcfg = readl(par->dc_regs + DC_GENERAL_CFG);
0089     tcfg = readl(par->dc_regs + DC_TIMING_CFG);
0090 
0091     /* Blank the display and disable the timing generator. */
0092     tcfg &= ~(DC_TCFG_BLKE | DC_TCFG_TGEN);
0093     writel(tcfg, par->dc_regs + DC_TIMING_CFG);
0094 
0095     /* Wait for pending memory requests before disabling the FIFO load. */
0096     udelay(100);
0097 
0098     /* Disable FIFO load and compression. */
0099     gcfg &= ~(DC_GCFG_DFLE | DC_GCFG_CMPE | DC_GCFG_DECE);
0100     writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
0101 
0102     /* Setup DCLK and its divisor. */
0103     gcfg &= ~DC_GCFG_DCLK_MASK;
0104     writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
0105 
0106     par->vid_ops->set_dclk(info);
0107 
0108     dclk_div = DC_GCFG_DCLK_DIV_1; /* FIXME: may need to divide DCLK by 2 sometimes? */
0109     gcfg |= dclk_div;
0110     writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
0111 
0112     /* Wait for the clock generatation to settle.  This is needed since
0113      * some of the register writes that follow require that clock to be
0114      * present. */
0115     udelay(1000); /* FIXME: seems a little long */
0116 
0117     /*
0118      * Setup new mode.
0119      */
0120 
0121     /* Clear all unused feature bits. */
0122     gcfg = DC_GCFG_VRDY | dclk_div;
0123 
0124     /* Set FIFO priority (default 6/5) and enable. */
0125     /* FIXME: increase fifo priority for 1280x1024 modes? */
0126     gcfg |= (6 << DC_GCFG_DFHPEL_POS) | (5 << DC_GCFG_DFHPSL_POS) | DC_GCFG_DFLE;
0127 
0128     /* FIXME: Set pixel and line double bits if necessary. */
0129 
0130     /* Framebuffer start offset. */
0131     writel(0, par->dc_regs + DC_FB_ST_OFFSET);
0132 
0133     /* Line delta and line buffer length. */
0134     writel(info->fix.line_length >> 2, par->dc_regs + DC_LINE_DELTA);
0135     writel(((info->var.xres * info->var.bits_per_pixel/8) >> 3) + 2,
0136            par->dc_regs + DC_BUF_SIZE);
0137 
0138     /* Output configuration. Enable panel data, set pixel format. */
0139     ocfg = DC_OCFG_PCKE | DC_OCFG_PDEL | DC_OCFG_PDEH;
0140     if (info->var.bits_per_pixel == 8) ocfg |= DC_OCFG_8BPP;
0141 
0142     /* Enable timing generator, sync and FP data. */
0143     tcfg = DC_TCFG_FPPE | DC_TCFG_HSYE | DC_TCFG_VSYE | DC_TCFG_BLKE
0144         | DC_TCFG_TGEN;
0145 
0146     /* Horizontal and vertical timings. */
0147     hactive = info->var.xres;
0148     hblankstart = hactive;
0149     hsyncstart = hblankstart + info->var.right_margin;
0150     hsyncend =  hsyncstart + info->var.hsync_len;
0151     hblankend = hsyncend + info->var.left_margin;
0152     htotal = hblankend;
0153 
0154     vactive = info->var.yres;
0155     vblankstart = vactive;
0156     vsyncstart = vblankstart + info->var.lower_margin;
0157     vsyncend =  vsyncstart + info->var.vsync_len;
0158     vblankend = vsyncend + info->var.upper_margin;
0159     vtotal = vblankend;
0160 
0161     val = (hactive - 1) | ((htotal - 1) << 16);
0162     writel(val, par->dc_regs + DC_H_TIMING_1);
0163     val = (hblankstart - 1) | ((hblankend - 1) << 16);
0164     writel(val, par->dc_regs + DC_H_TIMING_2);
0165     val = (hsyncstart - 1) | ((hsyncend - 1) << 16);
0166     writel(val, par->dc_regs + DC_H_TIMING_3);
0167     writel(val, par->dc_regs + DC_FP_H_TIMING);
0168     val = (vactive - 1) | ((vtotal - 1) << 16);
0169     writel(val, par->dc_regs + DC_V_TIMING_1);
0170     val = (vblankstart - 1) | ((vblankend - 1) << 16);
0171     writel(val, par->dc_regs + DC_V_TIMING_2);
0172     val = (vsyncstart - 1) | ((vsyncend - 1) << 16);
0173     writel(val, par->dc_regs + DC_V_TIMING_3);
0174     val = (vsyncstart - 2) | ((vsyncend - 2) << 16);
0175     writel(val, par->dc_regs + DC_FP_V_TIMING);
0176 
0177     /* Write final register values. */
0178     writel(ocfg, par->dc_regs + DC_OUTPUT_CFG);
0179     writel(tcfg, par->dc_regs + DC_TIMING_CFG);
0180     udelay(1000); /* delay after TIMING_CFG. FIXME: perhaps a little long */
0181     writel(gcfg, par->dc_regs + DC_GENERAL_CFG);
0182 
0183     par->vid_ops->configure_display(info);
0184 
0185     /* Relock display controller registers */
0186     writel(0, par->dc_regs + DC_UNLOCK);
0187 
0188     /* FIXME: write line_length and bpp to Graphics Pipeline GP_BLT_STATUS
0189      * register. */
0190 }
0191 
0192 static void gx1_set_hw_palette_reg(struct fb_info *info, unsigned regno,
0193                    unsigned red, unsigned green, unsigned blue)
0194 {
0195     struct geodefb_par *par = info->par;
0196     int val;
0197 
0198     /* Hardware palette is in RGB 6-6-6 format. */
0199     val  = (red   <<  2) & 0x3f000;
0200     val |= (green >>  4) & 0x00fc0;
0201     val |= (blue  >> 10) & 0x0003f;
0202 
0203     writel(regno, par->dc_regs + DC_PAL_ADDRESS);
0204     writel(val, par->dc_regs + DC_PAL_DATA);
0205 }
0206 
0207 const struct geode_dc_ops gx1_dc_ops = {
0208     .set_mode    = gx1_set_mode,
0209     .set_palette_reg = gx1_set_hw_palette_reg,
0210 };