1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * drivers/video/geode/display_gx1.c
4 * -- Geode GX1 display controller
6 * Copyright (C) 2005 Arcom Control Systems Ltd.
8 * Based on AMD's original 2.4 driver:
9 * Copyright (C) 2004 Advanced Micro Devices, Inc.
11 #include <linux/spinlock.h>
13 #include <linux/delay.h>
15 #include <asm/div64.h>
16 #include <asm/delay.h>
19 #include "display_gx1.h"
21 static DEFINE_SPINLOCK(gx1_conf_reg_lock
);
23 static u8
gx1_read_conf_reg(u8 reg
)
28 spin_lock_irqsave(&gx1_conf_reg_lock
, flags
);
30 outb(CONFIG_CCR3
, 0x22);
32 outb(CONFIG_CCR3
, 0x22);
33 outb(ccr3
| CONFIG_CCR3_MAPEN
, 0x23);
36 outb(CONFIG_CCR3
, 0x22);
39 spin_unlock_irqrestore(&gx1_conf_reg_lock
, flags
);
44 unsigned gx1_gx_base(void)
46 return (gx1_read_conf_reg(CONFIG_GCR
) & 0x03) << 30;
49 int gx1_frame_buffer_size(void)
51 void __iomem
*mc_regs
;
54 unsigned dram_size
= 0, fb_base
;
56 mc_regs
= ioremap(gx1_gx_base() + 0x8400, 0x100);
61 /* Calculate the total size of both DIMM0 and DIMM1. */
62 bank_cfg
= readl(mc_regs
+ MC_BANK_CFG
);
64 for (d
= 0; d
< 2; d
++) {
65 if ((bank_cfg
& MC_BCFG_DIMM0_PG_SZ_MASK
) != MC_BCFG_DIMM0_PG_SZ_NO_DIMM
)
66 dram_size
+= 0x400000 << ((bank_cfg
& MC_BCFG_DIMM0_SZ_MASK
) >> 8);
67 bank_cfg
>>= 16; /* look at DIMM1 next */
70 fb_base
= (readl(mc_regs
+ MC_GBASE_ADD
) & MC_GADD_GBADD_MASK
) << 19;
74 return dram_size
- fb_base
;
77 static void gx1_set_mode(struct fb_info
*info
)
79 struct geodefb_par
*par
= info
->par
;
80 u32 gcfg
, tcfg
, ocfg
, dclk_div
, val
;
81 int hactive
, hblankstart
, hsyncstart
, hsyncend
, hblankend
, htotal
;
82 int vactive
, vblankstart
, vsyncstart
, vsyncend
, vblankend
, vtotal
;
84 /* Unlock the display controller registers. */
85 readl(par
->dc_regs
+ DC_UNLOCK
);
86 writel(DC_UNLOCK_CODE
, par
->dc_regs
+ DC_UNLOCK
);
88 gcfg
= readl(par
->dc_regs
+ DC_GENERAL_CFG
);
89 tcfg
= readl(par
->dc_regs
+ DC_TIMING_CFG
);
91 /* Blank the display and disable the timing generator. */
92 tcfg
&= ~(DC_TCFG_BLKE
| DC_TCFG_TGEN
);
93 writel(tcfg
, par
->dc_regs
+ DC_TIMING_CFG
);
95 /* Wait for pending memory requests before disabling the FIFO load. */
98 /* Disable FIFO load and compression. */
99 gcfg
&= ~(DC_GCFG_DFLE
| DC_GCFG_CMPE
| DC_GCFG_DECE
);
100 writel(gcfg
, par
->dc_regs
+ DC_GENERAL_CFG
);
102 /* Setup DCLK and its divisor. */
103 gcfg
&= ~DC_GCFG_DCLK_MASK
;
104 writel(gcfg
, par
->dc_regs
+ DC_GENERAL_CFG
);
106 par
->vid_ops
->set_dclk(info
);
108 dclk_div
= DC_GCFG_DCLK_DIV_1
; /* FIXME: may need to divide DCLK by 2 sometimes? */
110 writel(gcfg
, par
->dc_regs
+ DC_GENERAL_CFG
);
112 /* Wait for the clock generatation to settle. This is needed since
113 * some of the register writes that follow require that clock to be
115 udelay(1000); /* FIXME: seems a little long */
121 /* Clear all unused feature bits. */
122 gcfg
= DC_GCFG_VRDY
| dclk_div
;
124 /* Set FIFO priority (default 6/5) and enable. */
125 /* FIXME: increase fifo priority for 1280x1024 modes? */
126 gcfg
|= (6 << DC_GCFG_DFHPEL_POS
) | (5 << DC_GCFG_DFHPSL_POS
) | DC_GCFG_DFLE
;
128 /* FIXME: Set pixel and line double bits if necessary. */
130 /* Framebuffer start offset. */
131 writel(0, par
->dc_regs
+ DC_FB_ST_OFFSET
);
133 /* Line delta and line buffer length. */
134 writel(info
->fix
.line_length
>> 2, par
->dc_regs
+ DC_LINE_DELTA
);
135 writel(((info
->var
.xres
* info
->var
.bits_per_pixel
/8) >> 3) + 2,
136 par
->dc_regs
+ DC_BUF_SIZE
);
138 /* Output configuration. Enable panel data, set pixel format. */
139 ocfg
= DC_OCFG_PCKE
| DC_OCFG_PDEL
| DC_OCFG_PDEH
;
140 if (info
->var
.bits_per_pixel
== 8) ocfg
|= DC_OCFG_8BPP
;
142 /* Enable timing generator, sync and FP data. */
143 tcfg
= DC_TCFG_FPPE
| DC_TCFG_HSYE
| DC_TCFG_VSYE
| DC_TCFG_BLKE
146 /* Horizontal and vertical timings. */
147 hactive
= info
->var
.xres
;
148 hblankstart
= hactive
;
149 hsyncstart
= hblankstart
+ info
->var
.right_margin
;
150 hsyncend
= hsyncstart
+ info
->var
.hsync_len
;
151 hblankend
= hsyncend
+ info
->var
.left_margin
;
154 vactive
= info
->var
.yres
;
155 vblankstart
= vactive
;
156 vsyncstart
= vblankstart
+ info
->var
.lower_margin
;
157 vsyncend
= vsyncstart
+ info
->var
.vsync_len
;
158 vblankend
= vsyncend
+ info
->var
.upper_margin
;
161 val
= (hactive
- 1) | ((htotal
- 1) << 16);
162 writel(val
, par
->dc_regs
+ DC_H_TIMING_1
);
163 val
= (hblankstart
- 1) | ((hblankend
- 1) << 16);
164 writel(val
, par
->dc_regs
+ DC_H_TIMING_2
);
165 val
= (hsyncstart
- 1) | ((hsyncend
- 1) << 16);
166 writel(val
, par
->dc_regs
+ DC_H_TIMING_3
);
167 writel(val
, par
->dc_regs
+ DC_FP_H_TIMING
);
168 val
= (vactive
- 1) | ((vtotal
- 1) << 16);
169 writel(val
, par
->dc_regs
+ DC_V_TIMING_1
);
170 val
= (vblankstart
- 1) | ((vblankend
- 1) << 16);
171 writel(val
, par
->dc_regs
+ DC_V_TIMING_2
);
172 val
= (vsyncstart
- 1) | ((vsyncend
- 1) << 16);
173 writel(val
, par
->dc_regs
+ DC_V_TIMING_3
);
174 val
= (vsyncstart
- 2) | ((vsyncend
- 2) << 16);
175 writel(val
, par
->dc_regs
+ DC_FP_V_TIMING
);
177 /* Write final register values. */
178 writel(ocfg
, par
->dc_regs
+ DC_OUTPUT_CFG
);
179 writel(tcfg
, par
->dc_regs
+ DC_TIMING_CFG
);
180 udelay(1000); /* delay after TIMING_CFG. FIXME: perhaps a little long */
181 writel(gcfg
, par
->dc_regs
+ DC_GENERAL_CFG
);
183 par
->vid_ops
->configure_display(info
);
185 /* Relock display controller registers */
186 writel(0, par
->dc_regs
+ DC_UNLOCK
);
188 /* FIXME: write line_length and bpp to Graphics Pipeline GP_BLT_STATUS
192 static void gx1_set_hw_palette_reg(struct fb_info
*info
, unsigned regno
,
193 unsigned red
, unsigned green
, unsigned blue
)
195 struct geodefb_par
*par
= info
->par
;
198 /* Hardware palette is in RGB 6-6-6 format. */
199 val
= (red
<< 2) & 0x3f000;
200 val
|= (green
>> 4) & 0x00fc0;
201 val
|= (blue
>> 10) & 0x0003f;
203 writel(regno
, par
->dc_regs
+ DC_PAL_ADDRESS
);
204 writel(val
, par
->dc_regs
+ DC_PAL_DATA
);
207 const struct geode_dc_ops gx1_dc_ops
= {
208 .set_mode
= gx1_set_mode
,
209 .set_palette_reg
= gx1_set_hw_palette_reg
,