diff options
Diffstat (limited to 'drivers/video/carminefb.c')
-rw-r--r-- | drivers/video/carminefb.c | 790 |
1 files changed, 790 insertions, 0 deletions
diff --git a/drivers/video/carminefb.c b/drivers/video/carminefb.c new file mode 100644 index 000000000000..e15bb447440a --- /dev/null +++ b/drivers/video/carminefb.c | |||
@@ -0,0 +1,790 @@ | |||
1 | /* | ||
2 | * Frame buffer driver for the Carmine GPU. | ||
3 | * | ||
4 | * The driver configures the GPU as follows | ||
5 | * - FB0 is display 0 with unique memory area | ||
6 | * - FB1 is display 1 with unique memory area | ||
7 | * - both display use 32 bit colors | ||
8 | */ | ||
9 | #include <linux/delay.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/fb.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/pci.h> | ||
14 | |||
15 | #include "carminefb.h" | ||
16 | #include "carminefb_regs.h" | ||
17 | |||
18 | #if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) | ||
19 | #error "The endianness of the target host has not been defined." | ||
20 | #endif | ||
21 | |||
22 | /* | ||
23 | * The initial video mode can be supplied via two different ways: | ||
24 | * - as a string that is passed to fb_find_mode() (module option fb_mode_str) | ||
25 | * - as an integer that picks the video mode from carmine_modedb[] (module | ||
26 | * option fb_mode) | ||
27 | * | ||
28 | * If nothing is used than the initial video mode will be the | ||
29 | * CARMINEFB_DEFAULT_VIDEO_MODE member of the carmine_modedb[]. | ||
30 | */ | ||
31 | #define CARMINEFB_DEFAULT_VIDEO_MODE 1 | ||
32 | |||
33 | static unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE; | ||
34 | module_param(fb_mode, uint, 444); | ||
35 | MODULE_PARM_DESC(fb_mode, "Initial video mode as integer."); | ||
36 | |||
37 | static char *fb_mode_str; | ||
38 | module_param(fb_mode_str, charp, 444); | ||
39 | MODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters."); | ||
40 | |||
41 | /* | ||
42 | * Carminefb displays: | ||
43 | * 0b000 None | ||
44 | * 0b001 Display 0 | ||
45 | * 0b010 Display 1 | ||
46 | */ | ||
47 | static int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1; | ||
48 | module_param(fb_displays, int, 444); | ||
49 | MODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used"); | ||
50 | |||
51 | struct carmine_hw { | ||
52 | void __iomem *v_regs; | ||
53 | void __iomem *screen_mem; | ||
54 | struct fb_info *fb[MAX_DISPLAY]; | ||
55 | }; | ||
56 | |||
57 | struct carmine_resolution { | ||
58 | u32 htp; | ||
59 | u32 hsp; | ||
60 | u32 hsw; | ||
61 | u32 hdp; | ||
62 | u32 vtr; | ||
63 | u32 vsp; | ||
64 | u32 vsw; | ||
65 | u32 vdp; | ||
66 | u32 disp_mode; | ||
67 | }; | ||
68 | |||
69 | struct carmine_fb { | ||
70 | void __iomem *display_reg; | ||
71 | void __iomem *screen_base; | ||
72 | u32 smem_offset; | ||
73 | u32 cur_mode; | ||
74 | u32 new_mode; | ||
75 | struct carmine_resolution *res; | ||
76 | u32 pseudo_palette[16]; | ||
77 | }; | ||
78 | |||
79 | static struct fb_fix_screeninfo carminefb_fix __devinitdata = { | ||
80 | .id = "Carmine", | ||
81 | .type = FB_TYPE_PACKED_PIXELS, | ||
82 | .visual = FB_VISUAL_TRUECOLOR, | ||
83 | .accel = FB_ACCEL_NONE, | ||
84 | }; | ||
85 | |||
86 | static const struct fb_videomode carmine_modedb[] = { | ||
87 | { | ||
88 | .name = "640x480", | ||
89 | .xres = 640, | ||
90 | .yres = 480, | ||
91 | }, { | ||
92 | .name = "800x600", | ||
93 | .xres = 800, | ||
94 | .yres = 600, | ||
95 | }, | ||
96 | }; | ||
97 | |||
98 | static struct carmine_resolution car_modes[] = { | ||
99 | { | ||
100 | /* 640x480 */ | ||
101 | .htp = 800, | ||
102 | .hsp = 672, | ||
103 | .hsw = 96, | ||
104 | .hdp = 640, | ||
105 | .vtr = 525, | ||
106 | .vsp = 490, | ||
107 | .vsw = 2, | ||
108 | .vdp = 480, | ||
109 | .disp_mode = 0x1400, | ||
110 | }, | ||
111 | { | ||
112 | /* 800x600 */ | ||
113 | .htp = 1060, | ||
114 | .hsp = 864, | ||
115 | .hsw = 72, | ||
116 | .hdp = 800, | ||
117 | .vtr = 628, | ||
118 | .vsp = 601, | ||
119 | .vsw = 2, | ||
120 | .vdp = 600, | ||
121 | .disp_mode = 0x0d00, | ||
122 | } | ||
123 | }; | ||
124 | |||
125 | static int carmine_find_mode(const struct fb_var_screeninfo *var) | ||
126 | { | ||
127 | int i; | ||
128 | |||
129 | for (i = 0; i < ARRAY_SIZE(car_modes); i++) | ||
130 | if (car_modes[i].hdp == var->xres && | ||
131 | car_modes[i].vdp == var->yres) | ||
132 | return i; | ||
133 | return -EINVAL; | ||
134 | } | ||
135 | |||
136 | static void c_set_disp_reg(const struct carmine_fb *par, | ||
137 | u32 offset, u32 val) | ||
138 | { | ||
139 | writel(val, par->display_reg + offset); | ||
140 | } | ||
141 | |||
142 | static u32 c_get_disp_reg(const struct carmine_fb *par, | ||
143 | u32 offset) | ||
144 | { | ||
145 | return readl(par->display_reg + offset); | ||
146 | } | ||
147 | |||
148 | static void c_set_hw_reg(const struct carmine_hw *hw, | ||
149 | u32 offset, u32 val) | ||
150 | { | ||
151 | writel(val, hw->v_regs + offset); | ||
152 | } | ||
153 | |||
154 | static u32 c_get_hw_reg(const struct carmine_hw *hw, | ||
155 | u32 offset) | ||
156 | { | ||
157 | return readl(hw->v_regs + offset); | ||
158 | } | ||
159 | |||
160 | static int carmine_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
161 | unsigned blue, unsigned transp, struct fb_info *info) | ||
162 | { | ||
163 | if (regno >= 16) | ||
164 | return 1; | ||
165 | |||
166 | red >>= 8; | ||
167 | green >>= 8; | ||
168 | blue >>= 8; | ||
169 | transp >>= 8; | ||
170 | |||
171 | ((u32 *)info->pseudo_palette)[regno] = be32_to_cpu(transp << 24 | | ||
172 | red << 0 | green << 8 | blue << 16); | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int carmine_check_var(struct fb_var_screeninfo *var, | ||
177 | struct fb_info *info) | ||
178 | { | ||
179 | int ret; | ||
180 | |||
181 | ret = carmine_find_mode(var); | ||
182 | if (ret < 0) | ||
183 | return ret; | ||
184 | |||
185 | if (var->grayscale || var->rotate || var->nonstd) | ||
186 | return -EINVAL; | ||
187 | |||
188 | var->xres_virtual = var->xres; | ||
189 | var->yres_virtual = var->yres; | ||
190 | |||
191 | var->bits_per_pixel = 32; | ||
192 | |||
193 | #ifdef __BIG_ENDIAN | ||
194 | var->transp.offset = 24; | ||
195 | var->red.offset = 0; | ||
196 | var->green.offset = 8; | ||
197 | var->blue.offset = 16; | ||
198 | #else | ||
199 | var->transp.offset = 24; | ||
200 | var->red.offset = 16; | ||
201 | var->green.offset = 8; | ||
202 | var->blue.offset = 0; | ||
203 | #endif | ||
204 | |||
205 | var->red.length = 8; | ||
206 | var->green.length = 8; | ||
207 | var->blue.length = 8; | ||
208 | var->transp.length = 8; | ||
209 | |||
210 | var->red.msb_right = 0; | ||
211 | var->green.msb_right = 0; | ||
212 | var->blue.msb_right = 0; | ||
213 | var->transp.msb_right = 0; | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static void carmine_init_display_param(struct carmine_fb *par) | ||
218 | { | ||
219 | u32 width; | ||
220 | u32 height; | ||
221 | u32 param; | ||
222 | u32 window_size; | ||
223 | u32 soffset = par->smem_offset; | ||
224 | |||
225 | c_set_disp_reg(par, CARMINE_DISP_REG_C_TRANS, 0); | ||
226 | c_set_disp_reg(par, CARMINE_DISP_REG_MLMR_TRANS, 0); | ||
227 | c_set_disp_reg(par, CARMINE_DISP_REG_CURSOR_MODE, | ||
228 | CARMINE_CURSOR0_PRIORITY_MASK | | ||
229 | CARMINE_CURSOR1_PRIORITY_MASK | | ||
230 | CARMINE_CURSOR_CUTZ_MASK); | ||
231 | |||
232 | /* Set default cursor position */ | ||
233 | c_set_disp_reg(par, CARMINE_DISP_REG_CUR1_POS, 0 << 16 | 0); | ||
234 | c_set_disp_reg(par, CARMINE_DISP_REG_CUR2_POS, 0 << 16 | 0); | ||
235 | |||
236 | /* Set default display mode */ | ||
237 | c_set_disp_reg(par, CARMINE_DISP_REG_L0_EXT_MODE, CARMINE_WINDOW_MODE | | ||
238 | CARMINE_EXT_CMODE_DIRECT24_RGBA); | ||
239 | c_set_disp_reg(par, CARMINE_DISP_REG_L1_EXT_MODE, | ||
240 | CARMINE_EXT_CMODE_DIRECT24_RGBA); | ||
241 | c_set_disp_reg(par, CARMINE_DISP_REG_L2_EXT_MODE, CARMINE_EXTEND_MODE | | ||
242 | CARMINE_EXT_CMODE_DIRECT24_RGBA); | ||
243 | c_set_disp_reg(par, CARMINE_DISP_REG_L3_EXT_MODE, CARMINE_EXTEND_MODE | | ||
244 | CARMINE_EXT_CMODE_DIRECT24_RGBA); | ||
245 | c_set_disp_reg(par, CARMINE_DISP_REG_L4_EXT_MODE, CARMINE_EXTEND_MODE | | ||
246 | CARMINE_EXT_CMODE_DIRECT24_RGBA); | ||
247 | c_set_disp_reg(par, CARMINE_DISP_REG_L5_EXT_MODE, CARMINE_EXTEND_MODE | | ||
248 | CARMINE_EXT_CMODE_DIRECT24_RGBA); | ||
249 | c_set_disp_reg(par, CARMINE_DISP_REG_L6_EXT_MODE, CARMINE_EXTEND_MODE | | ||
250 | CARMINE_EXT_CMODE_DIRECT24_RGBA); | ||
251 | c_set_disp_reg(par, CARMINE_DISP_REG_L7_EXT_MODE, CARMINE_EXTEND_MODE | | ||
252 | CARMINE_EXT_CMODE_DIRECT24_RGBA); | ||
253 | |||
254 | /* Set default frame size to layer mode register */ | ||
255 | width = par->res->hdp * 4 / CARMINE_DISP_WIDTH_UNIT; | ||
256 | width = width << CARMINE_DISP_WIDTH_SHIFT; | ||
257 | |||
258 | height = par->res->vdp - 1; | ||
259 | param = width | height; | ||
260 | |||
261 | c_set_disp_reg(par, CARMINE_DISP_REG_L0_MODE_W_H, param); | ||
262 | c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIDTH, width); | ||
263 | c_set_disp_reg(par, CARMINE_DISP_REG_L2_MODE_W_H, param); | ||
264 | c_set_disp_reg(par, CARMINE_DISP_REG_L3_MODE_W_H, param); | ||
265 | c_set_disp_reg(par, CARMINE_DISP_REG_L4_MODE_W_H, param); | ||
266 | c_set_disp_reg(par, CARMINE_DISP_REG_L5_MODE_W_H, param); | ||
267 | c_set_disp_reg(par, CARMINE_DISP_REG_L6_MODE_W_H, param); | ||
268 | c_set_disp_reg(par, CARMINE_DISP_REG_L7_MODE_W_H, param); | ||
269 | |||
270 | /* Set default pos and size */ | ||
271 | window_size = (par->res->vdp - 1) << CARMINE_DISP_WIN_H_SHIFT; | ||
272 | window_size |= par->res->hdp; | ||
273 | |||
274 | c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_POS, 0); | ||
275 | c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_SIZE, window_size); | ||
276 | c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_POS, 0); | ||
277 | c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_SIZE, window_size); | ||
278 | c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_POS, 0); | ||
279 | c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_SIZE, window_size); | ||
280 | c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_POS, 0); | ||
281 | c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_SIZE, window_size); | ||
282 | c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_POS, 0); | ||
283 | c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_SIZE, window_size); | ||
284 | c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_POS, 0); | ||
285 | c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_SIZE, window_size); | ||
286 | c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_POS, 0); | ||
287 | c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_SIZE, window_size); | ||
288 | c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_POS, 0); | ||
289 | c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_SIZE, window_size); | ||
290 | |||
291 | /* Set default origin address */ | ||
292 | c_set_disp_reg(par, CARMINE_DISP_REG_L0_ORG_ADR, soffset); | ||
293 | c_set_disp_reg(par, CARMINE_DISP_REG_L1_ORG_ADR, soffset); | ||
294 | c_set_disp_reg(par, CARMINE_DISP_REG_L2_ORG_ADR1, soffset); | ||
295 | c_set_disp_reg(par, CARMINE_DISP_REG_L3_ORG_ADR1, soffset); | ||
296 | c_set_disp_reg(par, CARMINE_DISP_REG_L4_ORG_ADR1, soffset); | ||
297 | c_set_disp_reg(par, CARMINE_DISP_REG_L5_ORG_ADR1, soffset); | ||
298 | c_set_disp_reg(par, CARMINE_DISP_REG_L6_ORG_ADR1, soffset); | ||
299 | c_set_disp_reg(par, CARMINE_DISP_REG_L7_ORG_ADR1, soffset); | ||
300 | |||
301 | /* Set default display address */ | ||
302 | c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_ADR, soffset); | ||
303 | c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_ADR1, soffset); | ||
304 | c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_ADR1, soffset); | ||
305 | c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_ADR1, soffset); | ||
306 | c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_ADR1, soffset); | ||
307 | c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_ADR0, soffset); | ||
308 | c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_ADR0, soffset); | ||
309 | |||
310 | /* Set default display position */ | ||
311 | c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_POS, 0); | ||
312 | c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_POS, 0); | ||
313 | c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_POS, 0); | ||
314 | c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_POS, 0); | ||
315 | c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_POS, 0); | ||
316 | c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_POS, 0); | ||
317 | c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_POS, 0); | ||
318 | |||
319 | /* Set default blend mode */ | ||
320 | c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L0, 0); | ||
321 | c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L1, 0); | ||
322 | c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L2, 0); | ||
323 | c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L3, 0); | ||
324 | c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L4, 0); | ||
325 | c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L5, 0); | ||
326 | c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L6, 0); | ||
327 | c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L7, 0); | ||
328 | |||
329 | /* default transparency mode */ | ||
330 | c_set_disp_reg(par, CARMINE_DISP_REG_L0_TRANS, 0); | ||
331 | c_set_disp_reg(par, CARMINE_DISP_REG_L1_TRANS, 0); | ||
332 | c_set_disp_reg(par, CARMINE_DISP_REG_L2_TRANS, 0); | ||
333 | c_set_disp_reg(par, CARMINE_DISP_REG_L3_TRANS, 0); | ||
334 | c_set_disp_reg(par, CARMINE_DISP_REG_L4_TRANS, 0); | ||
335 | c_set_disp_reg(par, CARMINE_DISP_REG_L5_TRANS, 0); | ||
336 | c_set_disp_reg(par, CARMINE_DISP_REG_L6_TRANS, 0); | ||
337 | c_set_disp_reg(par, CARMINE_DISP_REG_L7_TRANS, 0); | ||
338 | |||
339 | /* Set default read skip parameter */ | ||
340 | c_set_disp_reg(par, CARMINE_DISP_REG_L0RM, 0); | ||
341 | c_set_disp_reg(par, CARMINE_DISP_REG_L2RM, 0); | ||
342 | c_set_disp_reg(par, CARMINE_DISP_REG_L3RM, 0); | ||
343 | c_set_disp_reg(par, CARMINE_DISP_REG_L4RM, 0); | ||
344 | c_set_disp_reg(par, CARMINE_DISP_REG_L5RM, 0); | ||
345 | c_set_disp_reg(par, CARMINE_DISP_REG_L6RM, 0); | ||
346 | c_set_disp_reg(par, CARMINE_DISP_REG_L7RM, 0); | ||
347 | |||
348 | c_set_disp_reg(par, CARMINE_DISP_REG_L0PX, 0); | ||
349 | c_set_disp_reg(par, CARMINE_DISP_REG_L2PX, 0); | ||
350 | c_set_disp_reg(par, CARMINE_DISP_REG_L3PX, 0); | ||
351 | c_set_disp_reg(par, CARMINE_DISP_REG_L4PX, 0); | ||
352 | c_set_disp_reg(par, CARMINE_DISP_REG_L5PX, 0); | ||
353 | c_set_disp_reg(par, CARMINE_DISP_REG_L6PX, 0); | ||
354 | c_set_disp_reg(par, CARMINE_DISP_REG_L7PX, 0); | ||
355 | |||
356 | c_set_disp_reg(par, CARMINE_DISP_REG_L0PY, 0); | ||
357 | c_set_disp_reg(par, CARMINE_DISP_REG_L2PY, 0); | ||
358 | c_set_disp_reg(par, CARMINE_DISP_REG_L3PY, 0); | ||
359 | c_set_disp_reg(par, CARMINE_DISP_REG_L4PY, 0); | ||
360 | c_set_disp_reg(par, CARMINE_DISP_REG_L5PY, 0); | ||
361 | c_set_disp_reg(par, CARMINE_DISP_REG_L6PY, 0); | ||
362 | c_set_disp_reg(par, CARMINE_DISP_REG_L7PY, 0); | ||
363 | } | ||
364 | |||
365 | static void set_display_parameters(struct carmine_fb *par) | ||
366 | { | ||
367 | u32 mode; | ||
368 | u32 hdp, vdp, htp, hsp, hsw, vtr, vsp, vsw; | ||
369 | |||
370 | /* | ||
371 | * display timing. Parameters are decreased by one because hardware | ||
372 | * spec is 0 to (n - 1) | ||
373 | * */ | ||
374 | hdp = par->res->hdp - 1; | ||
375 | vdp = par->res->vdp - 1; | ||
376 | htp = par->res->htp - 1; | ||
377 | hsp = par->res->hsp - 1; | ||
378 | hsw = par->res->hsw - 1; | ||
379 | vtr = par->res->vtr - 1; | ||
380 | vsp = par->res->vsp - 1; | ||
381 | vsw = par->res->vsw - 1; | ||
382 | |||
383 | c_set_disp_reg(par, CARMINE_DISP_REG_H_TOTAL, | ||
384 | htp << CARMINE_DISP_HTP_SHIFT); | ||
385 | c_set_disp_reg(par, CARMINE_DISP_REG_H_PERIOD, | ||
386 | (hdp << CARMINE_DISP_HDB_SHIFT) | hdp); | ||
387 | c_set_disp_reg(par, CARMINE_DISP_REG_V_H_W_H_POS, | ||
388 | (vsw << CARMINE_DISP_VSW_SHIFT) | | ||
389 | (hsw << CARMINE_DISP_HSW_SHIFT) | | ||
390 | (hsp)); | ||
391 | c_set_disp_reg(par, CARMINE_DISP_REG_V_TOTAL, | ||
392 | vtr << CARMINE_DISP_VTR_SHIFT); | ||
393 | c_set_disp_reg(par, CARMINE_DISP_REG_V_PERIOD_POS, | ||
394 | (vdp << CARMINE_DISP_VDP_SHIFT) | vsp); | ||
395 | |||
396 | /* clock */ | ||
397 | mode = c_get_disp_reg(par, CARMINE_DISP_REG_DCM1); | ||
398 | mode = (mode & ~CARMINE_DISP_DCM_MASK) | | ||
399 | (par->res->disp_mode & CARMINE_DISP_DCM_MASK); | ||
400 | /* enable video output and layer 0 */ | ||
401 | mode |= CARMINE_DEN | CARMINE_L0E; | ||
402 | c_set_disp_reg(par, CARMINE_DISP_REG_DCM1, mode); | ||
403 | } | ||
404 | |||
405 | static int carmine_set_par(struct fb_info *info) | ||
406 | { | ||
407 | struct carmine_fb *par = info->par; | ||
408 | int ret; | ||
409 | |||
410 | ret = carmine_find_mode(&info->var); | ||
411 | if (ret < 0) | ||
412 | return ret; | ||
413 | |||
414 | par->new_mode = ret; | ||
415 | if (par->cur_mode != par->new_mode) { | ||
416 | |||
417 | par->cur_mode = par->new_mode; | ||
418 | par->res = &car_modes[par->new_mode]; | ||
419 | |||
420 | carmine_init_display_param(par); | ||
421 | set_display_parameters(par); | ||
422 | } | ||
423 | |||
424 | info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8; | ||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | static int init_hardware(struct carmine_hw *hw) | ||
429 | { | ||
430 | u32 flags; | ||
431 | u32 loops; | ||
432 | u32 ret; | ||
433 | |||
434 | /* Initalize Carmine */ | ||
435 | /* Sets internal clock */ | ||
436 | c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, | ||
437 | CARMINE_DFLT_IP_CLOCK_ENABLE); | ||
438 | |||
439 | /* Video signal output is turned off */ | ||
440 | c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0); | ||
441 | c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0); | ||
442 | |||
443 | /* Software reset */ | ||
444 | c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 1); | ||
445 | c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 0); | ||
446 | |||
447 | /* I/O mode settings */ | ||
448 | flags = CARMINE_DFLT_IP_DCTL_IO_CONT1 << 16 | | ||
449 | CARMINE_DFLT_IP_DCTL_IO_CONT0; | ||
450 | c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_IOCONT1_IOCONT0, | ||
451 | flags); | ||
452 | |||
453 | /* DRAM initial sequence */ | ||
454 | flags = CARMINE_DFLT_IP_DCTL_MODE << 16 | CARMINE_DFLT_IP_DCTL_ADD; | ||
455 | c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, | ||
456 | flags); | ||
457 | |||
458 | flags = CARMINE_DFLT_IP_DCTL_SET_TIME1 << 16 | | ||
459 | CARMINE_DFLT_IP_DCTL_EMODE; | ||
460 | c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_SETTIME1_EMODE, | ||
461 | flags); | ||
462 | |||
463 | flags = CARMINE_DFLT_IP_DCTL_REFRESH << 16 | | ||
464 | CARMINE_DFLT_IP_DCTL_SET_TIME2; | ||
465 | c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_REFRESH_SETTIME2, | ||
466 | flags); | ||
467 | |||
468 | flags = CARMINE_DFLT_IP_DCTL_RESERVE2 << 16 | | ||
469 | CARMINE_DFLT_IP_DCTL_FIFO_DEPTH; | ||
470 | c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV2_RSV1, flags); | ||
471 | |||
472 | flags = CARMINE_DFLT_IP_DCTL_DDRIF2 << 16 | CARMINE_DFLT_IP_DCTL_DDRIF1; | ||
473 | c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_DDRIF2_DDRIF1, | ||
474 | flags); | ||
475 | |||
476 | flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 | | ||
477 | CARMINE_DFLT_IP_DCTL_STATES; | ||
478 | c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES, | ||
479 | flags); | ||
480 | |||
481 | /* Executes DLL reset */ | ||
482 | if (CARMINE_DCTL_DLL_RESET) { | ||
483 | for (loops = 0; loops < CARMINE_DCTL_INIT_WAIT_LIMIT; loops++) { | ||
484 | |||
485 | ret = c_get_hw_reg(hw, CARMINE_DCTL_REG + | ||
486 | CARMINE_DCTL_REG_RSV0_STATES); | ||
487 | ret &= CARMINE_DCTL_REG_STATES_MASK; | ||
488 | if (!ret) | ||
489 | break; | ||
490 | |||
491 | mdelay(CARMINE_DCTL_INIT_WAIT_INTERVAL); | ||
492 | } | ||
493 | |||
494 | if (loops >= CARMINE_DCTL_INIT_WAIT_LIMIT) { | ||
495 | printk(KERN_ERR "DRAM init failed\n"); | ||
496 | return -EIO; | ||
497 | } | ||
498 | } | ||
499 | |||
500 | flags = CARMINE_DFLT_IP_DCTL_MODE_AFT_RST << 16 | | ||
501 | CARMINE_DFLT_IP_DCTL_ADD; | ||
502 | c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, flags); | ||
503 | |||
504 | flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 | | ||
505 | CARMINE_DFLT_IP_DCTL_STATES_AFT_RST; | ||
506 | c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES, | ||
507 | flags); | ||
508 | |||
509 | /* Initialize the write back register */ | ||
510 | c_set_hw_reg(hw, CARMINE_WB_REG + CARMINE_WB_REG_WBM, | ||
511 | CARMINE_WB_REG_WBM_DEFAULT); | ||
512 | |||
513 | /* Initialize the Kottos registers */ | ||
514 | c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRINTM, 0); | ||
515 | c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRERRM, 0); | ||
516 | |||
517 | /* Set DC offsets */ | ||
518 | c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PX, 0); | ||
519 | c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PY, 0); | ||
520 | c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LX, 0); | ||
521 | c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LY, 0); | ||
522 | c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TX, 0); | ||
523 | c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TY, 0); | ||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | static struct fb_ops carminefb_ops = { | ||
528 | .owner = THIS_MODULE, | ||
529 | .fb_fillrect = cfb_fillrect, | ||
530 | .fb_copyarea = cfb_copyarea, | ||
531 | .fb_imageblit = cfb_imageblit, | ||
532 | |||
533 | .fb_check_var = carmine_check_var, | ||
534 | .fb_set_par = carmine_set_par, | ||
535 | .fb_setcolreg = carmine_setcolreg, | ||
536 | }; | ||
537 | |||
538 | static int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base, | ||
539 | int smem_offset, struct device *device, struct fb_info **rinfo) | ||
540 | { | ||
541 | int ret; | ||
542 | struct fb_info *info; | ||
543 | struct carmine_fb *par; | ||
544 | |||
545 | info = framebuffer_alloc(sizeof *par, device); | ||
546 | if (!info) | ||
547 | return -ENOMEM; | ||
548 | |||
549 | par = info->par; | ||
550 | par->display_reg = regs; | ||
551 | par->smem_offset = smem_offset; | ||
552 | |||
553 | info->screen_base = smem_base + smem_offset; | ||
554 | info->screen_size = CARMINE_DISPLAY_MEM; | ||
555 | info->fbops = &carminefb_ops; | ||
556 | |||
557 | info->fix = carminefb_fix; | ||
558 | info->pseudo_palette = par->pseudo_palette; | ||
559 | info->flags = FBINFO_DEFAULT; | ||
560 | |||
561 | ret = fb_alloc_cmap(&info->cmap, 256, 1); | ||
562 | if (ret < 0) | ||
563 | goto err_free_fb; | ||
564 | |||
565 | if (fb_mode > ARRAY_SIZE(carmine_modedb)) | ||
566 | fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE; | ||
567 | |||
568 | par->cur_mode = par->new_mode = ~0; | ||
569 | |||
570 | ret = fb_find_mode(&info->var, info, fb_mode_str, carmine_modedb, | ||
571 | ARRAY_SIZE(carmine_modedb), | ||
572 | &carmine_modedb[fb_mode], 32); | ||
573 | if (!ret || ret == 4) { | ||
574 | ret = -EINVAL; | ||
575 | goto err_dealloc_cmap; | ||
576 | } | ||
577 | |||
578 | fb_videomode_to_modelist(carmine_modedb, ARRAY_SIZE(carmine_modedb), | ||
579 | &info->modelist); | ||
580 | |||
581 | ret = register_framebuffer(info); | ||
582 | if (ret < 0) | ||
583 | goto err_dealloc_cmap; | ||
584 | |||
585 | printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, | ||
586 | info->fix.id); | ||
587 | |||
588 | *rinfo = info; | ||
589 | return 0; | ||
590 | |||
591 | err_dealloc_cmap: | ||
592 | fb_dealloc_cmap(&info->cmap); | ||
593 | err_free_fb: | ||
594 | framebuffer_release(info); | ||
595 | return ret; | ||
596 | } | ||
597 | |||
598 | static void cleanup_fb_device(struct fb_info *info) | ||
599 | { | ||
600 | if (info) { | ||
601 | unregister_framebuffer(info); | ||
602 | fb_dealloc_cmap(&info->cmap); | ||
603 | framebuffer_release(info); | ||
604 | } | ||
605 | } | ||
606 | |||
607 | static int __devinit carminefb_probe(struct pci_dev *dev, | ||
608 | const struct pci_device_id *ent) | ||
609 | { | ||
610 | struct carmine_hw *hw; | ||
611 | struct device *device = &dev->dev; | ||
612 | struct fb_info *info; | ||
613 | int ret; | ||
614 | |||
615 | ret = pci_enable_device(dev); | ||
616 | if (ret) | ||
617 | return ret; | ||
618 | |||
619 | ret = -ENOMEM; | ||
620 | hw = kzalloc(sizeof *hw, GFP_KERNEL); | ||
621 | if (!hw) | ||
622 | goto err_enable_pci; | ||
623 | |||
624 | carminefb_fix.mmio_start = pci_resource_start(dev, CARMINE_CONFIG_BAR); | ||
625 | carminefb_fix.mmio_len = pci_resource_len(dev, CARMINE_CONFIG_BAR); | ||
626 | |||
627 | if (!request_mem_region(carminefb_fix.mmio_start, | ||
628 | carminefb_fix.mmio_len, | ||
629 | "carminefb regbase")) { | ||
630 | printk(KERN_ERR "carminefb: Can't reserve regbase.\n"); | ||
631 | ret = -EBUSY; | ||
632 | goto err_free_hw; | ||
633 | } | ||
634 | hw->v_regs = ioremap_nocache(carminefb_fix.mmio_start, | ||
635 | carminefb_fix.mmio_len); | ||
636 | if (!hw->v_regs) { | ||
637 | printk(KERN_ERR "carminefb: Can't remap %s register.\n", | ||
638 | carminefb_fix.id); | ||
639 | goto err_free_reg_mmio; | ||
640 | } | ||
641 | |||
642 | carminefb_fix.smem_start = pci_resource_start(dev, CARMINE_MEMORY_BAR); | ||
643 | carminefb_fix.smem_len = pci_resource_len(dev, CARMINE_MEMORY_BAR); | ||
644 | |||
645 | /* The memory area tends to be very large (256 MiB). Remap only what | ||
646 | * is required for that largest resolution to avoid remaps at run | ||
647 | * time | ||
648 | */ | ||
649 | if (carminefb_fix.smem_len > CARMINE_TOTAL_DIPLAY_MEM) | ||
650 | carminefb_fix.smem_len = CARMINE_TOTAL_DIPLAY_MEM; | ||
651 | |||
652 | else if (carminefb_fix.smem_len < CARMINE_TOTAL_DIPLAY_MEM) { | ||
653 | printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d " | ||
654 | "are required.", carminefb_fix.smem_len, | ||
655 | CARMINE_TOTAL_DIPLAY_MEM); | ||
656 | goto err_free_reg_mmio; | ||
657 | } | ||
658 | |||
659 | if (!request_mem_region(carminefb_fix.smem_start, | ||
660 | carminefb_fix.smem_len, "carminefb smem")) { | ||
661 | printk(KERN_ERR "carminefb: Can't reserve smem.\n"); | ||
662 | goto err_unmap_vregs; | ||
663 | } | ||
664 | |||
665 | hw->screen_mem = ioremap_nocache(carminefb_fix.smem_start, | ||
666 | carminefb_fix.smem_len); | ||
667 | if (!hw->screen_mem) { | ||
668 | printk(KERN_ERR "carmine: Can't ioremap smem area.\n"); | ||
669 | release_mem_region(carminefb_fix.smem_start, | ||
670 | carminefb_fix.smem_len); | ||
671 | goto err_reg_smem; | ||
672 | } | ||
673 | |||
674 | ret = init_hardware(hw); | ||
675 | if (ret) | ||
676 | goto err_unmap_screen; | ||
677 | |||
678 | info = NULL; | ||
679 | if (fb_displays & CARMINE_USE_DISPLAY0) { | ||
680 | ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP0_REG, | ||
681 | hw->screen_mem, CARMINE_DISPLAY_MEM * 0, | ||
682 | device, &info); | ||
683 | if (ret) | ||
684 | goto err_deinit_hw; | ||
685 | } | ||
686 | |||
687 | hw->fb[0] = info; | ||
688 | |||
689 | info = NULL; | ||
690 | if (fb_displays & CARMINE_USE_DISPLAY1) { | ||
691 | ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP1_REG, | ||
692 | hw->screen_mem, CARMINE_DISPLAY_MEM * 1, | ||
693 | device, &info); | ||
694 | if (ret) | ||
695 | goto err_cleanup_fb0; | ||
696 | } | ||
697 | |||
698 | hw->fb[1] = info; | ||
699 | info = NULL; | ||
700 | |||
701 | pci_set_drvdata(dev, hw); | ||
702 | return 0; | ||
703 | |||
704 | err_cleanup_fb0: | ||
705 | cleanup_fb_device(hw->fb[0]); | ||
706 | err_deinit_hw: | ||
707 | /* disable clock, etc */ | ||
708 | c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0); | ||
709 | err_unmap_screen: | ||
710 | iounmap(hw->screen_mem); | ||
711 | err_reg_smem: | ||
712 | release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len); | ||
713 | err_unmap_vregs: | ||
714 | iounmap(hw->v_regs); | ||
715 | err_free_reg_mmio: | ||
716 | release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len); | ||
717 | err_free_hw: | ||
718 | kfree(hw); | ||
719 | err_enable_pci: | ||
720 | pci_disable_device(dev); | ||
721 | return ret; | ||
722 | } | ||
723 | |||
724 | static void __devexit carminefb_remove(struct pci_dev *dev) | ||
725 | { | ||
726 | struct carmine_hw *hw = pci_get_drvdata(dev); | ||
727 | struct fb_fix_screeninfo fix; | ||
728 | int i; | ||
729 | |||
730 | /* in case we use only fb1 and not fb1 */ | ||
731 | if (hw->fb[0]) | ||
732 | fix = hw->fb[0]->fix; | ||
733 | else | ||
734 | fix = hw->fb[1]->fix; | ||
735 | |||
736 | /* deactivate display(s) and switch clocks */ | ||
737 | c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0); | ||
738 | c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0); | ||
739 | c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0); | ||
740 | |||
741 | for (i = 0; i < MAX_DISPLAY; i++) | ||
742 | cleanup_fb_device(hw->fb[i]); | ||
743 | |||
744 | iounmap(hw->screen_mem); | ||
745 | release_mem_region(fix.smem_start, fix.smem_len); | ||
746 | iounmap(hw->v_regs); | ||
747 | release_mem_region(fix.mmio_start, fix.mmio_len); | ||
748 | |||
749 | pci_set_drvdata(dev, NULL); | ||
750 | pci_disable_device(dev); | ||
751 | kfree(hw); | ||
752 | } | ||
753 | |||
754 | #define PCI_VENDOR_ID_FUJITU_LIMITED 0x10cf | ||
755 | static struct pci_device_id carmine_devices[] __devinitdata = { | ||
756 | { | ||
757 | PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b)}, | ||
758 | {0, 0, 0, 0, 0, 0, 0} | ||
759 | }; | ||
760 | |||
761 | MODULE_DEVICE_TABLE(pci, carmine_devices); | ||
762 | |||
763 | static struct pci_driver carmine_pci_driver = { | ||
764 | .name = "carminefb", | ||
765 | .id_table = carmine_devices, | ||
766 | .probe = carminefb_probe, | ||
767 | .remove = __devexit_p(carminefb_remove), | ||
768 | }; | ||
769 | |||
770 | static int __init carminefb_init(void) | ||
771 | { | ||
772 | if (!(fb_displays & | ||
773 | (CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1))) { | ||
774 | printk(KERN_ERR "If you disable both displays than you don't " | ||
775 | "need the driver at all\n"); | ||
776 | return -EINVAL; | ||
777 | } | ||
778 | return pci_register_driver(&carmine_pci_driver); | ||
779 | } | ||
780 | module_init(carminefb_init); | ||
781 | |||
782 | static void __exit carminefb_cleanup(void) | ||
783 | { | ||
784 | pci_unregister_driver(&carmine_pci_driver); | ||
785 | } | ||
786 | module_exit(carminefb_cleanup); | ||
787 | |||
788 | MODULE_AUTHOR("Sebastian Siewior <bigeasy@linutronix.de>"); | ||
789 | MODULE_DESCRIPTION("Framebuffer driver for Fujitsu Carmine based devices"); | ||
790 | MODULE_LICENSE("GPL v2"); | ||