2 * drivers/video/geode/display_gx1.c
3 * -- Geode GX1 display controller
5 * Copyright (C) 2005 Arcom Control Systems Ltd.
7 * Based on AMD's original 2.4 driver:
8 * Copyright (C) 2004 Advanced Micro Devices, Inc.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 #include <linux/spinlock.h>
17 #include <linux/delay.h>
19 #include <asm/div64.h>
20 #include <asm/delay.h>
23 #include "display_gx1.h"
25 static spinlock_t gx1_conf_reg_lock
= SPIN_LOCK_UNLOCKED
;
27 static u8
gx1_read_conf_reg(u8 reg
)
32 spin_lock_irqsave(&gx1_conf_reg_lock
, flags
);
34 outb(CONFIG_CCR3
, 0x22);
36 outb(CONFIG_CCR3
, 0x22);
37 outb(ccr3
| CONFIG_CCR3_MAPEN
, 0x23);
40 outb(CONFIG_CCR3
, 0x22);
43 spin_unlock_irqrestore(&gx1_conf_reg_lock
, flags
);
48 unsigned gx1_gx_base(void)
50 return (gx1_read_conf_reg(CONFIG_GCR
) & 0x03) << 30;
53 int gx1_frame_buffer_size(void)
55 void __iomem
*mc_regs
;
58 unsigned dram_size
= 0, fb_base
;
60 mc_regs
= ioremap(gx1_gx_base() + 0x8400, 0x100);
65 /* Calculate the total size of both DIMM0 and DIMM1. */
66 bank_cfg
= readl(mc_regs
+ MC_BANK_CFG
);
68 for (d
= 0; d
< 2; d
++) {
69 if ((bank_cfg
& MC_BCFG_DIMM0_PG_SZ_MASK
) != MC_BCFG_DIMM0_PG_SZ_NO_DIMM
)
70 dram_size
+= 0x400000 << ((bank_cfg
& MC_BCFG_DIMM0_SZ_MASK
) >> 8);
71 bank_cfg
>>= 16; /* look at DIMM1 next */
74 fb_base
= (readl(mc_regs
+ MC_GBASE_ADD
) & MC_GADD_GBADD_MASK
) << 19;
78 return dram_size
- fb_base
;
81 static void gx1_set_mode(struct fb_info
*info
)
83 struct geodefb_par
*par
= info
->par
;
84 u32 gcfg
, tcfg
, ocfg
, dclk_div
, val
;
85 int hactive
, hblankstart
, hsyncstart
, hsyncend
, hblankend
, htotal
;
86 int vactive
, vblankstart
, vsyncstart
, vsyncend
, vblankend
, vtotal
;
88 /* Unlock the display controller registers. */
89 readl(par
->dc_regs
+ DC_UNLOCK
);
90 writel(DC_UNLOCK_CODE
, par
->dc_regs
+ DC_UNLOCK
);
92 gcfg
= readl(par
->dc_regs
+ DC_GENERAL_CFG
);
93 tcfg
= readl(par
->dc_regs
+ DC_TIMING_CFG
);
95 /* Blank the display and disable the timing generator. */
96 tcfg
&= ~(DC_TCFG_BLKE
| DC_TCFG_TGEN
);
97 writel(tcfg
, par
->dc_regs
+ DC_TIMING_CFG
);
99 /* Wait for pending memory requests before disabling the FIFO load. */
102 /* Disable FIFO load and compression. */
103 gcfg
&= ~(DC_GCFG_DFLE
| DC_GCFG_CMPE
| DC_GCFG_DECE
);
104 writel(gcfg
, par
->dc_regs
+ DC_GENERAL_CFG
);
106 /* Setup DCLK and its divisor. */
107 gcfg
&= ~DC_GCFG_DCLK_MASK
;
108 writel(gcfg
, par
->dc_regs
+ DC_GENERAL_CFG
);
110 par
->vid_ops
->set_dclk(info
);
112 dclk_div
= DC_GCFG_DCLK_DIV_1
; /* FIXME: may need to divide DCLK by 2 sometimes? */
114 writel(gcfg
, par
->dc_regs
+ DC_GENERAL_CFG
);
116 /* Wait for the clock generatation to settle. This is needed since
117 * some of the register writes that follow require that clock to be
119 udelay(1000); /* FIXME: seems a little long */
125 /* Clear all unused feature bits. */
126 gcfg
= DC_GCFG_VRDY
| dclk_div
;
128 /* Set FIFO priority (default 6/5) and enable. */
129 /* FIXME: increase fifo priority for 1280x1024 modes? */
130 gcfg
|= (6 << DC_GCFG_DFHPEL_POS
) | (5 << DC_GCFG_DFHPSL_POS
) | DC_GCFG_DFLE
;
132 /* FIXME: Set pixel and line double bits if necessary. */
134 /* Framebuffer start offset. */
135 writel(0, par
->dc_regs
+ DC_FB_ST_OFFSET
);
137 /* Line delta and line buffer length. */
138 writel(info
->fix
.line_length
>> 2, par
->dc_regs
+ DC_LINE_DELTA
);
139 writel(((info
->var
.xres
* info
->var
.bits_per_pixel
/8) >> 3) + 2,
140 par
->dc_regs
+ DC_BUF_SIZE
);
142 /* Output configuration. Enable panel data, set pixel format. */
143 ocfg
= DC_OCFG_PCKE
| DC_OCFG_PDEL
| DC_OCFG_PDEH
;
144 if (info
->var
.bits_per_pixel
== 8) ocfg
|= DC_OCFG_8BPP
;
146 /* Enable timing generator, sync and FP data. */
147 tcfg
= DC_TCFG_FPPE
| DC_TCFG_HSYE
| DC_TCFG_VSYE
| DC_TCFG_BLKE
150 /* Horizontal and vertical timings. */
151 hactive
= info
->var
.xres
;
152 hblankstart
= hactive
;
153 hsyncstart
= hblankstart
+ info
->var
.right_margin
;
154 hsyncend
= hsyncstart
+ info
->var
.hsync_len
;
155 hblankend
= hsyncend
+ info
->var
.left_margin
;
158 vactive
= info
->var
.yres
;
159 vblankstart
= vactive
;
160 vsyncstart
= vblankstart
+ info
->var
.lower_margin
;
161 vsyncend
= vsyncstart
+ info
->var
.vsync_len
;
162 vblankend
= vsyncend
+ info
->var
.upper_margin
;
165 val
= (hactive
- 1) | ((htotal
- 1) << 16);
166 writel(val
, par
->dc_regs
+ DC_H_TIMING_1
);
167 val
= (hblankstart
- 1) | ((hblankend
- 1) << 16);
168 writel(val
, par
->dc_regs
+ DC_H_TIMING_2
);
169 val
= (hsyncstart
- 1) | ((hsyncend
- 1) << 16);
170 writel(val
, par
->dc_regs
+ DC_H_TIMING_3
);
171 writel(val
, par
->dc_regs
+ DC_FP_H_TIMING
);
172 val
= (vactive
- 1) | ((vtotal
- 1) << 16);
173 writel(val
, par
->dc_regs
+ DC_V_TIMING_1
);
174 val
= (vblankstart
- 1) | ((vblankend
- 1) << 16);
175 writel(val
, par
->dc_regs
+ DC_V_TIMING_2
);
176 val
= (vsyncstart
- 1) | ((vsyncend
- 1) << 16);
177 writel(val
, par
->dc_regs
+ DC_V_TIMING_3
);
178 val
= (vsyncstart
- 2) | ((vsyncend
- 2) << 16);
179 writel(val
, par
->dc_regs
+ DC_FP_V_TIMING
);
181 /* Write final register values. */
182 writel(ocfg
, par
->dc_regs
+ DC_OUTPUT_CFG
);
183 writel(tcfg
, par
->dc_regs
+ DC_TIMING_CFG
);
184 udelay(1000); /* delay after TIMING_CFG. FIXME: perhaps a little long */
185 writel(gcfg
, par
->dc_regs
+ DC_GENERAL_CFG
);
187 par
->vid_ops
->configure_display(info
);
189 /* Relock display controller registers */
190 writel(0, par
->dc_regs
+ DC_UNLOCK
);
192 /* FIXME: write line_length and bpp to Graphics Pipeline GP_BLT_STATUS
196 static void gx1_set_hw_palette_reg(struct fb_info
*info
, unsigned regno
,
197 unsigned red
, unsigned green
, unsigned blue
)
199 struct geodefb_par
*par
= info
->par
;
202 /* Hardware palette is in RGB 6-6-6 format. */
203 val
= (red
<< 2) & 0x3f000;
204 val
|= (green
>> 4) & 0x00fc0;
205 val
|= (blue
>> 10) & 0x0003f;
207 writel(regno
, par
->dc_regs
+ DC_PAL_ADDRESS
);
208 writel(val
, par
->dc_regs
+ DC_PAL_DATA
);
211 struct geode_dc_ops gx1_dc_ops
= {
212 .set_mode
= gx1_set_mode
,
213 .set_palette_reg
= gx1_set_hw_palette_reg
,