aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/geode/video_gx.c
diff options
context:
space:
mode:
authorDavid Vrabel <dvrabel@arcom.com>2006-03-27 04:17:23 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-27 11:44:55 -0500
commitfc4effc7a98d0d320e478d1d42bc4a8a64380150 (patch)
tree2291ae0b2eca05c45399f6f161714f3083269f1d /drivers/video/geode/video_gx.c
parent7a07cd786dbd0111b9dd977e114438220cb4eee5 (diff)
[PATCH] fbdev: framebuffer driver for Geode GX
A framebuffer driver for the display controller in AMD Geode GX processors (Geode GX533, Geode GX500 etc.). Tested at 640x480, 800x600, 1024x768 and 1280x1024 at 8, 16, and 24 bpp with both CRT and TFT. No accelerated features currently implemented and compression remains disabled. This driver requires that the BIOS (or the SoftVG/Firmbase code in the BIOS) has created an appropriate virtual PCI header. Signed-off-by: David Vrabel <dvrabel@arcom.com> Signed-off-by: Antonino Daplas <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/video/geode/video_gx.c')
-rw-r--r--drivers/video/geode/video_gx.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/drivers/video/geode/video_gx.c b/drivers/video/geode/video_gx.c
new file mode 100644
index 000000000000..2b2a7880ea75
--- /dev/null
+++ b/drivers/video/geode/video_gx.c
@@ -0,0 +1,262 @@
1/*
2 * Geode GX video processor device.
3 *
4 * Copyright (C) 2006 Arcom Control Systems Ltd.
5 *
6 * Portions from AMD's original 2.4 driver:
7 * Copyright (C) 2004 Advanced Micro Devices, Inc.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 */
14#include <linux/fb.h>
15#include <linux/delay.h>
16#include <asm/io.h>
17#include <asm/delay.h>
18#include <asm/msr.h>
19
20#include "geodefb.h"
21#include "video_gx.h"
22
23
24/*
25 * Tables of register settings for various DOTCLKs.
26 */
27struct gx_pll_entry {
28 long pixclock; /* ps */
29 u32 sys_rstpll_bits;
30 u32 dotpll_value;
31};
32
33#define POSTDIV3 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3)
34#define PREMULT2 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPREMULT2)
35#define PREDIV2 ((u32)MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3)
36
37static const struct gx_pll_entry gx_pll_table_48MHz[] = {
38 { 40123, POSTDIV3, 0x00000BF2 }, /* 24.9230 */
39 { 39721, 0, 0x00000037 }, /* 25.1750 */
40 { 35308, POSTDIV3|PREMULT2, 0x00000B1A }, /* 28.3220 */
41 { 31746, POSTDIV3, 0x000002D2 }, /* 31.5000 */
42 { 27777, POSTDIV3|PREMULT2, 0x00000FE2 }, /* 36.0000 */
43 { 26666, POSTDIV3, 0x0000057A }, /* 37.5000 */
44 { 25000, POSTDIV3, 0x0000030A }, /* 40.0000 */
45 { 22271, 0, 0x00000063 }, /* 44.9000 */
46 { 20202, 0, 0x0000054B }, /* 49.5000 */
47 { 20000, 0, 0x0000026E }, /* 50.0000 */
48 { 19860, PREMULT2, 0x00000037 }, /* 50.3500 */
49 { 18518, POSTDIV3|PREMULT2, 0x00000B0D }, /* 54.0000 */
50 { 17777, 0, 0x00000577 }, /* 56.2500 */
51 { 17733, 0, 0x000007F7 }, /* 56.3916 */
52 { 17653, 0, 0x0000057B }, /* 56.6444 */
53 { 16949, PREMULT2, 0x00000707 }, /* 59.0000 */
54 { 15873, POSTDIV3|PREMULT2, 0x00000B39 }, /* 63.0000 */
55 { 15384, POSTDIV3|PREMULT2, 0x00000B45 }, /* 65.0000 */
56 { 14814, POSTDIV3|PREMULT2, 0x00000FC1 }, /* 67.5000 */
57 { 14124, POSTDIV3, 0x00000561 }, /* 70.8000 */
58 { 13888, POSTDIV3, 0x000007E1 }, /* 72.0000 */
59 { 13426, PREMULT2, 0x00000F4A }, /* 74.4810 */
60 { 13333, 0, 0x00000052 }, /* 75.0000 */
61 { 12698, 0, 0x00000056 }, /* 78.7500 */
62 { 12500, POSTDIV3|PREMULT2, 0x00000709 }, /* 80.0000 */
63 { 11135, PREMULT2, 0x00000262 }, /* 89.8000 */
64 { 10582, 0, 0x000002D2 }, /* 94.5000 */
65 { 10101, PREMULT2, 0x00000B4A }, /* 99.0000 */
66 { 10000, PREMULT2, 0x00000036 }, /* 100.0000 */
67 { 9259, 0, 0x000007E2 }, /* 108.0000 */
68 { 8888, 0, 0x000007F6 }, /* 112.5000 */
69 { 7692, POSTDIV3|PREMULT2, 0x00000FB0 }, /* 130.0000 */
70 { 7407, POSTDIV3|PREMULT2, 0x00000B50 }, /* 135.0000 */
71 { 6349, 0, 0x00000055 }, /* 157.5000 */
72 { 6172, 0, 0x000009C1 }, /* 162.0000 */
73 { 5787, PREMULT2, 0x0000002D }, /* 172.798 */
74 { 5698, 0, 0x000002C1 }, /* 175.5000 */
75 { 5291, 0, 0x000002D1 }, /* 189.0000 */
76 { 4938, 0, 0x00000551 }, /* 202.5000 */
77 { 4357, 0, 0x0000057D }, /* 229.5000 */
78};
79
80static const struct gx_pll_entry gx_pll_table_14MHz[] = {
81 { 39721, 0, 0x00000037 }, /* 25.1750 */
82 { 35308, 0, 0x00000B7B }, /* 28.3220 */
83 { 31746, 0, 0x000004D3 }, /* 31.5000 */
84 { 27777, 0, 0x00000BE3 }, /* 36.0000 */
85 { 26666, 0, 0x0000074F }, /* 37.5000 */
86 { 25000, 0, 0x0000050B }, /* 40.0000 */
87 { 22271, 0, 0x00000063 }, /* 44.9000 */
88 { 20202, 0, 0x0000054B }, /* 49.5000 */
89 { 20000, 0, 0x0000026E }, /* 50.0000 */
90 { 19860, 0, 0x000007C3 }, /* 50.3500 */
91 { 18518, 0, 0x000007E3 }, /* 54.0000 */
92 { 17777, 0, 0x00000577 }, /* 56.2500 */
93 { 17733, 0, 0x000002FB }, /* 56.3916 */
94 { 17653, 0, 0x0000057B }, /* 56.6444 */
95 { 16949, 0, 0x0000058B }, /* 59.0000 */
96 { 15873, 0, 0x0000095E }, /* 63.0000 */
97 { 15384, 0, 0x0000096A }, /* 65.0000 */
98 { 14814, 0, 0x00000BC2 }, /* 67.5000 */
99 { 14124, 0, 0x0000098A }, /* 70.8000 */
100 { 13888, 0, 0x00000BE2 }, /* 72.0000 */
101 { 13333, 0, 0x00000052 }, /* 75.0000 */
102 { 12698, 0, 0x00000056 }, /* 78.7500 */
103 { 12500, 0, 0x0000050A }, /* 80.0000 */
104 { 11135, 0, 0x0000078E }, /* 89.8000 */
105 { 10582, 0, 0x000002D2 }, /* 94.5000 */
106 { 10101, 0, 0x000011F6 }, /* 99.0000 */
107 { 10000, 0, 0x0000054E }, /* 100.0000 */
108 { 9259, 0, 0x000007E2 }, /* 108.0000 */
109 { 8888, 0, 0x000002FA }, /* 112.5000 */
110 { 7692, 0, 0x00000BB1 }, /* 130.0000 */
111 { 7407, 0, 0x00000975 }, /* 135.0000 */
112 { 6349, 0, 0x00000055 }, /* 157.5000 */
113 { 6172, 0, 0x000009C1 }, /* 162.0000 */
114 { 5698, 0, 0x000002C1 }, /* 175.5000 */
115 { 5291, 0, 0x00000539 }, /* 189.0000 */
116 { 4938, 0, 0x00000551 }, /* 202.5000 */
117 { 4357, 0, 0x0000057D }, /* 229.5000 */
118};
119
120static void gx_set_dclk_frequency(struct fb_info *info)
121{
122 const struct gx_pll_entry *pll_table;
123 int pll_table_len;
124 int i, best_i;
125 long min, diff;
126 u64 dotpll, sys_rstpll;
127 int timeout = 1000;
128
129 /* Rev. 1 Geode GXs use a 14 MHz reference clock instead of 48 MHz. */
130 if (cpu_data->x86_mask == 1) {
131 pll_table = gx_pll_table_14MHz;
132 pll_table_len = ARRAY_SIZE(gx_pll_table_14MHz);
133 } else {
134 pll_table = gx_pll_table_48MHz;
135 pll_table_len = ARRAY_SIZE(gx_pll_table_48MHz);
136 }
137
138 /* Search the table for the closest pixclock. */
139 best_i = 0;
140 min = abs(pll_table[0].pixclock - info->var.pixclock);
141 for (i = 1; i < pll_table_len; i++) {
142 diff = abs(pll_table[i].pixclock - info->var.pixclock);
143 if (diff < min) {
144 min = diff;
145 best_i = i;
146 }
147 }
148
149 rdmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll);
150 rdmsrl(MSR_GLCP_DOTPLL, dotpll);
151
152 /* Program new M, N and P. */
153 dotpll &= 0x00000000ffffffffull;
154 dotpll |= (u64)pll_table[best_i].dotpll_value << 32;
155 dotpll |= MSR_GLCP_DOTPLL_DOTRESET;
156 dotpll &= ~MSR_GLCP_DOTPLL_BYPASS;
157
158 wrmsrl(MSR_GLCP_DOTPLL, dotpll);
159
160 /* Program dividers. */
161 sys_rstpll &= ~( MSR_GLCP_SYS_RSTPLL_DOTPREDIV2
162 | MSR_GLCP_SYS_RSTPLL_DOTPREMULT2
163 | MSR_GLCP_SYS_RSTPLL_DOTPOSTDIV3 );
164 sys_rstpll |= pll_table[best_i].sys_rstpll_bits;
165
166 wrmsrl(MSR_GLCP_SYS_RSTPLL, sys_rstpll);
167
168 /* Clear reset bit to start PLL. */
169 dotpll &= ~(MSR_GLCP_DOTPLL_DOTRESET);
170 wrmsrl(MSR_GLCP_DOTPLL, dotpll);
171
172 /* Wait for LOCK bit. */
173 do {
174 rdmsrl(MSR_GLCP_DOTPLL, dotpll);
175 } while (timeout-- && !(dotpll & MSR_GLCP_DOTPLL_LOCK));
176}
177
178static void gx_configure_display(struct fb_info *info)
179{
180 struct geodefb_par *par = info->par;
181 u32 dcfg, fp_pm;
182
183 dcfg = readl(par->vid_regs + GX_DCFG);
184
185 /* Clear bits from existing mode. */
186 dcfg &= ~(GX_DCFG_CRT_SYNC_SKW_MASK
187 | GX_DCFG_CRT_HSYNC_POL | GX_DCFG_CRT_VSYNC_POL
188 | GX_DCFG_VSYNC_EN | GX_DCFG_HSYNC_EN);
189
190 /* Set default sync skew. */
191 dcfg |= GX_DCFG_CRT_SYNC_SKW_DFLT;
192
193 /* Enable hsync and vsync. */
194 dcfg |= GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN;
195
196 /* Sync polarities. */
197 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
198 dcfg |= GX_DCFG_CRT_HSYNC_POL;
199 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
200 dcfg |= GX_DCFG_CRT_VSYNC_POL;
201
202 writel(dcfg, par->vid_regs + GX_DCFG);
203
204 /* Power on flat panel. */
205 fp_pm = readl(par->vid_regs + GX_FP_PM);
206 fp_pm |= GX_FP_PM_P;
207 writel(fp_pm, par->vid_regs + GX_FP_PM);
208}
209
210static int gx_blank_display(struct fb_info *info, int blank_mode)
211{
212 struct geodefb_par *par = info->par;
213 u32 dcfg, fp_pm;
214 int blank, hsync, vsync;
215
216 /* CRT power saving modes. */
217 switch (blank_mode) {
218 case FB_BLANK_UNBLANK:
219 blank = 0; hsync = 1; vsync = 1;
220 break;
221 case FB_BLANK_NORMAL:
222 blank = 1; hsync = 1; vsync = 1;
223 break;
224 case FB_BLANK_VSYNC_SUSPEND:
225 blank = 1; hsync = 1; vsync = 0;
226 break;
227 case FB_BLANK_HSYNC_SUSPEND:
228 blank = 1; hsync = 0; vsync = 1;
229 break;
230 case FB_BLANK_POWERDOWN:
231 blank = 1; hsync = 0; vsync = 0;
232 break;
233 default:
234 return -EINVAL;
235 }
236 dcfg = readl(par->vid_regs + GX_DCFG);
237 dcfg &= ~(GX_DCFG_DAC_BL_EN
238 | GX_DCFG_HSYNC_EN | GX_DCFG_VSYNC_EN);
239 if (!blank)
240 dcfg |= GX_DCFG_DAC_BL_EN;
241 if (hsync)
242 dcfg |= GX_DCFG_HSYNC_EN;
243 if (vsync)
244 dcfg |= GX_DCFG_VSYNC_EN;
245 writel(dcfg, par->vid_regs + GX_DCFG);
246
247 /* Power on/off flat panel. */
248 fp_pm = readl(par->vid_regs + GX_FP_PM);
249 if (blank_mode == FB_BLANK_POWERDOWN)
250 fp_pm &= ~GX_FP_PM_P;
251 else
252 fp_pm |= GX_FP_PM_P;
253 writel(fp_pm, par->vid_regs + GX_FP_PM);
254
255 return 0;
256}
257
258struct geode_vid_ops gx_vid_ops = {
259 .set_dclk = gx_set_dclk_frequency,
260 .configure_display = gx_configure_display,
261 .blank_display = gx_blank_display,
262};