diff options
Diffstat (limited to 'drivers/video/fsl-diu-fb.c')
-rw-r--r-- | drivers/video/fsl-diu-fb.c | 1721 |
1 files changed, 1721 insertions, 0 deletions
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c new file mode 100644 index 000000000000..b50bb03cb5ab --- /dev/null +++ b/drivers/video/fsl-diu-fb.c | |||
@@ -0,0 +1,1721 @@ | |||
1 | /* | ||
2 | * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * Freescale DIU Frame Buffer device driver | ||
5 | * | ||
6 | * Authors: Hongjun Chen <hong-jun.chen@freescale.com> | ||
7 | * Paul Widmer <paul.widmer@freescale.com> | ||
8 | * Srikanth Srinivasan <srikanth.srinivasan@freescale.com> | ||
9 | * York Sun <yorksun@freescale.com> | ||
10 | * | ||
11 | * Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify it | ||
14 | * under the terms of the GNU General Public License as published by the | ||
15 | * Free Software Foundation; either version 2 of the License, or (at your | ||
16 | * option) any later version. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/errno.h> | ||
23 | #include <linux/string.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/fb.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/dma-mapping.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/clk.h> | ||
31 | #include <linux/uaccess.h> | ||
32 | #include <linux/vmalloc.h> | ||
33 | |||
34 | #include <linux/of_platform.h> | ||
35 | |||
36 | #include <sysdev/fsl_soc.h> | ||
37 | #include "fsl-diu-fb.h" | ||
38 | |||
39 | /* | ||
40 | * These parameters give default parameters | ||
41 | * for video output 1024x768, | ||
42 | * FIXME - change timing to proper amounts | ||
43 | * hsync 31.5kHz, vsync 60Hz | ||
44 | */ | ||
45 | static struct fb_videomode __devinitdata fsl_diu_default_mode = { | ||
46 | .refresh = 60, | ||
47 | .xres = 1024, | ||
48 | .yres = 768, | ||
49 | .pixclock = 15385, | ||
50 | .left_margin = 160, | ||
51 | .right_margin = 24, | ||
52 | .upper_margin = 29, | ||
53 | .lower_margin = 3, | ||
54 | .hsync_len = 136, | ||
55 | .vsync_len = 6, | ||
56 | .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
57 | .vmode = FB_VMODE_NONINTERLACED | ||
58 | }; | ||
59 | |||
60 | static struct fb_videomode __devinitdata fsl_diu_mode_db[] = { | ||
61 | { | ||
62 | .name = "1024x768-60", | ||
63 | .refresh = 60, | ||
64 | .xres = 1024, | ||
65 | .yres = 768, | ||
66 | .pixclock = 15385, | ||
67 | .left_margin = 160, | ||
68 | .right_margin = 24, | ||
69 | .upper_margin = 29, | ||
70 | .lower_margin = 3, | ||
71 | .hsync_len = 136, | ||
72 | .vsync_len = 6, | ||
73 | .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
74 | .vmode = FB_VMODE_NONINTERLACED | ||
75 | }, | ||
76 | { | ||
77 | .name = "1024x768-70", | ||
78 | .refresh = 70, | ||
79 | .xres = 1024, | ||
80 | .yres = 768, | ||
81 | .pixclock = 16886, | ||
82 | .left_margin = 3, | ||
83 | .right_margin = 3, | ||
84 | .upper_margin = 2, | ||
85 | .lower_margin = 2, | ||
86 | .hsync_len = 40, | ||
87 | .vsync_len = 18, | ||
88 | .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
89 | .vmode = FB_VMODE_NONINTERLACED | ||
90 | }, | ||
91 | { | ||
92 | .name = "1024x768-75", | ||
93 | .refresh = 75, | ||
94 | .xres = 1024, | ||
95 | .yres = 768, | ||
96 | .pixclock = 15009, | ||
97 | .left_margin = 3, | ||
98 | .right_margin = 3, | ||
99 | .upper_margin = 2, | ||
100 | .lower_margin = 2, | ||
101 | .hsync_len = 80, | ||
102 | .vsync_len = 32, | ||
103 | .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
104 | .vmode = FB_VMODE_NONINTERLACED | ||
105 | }, | ||
106 | { | ||
107 | .name = "1280x1024-60", | ||
108 | .refresh = 60, | ||
109 | .xres = 1280, | ||
110 | .yres = 1024, | ||
111 | .pixclock = 9375, | ||
112 | .left_margin = 38, | ||
113 | .right_margin = 128, | ||
114 | .upper_margin = 2, | ||
115 | .lower_margin = 7, | ||
116 | .hsync_len = 216, | ||
117 | .vsync_len = 37, | ||
118 | .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
119 | .vmode = FB_VMODE_NONINTERLACED | ||
120 | }, | ||
121 | { | ||
122 | .name = "1280x1024-70", | ||
123 | .refresh = 70, | ||
124 | .xres = 1280, | ||
125 | .yres = 1024, | ||
126 | .pixclock = 9380, | ||
127 | .left_margin = 6, | ||
128 | .right_margin = 6, | ||
129 | .upper_margin = 4, | ||
130 | .lower_margin = 4, | ||
131 | .hsync_len = 60, | ||
132 | .vsync_len = 94, | ||
133 | .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
134 | .vmode = FB_VMODE_NONINTERLACED | ||
135 | }, | ||
136 | { | ||
137 | .name = "1280x1024-75", | ||
138 | .refresh = 75, | ||
139 | .xres = 1280, | ||
140 | .yres = 1024, | ||
141 | .pixclock = 9380, | ||
142 | .left_margin = 6, | ||
143 | .right_margin = 6, | ||
144 | .upper_margin = 4, | ||
145 | .lower_margin = 4, | ||
146 | .hsync_len = 60, | ||
147 | .vsync_len = 15, | ||
148 | .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
149 | .vmode = FB_VMODE_NONINTERLACED | ||
150 | }, | ||
151 | { | ||
152 | .name = "320x240", /* for AOI only */ | ||
153 | .refresh = 60, | ||
154 | .xres = 320, | ||
155 | .yres = 240, | ||
156 | .pixclock = 15385, | ||
157 | .left_margin = 0, | ||
158 | .right_margin = 0, | ||
159 | .upper_margin = 0, | ||
160 | .lower_margin = 0, | ||
161 | .hsync_len = 0, | ||
162 | .vsync_len = 0, | ||
163 | .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
164 | .vmode = FB_VMODE_NONINTERLACED | ||
165 | }, | ||
166 | { | ||
167 | .name = "1280x480-60", | ||
168 | .refresh = 60, | ||
169 | .xres = 1280, | ||
170 | .yres = 480, | ||
171 | .pixclock = 18939, | ||
172 | .left_margin = 353, | ||
173 | .right_margin = 47, | ||
174 | .upper_margin = 39, | ||
175 | .lower_margin = 4, | ||
176 | .hsync_len = 8, | ||
177 | .vsync_len = 2, | ||
178 | .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
179 | .vmode = FB_VMODE_NONINTERLACED | ||
180 | }, | ||
181 | }; | ||
182 | |||
183 | static char *fb_mode = "1024x768-32@60"; | ||
184 | static unsigned long default_bpp = 32; | ||
185 | static int monitor_port; | ||
186 | |||
187 | #if defined(CONFIG_NOT_COHERENT_CACHE) | ||
188 | static u8 *coherence_data; | ||
189 | static size_t coherence_data_size; | ||
190 | static unsigned int d_cache_line_size; | ||
191 | #endif | ||
192 | |||
193 | static DEFINE_SPINLOCK(diu_lock); | ||
194 | |||
195 | struct fsl_diu_data { | ||
196 | struct fb_info *fsl_diu_info[FSL_AOI_NUM - 1]; | ||
197 | /*FSL_AOI_NUM has one dummy AOI */ | ||
198 | struct device_attribute dev_attr; | ||
199 | struct diu_ad *dummy_ad; | ||
200 | void *dummy_aoi_virt; | ||
201 | unsigned int irq; | ||
202 | int fb_enabled; | ||
203 | int monitor_port; | ||
204 | }; | ||
205 | |||
206 | struct mfb_info { | ||
207 | int index; | ||
208 | int type; | ||
209 | char *id; | ||
210 | int registered; | ||
211 | int blank; | ||
212 | unsigned long pseudo_palette[16]; | ||
213 | struct diu_ad *ad; | ||
214 | int cursor_reset; | ||
215 | unsigned char g_alpha; | ||
216 | unsigned int count; | ||
217 | int x_aoi_d; /* aoi display x offset to physical screen */ | ||
218 | int y_aoi_d; /* aoi display y offset to physical screen */ | ||
219 | struct fsl_diu_data *parent; | ||
220 | }; | ||
221 | |||
222 | |||
223 | static struct mfb_info mfb_template[] = { | ||
224 | { /* AOI 0 for plane 0 */ | ||
225 | .index = 0, | ||
226 | .type = MFB_TYPE_OUTPUT, | ||
227 | .id = "Panel0", | ||
228 | .registered = 0, | ||
229 | .count = 0, | ||
230 | .x_aoi_d = 0, | ||
231 | .y_aoi_d = 0, | ||
232 | }, | ||
233 | { /* AOI 0 for plane 1 */ | ||
234 | .index = 1, | ||
235 | .type = MFB_TYPE_OUTPUT, | ||
236 | .id = "Panel1 AOI0", | ||
237 | .registered = 0, | ||
238 | .g_alpha = 0xff, | ||
239 | .count = 0, | ||
240 | .x_aoi_d = 0, | ||
241 | .y_aoi_d = 0, | ||
242 | }, | ||
243 | { /* AOI 1 for plane 1 */ | ||
244 | .index = 2, | ||
245 | .type = MFB_TYPE_OUTPUT, | ||
246 | .id = "Panel1 AOI1", | ||
247 | .registered = 0, | ||
248 | .g_alpha = 0xff, | ||
249 | .count = 0, | ||
250 | .x_aoi_d = 0, | ||
251 | .y_aoi_d = 480, | ||
252 | }, | ||
253 | { /* AOI 0 for plane 2 */ | ||
254 | .index = 3, | ||
255 | .type = MFB_TYPE_OUTPUT, | ||
256 | .id = "Panel2 AOI0", | ||
257 | .registered = 0, | ||
258 | .g_alpha = 0xff, | ||
259 | .count = 0, | ||
260 | .x_aoi_d = 640, | ||
261 | .y_aoi_d = 0, | ||
262 | }, | ||
263 | { /* AOI 1 for plane 2 */ | ||
264 | .index = 4, | ||
265 | .type = MFB_TYPE_OUTPUT, | ||
266 | .id = "Panel2 AOI1", | ||
267 | .registered = 0, | ||
268 | .g_alpha = 0xff, | ||
269 | .count = 0, | ||
270 | .x_aoi_d = 640, | ||
271 | .y_aoi_d = 480, | ||
272 | }, | ||
273 | }; | ||
274 | |||
275 | static struct diu_hw dr = { | ||
276 | .mode = MFB_MODE1, | ||
277 | .reg_lock = __SPIN_LOCK_UNLOCKED(diu_hw.reg_lock), | ||
278 | }; | ||
279 | |||
280 | static struct diu_pool pool; | ||
281 | |||
282 | /* To allocate memory for framebuffer. First try __get_free_pages(). If it | ||
283 | * fails, try rh_alloc. The reason is __get_free_pages() cannot allocate | ||
284 | * very large memory (more than 4MB). We don't want to allocate all memory | ||
285 | * in rheap since small memory allocation/deallocation will fragment the | ||
286 | * rheap and make the furture large allocation fail. | ||
287 | */ | ||
288 | |||
289 | void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys) | ||
290 | { | ||
291 | void *virt; | ||
292 | |||
293 | pr_debug("size=%lu\n", size); | ||
294 | |||
295 | virt = (void *)__get_free_pages(GFP_DMA | __GFP_ZERO, get_order(size)); | ||
296 | if (virt) { | ||
297 | *phys = virt_to_phys(virt); | ||
298 | pr_debug("virt %p, phys=%llx\n", virt, (uint64_t) *phys); | ||
299 | return virt; | ||
300 | } | ||
301 | if (!diu_ops.diu_mem) { | ||
302 | printk(KERN_INFO "%s: no diu_mem." | ||
303 | " To reserve more memory, put 'diufb=15M' " | ||
304 | "in the command line\n", __func__); | ||
305 | return NULL; | ||
306 | } | ||
307 | |||
308 | virt = (void *)rh_alloc(&diu_ops.diu_rh_info, size, "DIU"); | ||
309 | if (virt) { | ||
310 | *phys = virt_to_bus(virt); | ||
311 | memset(virt, 0, size); | ||
312 | } | ||
313 | |||
314 | pr_debug("rh virt=%p phys=%lx\n", virt, *phys); | ||
315 | |||
316 | return virt; | ||
317 | } | ||
318 | |||
319 | void fsl_diu_free(void *p, unsigned long size) | ||
320 | { | ||
321 | pr_debug("p=%p size=%lu\n", p, size); | ||
322 | |||
323 | if (!p) | ||
324 | return; | ||
325 | |||
326 | if ((p >= diu_ops.diu_mem) && | ||
327 | (p < (diu_ops.diu_mem + diu_ops.diu_size))) { | ||
328 | pr_debug("rh\n"); | ||
329 | rh_free(&diu_ops.diu_rh_info, (unsigned long) p); | ||
330 | } else { | ||
331 | pr_debug("dma\n"); | ||
332 | free_pages((unsigned long)p, get_order(size)); | ||
333 | } | ||
334 | } | ||
335 | |||
336 | static int fsl_diu_enable_panel(struct fb_info *info) | ||
337 | { | ||
338 | struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; | ||
339 | struct diu *hw = dr.diu_reg; | ||
340 | struct diu_ad *ad = mfbi->ad; | ||
341 | struct fsl_diu_data *machine_data = mfbi->parent; | ||
342 | int res = 0; | ||
343 | |||
344 | pr_debug("enable_panel index %d\n", mfbi->index); | ||
345 | if (mfbi->type != MFB_TYPE_OFF) { | ||
346 | switch (mfbi->index) { | ||
347 | case 0: /* plane 0 */ | ||
348 | if (hw->desc[0] != ad->paddr) | ||
349 | out_be32(&hw->desc[0], ad->paddr); | ||
350 | break; | ||
351 | case 1: /* plane 1 AOI 0 */ | ||
352 | cmfbi = machine_data->fsl_diu_info[2]->par; | ||
353 | if (hw->desc[1] != ad->paddr) { /* AOI0 closed */ | ||
354 | if (cmfbi->count > 0) /* AOI1 open */ | ||
355 | ad->next_ad = | ||
356 | cpu_to_le32(cmfbi->ad->paddr); | ||
357 | else | ||
358 | ad->next_ad = 0; | ||
359 | out_be32(&hw->desc[1], ad->paddr); | ||
360 | } | ||
361 | break; | ||
362 | case 3: /* plane 2 AOI 0 */ | ||
363 | cmfbi = machine_data->fsl_diu_info[4]->par; | ||
364 | if (hw->desc[2] != ad->paddr) { /* AOI0 closed */ | ||
365 | if (cmfbi->count > 0) /* AOI1 open */ | ||
366 | ad->next_ad = | ||
367 | cpu_to_le32(cmfbi->ad->paddr); | ||
368 | else | ||
369 | ad->next_ad = 0; | ||
370 | out_be32(&hw->desc[2], ad->paddr); | ||
371 | } | ||
372 | break; | ||
373 | case 2: /* plane 1 AOI 1 */ | ||
374 | pmfbi = machine_data->fsl_diu_info[1]->par; | ||
375 | ad->next_ad = 0; | ||
376 | if (hw->desc[1] == machine_data->dummy_ad->paddr) | ||
377 | out_be32(&hw->desc[1], ad->paddr); | ||
378 | else /* AOI0 open */ | ||
379 | pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); | ||
380 | break; | ||
381 | case 4: /* plane 2 AOI 1 */ | ||
382 | pmfbi = machine_data->fsl_diu_info[3]->par; | ||
383 | ad->next_ad = 0; | ||
384 | if (hw->desc[2] == machine_data->dummy_ad->paddr) | ||
385 | out_be32(&hw->desc[2], ad->paddr); | ||
386 | else /* AOI0 was open */ | ||
387 | pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); | ||
388 | break; | ||
389 | default: | ||
390 | res = -EINVAL; | ||
391 | break; | ||
392 | } | ||
393 | } else | ||
394 | res = -EINVAL; | ||
395 | return res; | ||
396 | } | ||
397 | |||
398 | static int fsl_diu_disable_panel(struct fb_info *info) | ||
399 | { | ||
400 | struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; | ||
401 | struct diu *hw = dr.diu_reg; | ||
402 | struct diu_ad *ad = mfbi->ad; | ||
403 | struct fsl_diu_data *machine_data = mfbi->parent; | ||
404 | int res = 0; | ||
405 | |||
406 | switch (mfbi->index) { | ||
407 | case 0: /* plane 0 */ | ||
408 | if (hw->desc[0] != machine_data->dummy_ad->paddr) | ||
409 | out_be32(&hw->desc[0], | ||
410 | machine_data->dummy_ad->paddr); | ||
411 | break; | ||
412 | case 1: /* plane 1 AOI 0 */ | ||
413 | cmfbi = machine_data->fsl_diu_info[2]->par; | ||
414 | if (cmfbi->count > 0) /* AOI1 is open */ | ||
415 | out_be32(&hw->desc[1], cmfbi->ad->paddr); | ||
416 | /* move AOI1 to the first */ | ||
417 | else /* AOI1 was closed */ | ||
418 | out_be32(&hw->desc[1], | ||
419 | machine_data->dummy_ad->paddr); | ||
420 | /* close AOI 0 */ | ||
421 | break; | ||
422 | case 3: /* plane 2 AOI 0 */ | ||
423 | cmfbi = machine_data->fsl_diu_info[4]->par; | ||
424 | if (cmfbi->count > 0) /* AOI1 is open */ | ||
425 | out_be32(&hw->desc[2], cmfbi->ad->paddr); | ||
426 | /* move AOI1 to the first */ | ||
427 | else /* AOI1 was closed */ | ||
428 | out_be32(&hw->desc[2], | ||
429 | machine_data->dummy_ad->paddr); | ||
430 | /* close AOI 0 */ | ||
431 | break; | ||
432 | case 2: /* plane 1 AOI 1 */ | ||
433 | pmfbi = machine_data->fsl_diu_info[1]->par; | ||
434 | if (hw->desc[1] != ad->paddr) { | ||
435 | /* AOI1 is not the first in the chain */ | ||
436 | if (pmfbi->count > 0) | ||
437 | /* AOI0 is open, must be the first */ | ||
438 | pmfbi->ad->next_ad = 0; | ||
439 | } else /* AOI1 is the first in the chain */ | ||
440 | out_be32(&hw->desc[1], machine_data->dummy_ad->paddr); | ||
441 | /* close AOI 1 */ | ||
442 | break; | ||
443 | case 4: /* plane 2 AOI 1 */ | ||
444 | pmfbi = machine_data->fsl_diu_info[3]->par; | ||
445 | if (hw->desc[2] != ad->paddr) { | ||
446 | /* AOI1 is not the first in the chain */ | ||
447 | if (pmfbi->count > 0) | ||
448 | /* AOI0 is open, must be the first */ | ||
449 | pmfbi->ad->next_ad = 0; | ||
450 | } else /* AOI1 is the first in the chain */ | ||
451 | out_be32(&hw->desc[2], machine_data->dummy_ad->paddr); | ||
452 | /* close AOI 1 */ | ||
453 | break; | ||
454 | default: | ||
455 | res = -EINVAL; | ||
456 | break; | ||
457 | } | ||
458 | |||
459 | return res; | ||
460 | } | ||
461 | |||
462 | static void enable_lcdc(struct fb_info *info) | ||
463 | { | ||
464 | struct diu *hw = dr.diu_reg; | ||
465 | struct mfb_info *mfbi = info->par; | ||
466 | struct fsl_diu_data *machine_data = mfbi->parent; | ||
467 | |||
468 | if (!machine_data->fb_enabled) { | ||
469 | out_be32(&hw->diu_mode, dr.mode); | ||
470 | machine_data->fb_enabled++; | ||
471 | } | ||
472 | } | ||
473 | |||
474 | static void disable_lcdc(struct fb_info *info) | ||
475 | { | ||
476 | struct diu *hw = dr.diu_reg; | ||
477 | struct mfb_info *mfbi = info->par; | ||
478 | struct fsl_diu_data *machine_data = mfbi->parent; | ||
479 | |||
480 | if (machine_data->fb_enabled) { | ||
481 | out_be32(&hw->diu_mode, 0); | ||
482 | machine_data->fb_enabled = 0; | ||
483 | } | ||
484 | } | ||
485 | |||
486 | static void adjust_aoi_size_position(struct fb_var_screeninfo *var, | ||
487 | struct fb_info *info) | ||
488 | { | ||
489 | struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par; | ||
490 | struct fsl_diu_data *machine_data = mfbi->parent; | ||
491 | int available_height, upper_aoi_bottom, index = mfbi->index; | ||
492 | int lower_aoi_is_open, upper_aoi_is_open; | ||
493 | __u32 base_plane_width, base_plane_height, upper_aoi_height; | ||
494 | |||
495 | base_plane_width = machine_data->fsl_diu_info[0]->var.xres; | ||
496 | base_plane_height = machine_data->fsl_diu_info[0]->var.yres; | ||
497 | |||
498 | switch (index) { | ||
499 | case 0: | ||
500 | if (mfbi->x_aoi_d != 0) | ||
501 | mfbi->x_aoi_d = 0; | ||
502 | if (mfbi->y_aoi_d != 0) | ||
503 | mfbi->y_aoi_d = 0; | ||
504 | break; | ||
505 | case 1: /* AOI 0 */ | ||
506 | case 3: | ||
507 | lower_aoi_mfbi = machine_data->fsl_diu_info[index+1]->par; | ||
508 | lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0; | ||
509 | if (var->xres > base_plane_width) | ||
510 | var->xres = base_plane_width; | ||
511 | if ((mfbi->x_aoi_d + var->xres) > base_plane_width) | ||
512 | mfbi->x_aoi_d = base_plane_width - var->xres; | ||
513 | |||
514 | if (lower_aoi_is_open) | ||
515 | available_height = lower_aoi_mfbi->y_aoi_d; | ||
516 | else | ||
517 | available_height = base_plane_height; | ||
518 | if (var->yres > available_height) | ||
519 | var->yres = available_height; | ||
520 | if ((mfbi->y_aoi_d + var->yres) > available_height) | ||
521 | mfbi->y_aoi_d = available_height - var->yres; | ||
522 | break; | ||
523 | case 2: /* AOI 1 */ | ||
524 | case 4: | ||
525 | upper_aoi_mfbi = machine_data->fsl_diu_info[index-1]->par; | ||
526 | upper_aoi_height = | ||
527 | machine_data->fsl_diu_info[index-1]->var.yres; | ||
528 | upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height; | ||
529 | upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0; | ||
530 | if (var->xres > base_plane_width) | ||
531 | var->xres = base_plane_width; | ||
532 | if ((mfbi->x_aoi_d + var->xres) > base_plane_width) | ||
533 | mfbi->x_aoi_d = base_plane_width - var->xres; | ||
534 | if (mfbi->y_aoi_d < 0) | ||
535 | mfbi->y_aoi_d = 0; | ||
536 | if (upper_aoi_is_open) { | ||
537 | if (mfbi->y_aoi_d < upper_aoi_bottom) | ||
538 | mfbi->y_aoi_d = upper_aoi_bottom; | ||
539 | available_height = base_plane_height | ||
540 | - upper_aoi_bottom; | ||
541 | } else | ||
542 | available_height = base_plane_height; | ||
543 | if (var->yres > available_height) | ||
544 | var->yres = available_height; | ||
545 | if ((mfbi->y_aoi_d + var->yres) > base_plane_height) | ||
546 | mfbi->y_aoi_d = base_plane_height - var->yres; | ||
547 | break; | ||
548 | } | ||
549 | } | ||
550 | /* | ||
551 | * Checks to see if the hardware supports the state requested by var passed | ||
552 | * in. This function does not alter the hardware state! If the var passed in | ||
553 | * is slightly off by what the hardware can support then we alter the var | ||
554 | * PASSED in to what we can do. If the hardware doesn't support mode change | ||
555 | * a -EINVAL will be returned by the upper layers. | ||
556 | */ | ||
557 | static int fsl_diu_check_var(struct fb_var_screeninfo *var, | ||
558 | struct fb_info *info) | ||
559 | { | ||
560 | unsigned long htotal, vtotal; | ||
561 | |||
562 | pr_debug("check_var xres: %d\n", var->xres); | ||
563 | pr_debug("check_var yres: %d\n", var->yres); | ||
564 | |||
565 | if (var->xres_virtual < var->xres) | ||
566 | var->xres_virtual = var->xres; | ||
567 | if (var->yres_virtual < var->yres) | ||
568 | var->yres_virtual = var->yres; | ||
569 | |||
570 | if (var->xoffset < 0) | ||
571 | var->xoffset = 0; | ||
572 | |||
573 | if (var->yoffset < 0) | ||
574 | var->yoffset = 0; | ||
575 | |||
576 | if (var->xoffset + info->var.xres > info->var.xres_virtual) | ||
577 | var->xoffset = info->var.xres_virtual - info->var.xres; | ||
578 | |||
579 | if (var->yoffset + info->var.yres > info->var.yres_virtual) | ||
580 | var->yoffset = info->var.yres_virtual - info->var.yres; | ||
581 | |||
582 | if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && | ||
583 | (var->bits_per_pixel != 16)) | ||
584 | var->bits_per_pixel = default_bpp; | ||
585 | |||
586 | switch (var->bits_per_pixel) { | ||
587 | case 16: | ||
588 | var->red.length = 5; | ||
589 | var->red.offset = 11; | ||
590 | var->red.msb_right = 0; | ||
591 | |||
592 | var->green.length = 6; | ||
593 | var->green.offset = 5; | ||
594 | var->green.msb_right = 0; | ||
595 | |||
596 | var->blue.length = 5; | ||
597 | var->blue.offset = 0; | ||
598 | var->blue.msb_right = 0; | ||
599 | |||
600 | var->transp.length = 0; | ||
601 | var->transp.offset = 0; | ||
602 | var->transp.msb_right = 0; | ||
603 | break; | ||
604 | case 24: | ||
605 | var->red.length = 8; | ||
606 | var->red.offset = 0; | ||
607 | var->red.msb_right = 0; | ||
608 | |||
609 | var->green.length = 8; | ||
610 | var->green.offset = 8; | ||
611 | var->green.msb_right = 0; | ||
612 | |||
613 | var->blue.length = 8; | ||
614 | var->blue.offset = 16; | ||
615 | var->blue.msb_right = 0; | ||
616 | |||
617 | var->transp.length = 0; | ||
618 | var->transp.offset = 0; | ||
619 | var->transp.msb_right = 0; | ||
620 | break; | ||
621 | case 32: | ||
622 | var->red.length = 8; | ||
623 | var->red.offset = 16; | ||
624 | var->red.msb_right = 0; | ||
625 | |||
626 | var->green.length = 8; | ||
627 | var->green.offset = 8; | ||
628 | var->green.msb_right = 0; | ||
629 | |||
630 | var->blue.length = 8; | ||
631 | var->blue.offset = 0; | ||
632 | var->blue.msb_right = 0; | ||
633 | |||
634 | var->transp.length = 8; | ||
635 | var->transp.offset = 24; | ||
636 | var->transp.msb_right = 0; | ||
637 | |||
638 | break; | ||
639 | } | ||
640 | /* If the pixclock is below the minimum spec'd value then set to | ||
641 | * refresh rate for 60Hz since this is supported by most monitors. | ||
642 | * Refer to Documentation/fb/ for calculations. | ||
643 | */ | ||
644 | if ((var->pixclock < MIN_PIX_CLK) || (var->pixclock > MAX_PIX_CLK)) { | ||
645 | htotal = var->xres + var->right_margin + var->hsync_len + | ||
646 | var->left_margin; | ||
647 | vtotal = var->yres + var->lower_margin + var->vsync_len + | ||
648 | var->upper_margin; | ||
649 | var->pixclock = (vtotal * htotal * 6UL) / 100UL; | ||
650 | var->pixclock = KHZ2PICOS(var->pixclock); | ||
651 | pr_debug("pixclock set for 60Hz refresh = %u ps\n", | ||
652 | var->pixclock); | ||
653 | } | ||
654 | |||
655 | var->height = -1; | ||
656 | var->width = -1; | ||
657 | var->grayscale = 0; | ||
658 | |||
659 | /* Copy nonstd field to/from sync for fbset usage */ | ||
660 | var->sync |= var->nonstd; | ||
661 | var->nonstd |= var->sync; | ||
662 | |||
663 | adjust_aoi_size_position(var, info); | ||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | static void set_fix(struct fb_info *info) | ||
668 | { | ||
669 | struct fb_fix_screeninfo *fix = &info->fix; | ||
670 | struct fb_var_screeninfo *var = &info->var; | ||
671 | struct mfb_info *mfbi = info->par; | ||
672 | |||
673 | strncpy(fix->id, mfbi->id, strlen(mfbi->id)); | ||
674 | fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; | ||
675 | fix->type = FB_TYPE_PACKED_PIXELS; | ||
676 | fix->accel = FB_ACCEL_NONE; | ||
677 | fix->visual = FB_VISUAL_TRUECOLOR; | ||
678 | fix->xpanstep = 1; | ||
679 | fix->ypanstep = 1; | ||
680 | } | ||
681 | |||
682 | static void update_lcdc(struct fb_info *info) | ||
683 | { | ||
684 | struct fb_var_screeninfo *var = &info->var; | ||
685 | struct mfb_info *mfbi = info->par; | ||
686 | struct fsl_diu_data *machine_data = mfbi->parent; | ||
687 | struct diu *hw; | ||
688 | int i, j; | ||
689 | char __iomem *cursor_base, *gamma_table_base; | ||
690 | |||
691 | u32 temp; | ||
692 | |||
693 | hw = dr.diu_reg; | ||
694 | |||
695 | if (mfbi->type == MFB_TYPE_OFF) { | ||
696 | fsl_diu_disable_panel(info); | ||
697 | return; | ||
698 | } | ||
699 | |||
700 | diu_ops.set_monitor_port(machine_data->monitor_port); | ||
701 | gamma_table_base = pool.gamma.vaddr; | ||
702 | cursor_base = pool.cursor.vaddr; | ||
703 | /* Prep for DIU init - gamma table, cursor table */ | ||
704 | |||
705 | for (i = 0; i <= 2; i++) | ||
706 | for (j = 0; j <= 255; j++) | ||
707 | *gamma_table_base++ = j; | ||
708 | |||
709 | diu_ops.set_gamma_table(machine_data->monitor_port, pool.gamma.vaddr); | ||
710 | |||
711 | pr_debug("update-lcdc: HW - %p\n Disabling DIU\n", hw); | ||
712 | disable_lcdc(info); | ||
713 | |||
714 | /* Program DIU registers */ | ||
715 | |||
716 | out_be32(&hw->gamma, pool.gamma.paddr); | ||
717 | out_be32(&hw->cursor, pool.cursor.paddr); | ||
718 | |||
719 | out_be32(&hw->bgnd, 0x007F7F7F); /* BGND */ | ||
720 | out_be32(&hw->bgnd_wb, 0); /* BGND_WB */ | ||
721 | out_be32(&hw->disp_size, (var->yres << 16 | var->xres)); | ||
722 | /* DISP SIZE */ | ||
723 | pr_debug("DIU xres: %d\n", var->xres); | ||
724 | pr_debug("DIU yres: %d\n", var->yres); | ||
725 | |||
726 | out_be32(&hw->wb_size, 0); /* WB SIZE */ | ||
727 | out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */ | ||
728 | |||
729 | /* Horizontal and vertical configuration register */ | ||
730 | temp = var->left_margin << 22 | /* BP_H */ | ||
731 | var->hsync_len << 11 | /* PW_H */ | ||
732 | var->right_margin; /* FP_H */ | ||
733 | |||
734 | out_be32(&hw->hsyn_para, temp); | ||
735 | |||
736 | temp = var->upper_margin << 22 | /* BP_V */ | ||
737 | var->vsync_len << 11 | /* PW_V */ | ||
738 | var->lower_margin; /* FP_V */ | ||
739 | |||
740 | out_be32(&hw->vsyn_para, temp); | ||
741 | |||
742 | pr_debug("DIU right_margin - %d\n", var->right_margin); | ||
743 | pr_debug("DIU left_margin - %d\n", var->left_margin); | ||
744 | pr_debug("DIU hsync_len - %d\n", var->hsync_len); | ||
745 | pr_debug("DIU upper_margin - %d\n", var->upper_margin); | ||
746 | pr_debug("DIU lower_margin - %d\n", var->lower_margin); | ||
747 | pr_debug("DIU vsync_len - %d\n", var->vsync_len); | ||
748 | pr_debug("DIU HSYNC - 0x%08x\n", hw->hsyn_para); | ||
749 | pr_debug("DIU VSYNC - 0x%08x\n", hw->vsyn_para); | ||
750 | |||
751 | diu_ops.set_pixel_clock(var->pixclock); | ||
752 | |||
753 | out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */ | ||
754 | out_be32(&hw->thresholds, 0x00037800); /* The Thresholds */ | ||
755 | out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */ | ||
756 | out_be32(&hw->plut, 0x01F5F666); | ||
757 | |||
758 | /* Enable the DIU */ | ||
759 | enable_lcdc(info); | ||
760 | } | ||
761 | |||
762 | static int map_video_memory(struct fb_info *info) | ||
763 | { | ||
764 | phys_addr_t phys; | ||
765 | |||
766 | pr_debug("info->var.xres_virtual = %d\n", info->var.xres_virtual); | ||
767 | pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual); | ||
768 | pr_debug("info->fix.line_length = %d\n", info->fix.line_length); | ||
769 | |||
770 | info->fix.smem_len = info->fix.line_length * info->var.yres_virtual; | ||
771 | pr_debug("MAP_VIDEO_MEMORY: smem_len = %d\n", info->fix.smem_len); | ||
772 | info->screen_base = fsl_diu_alloc(info->fix.smem_len, &phys); | ||
773 | if (info->screen_base == 0) { | ||
774 | printk(KERN_ERR "Unable to allocate fb memory\n"); | ||
775 | return -ENOMEM; | ||
776 | } | ||
777 | info->fix.smem_start = (unsigned long) phys; | ||
778 | info->screen_size = info->fix.smem_len; | ||
779 | |||
780 | pr_debug("Allocated fb @ paddr=0x%08lx, size=%d.\n", | ||
781 | info->fix.smem_start, | ||
782 | info->fix.smem_len); | ||
783 | pr_debug("screen base %p\n", info->screen_base); | ||
784 | |||
785 | return 0; | ||
786 | } | ||
787 | |||
788 | static void unmap_video_memory(struct fb_info *info) | ||
789 | { | ||
790 | fsl_diu_free(info->screen_base, info->fix.smem_len); | ||
791 | info->screen_base = 0; | ||
792 | info->fix.smem_start = 0; | ||
793 | info->fix.smem_len = 0; | ||
794 | } | ||
795 | |||
796 | /* | ||
797 | * Using the fb_var_screeninfo in fb_info we set the resolution of this | ||
798 | * particular framebuffer. This function alters the fb_fix_screeninfo stored | ||
799 | * in fb_info. It does not alter var in fb_info since we are using that | ||
800 | * data. This means we depend on the data in var inside fb_info to be | ||
801 | * supported by the hardware. fsl_diu_check_var is always called before | ||
802 | * fsl_diu_set_par to ensure this. | ||
803 | */ | ||
804 | static int fsl_diu_set_par(struct fb_info *info) | ||
805 | { | ||
806 | unsigned long len; | ||
807 | struct fb_var_screeninfo *var = &info->var; | ||
808 | struct mfb_info *mfbi = info->par; | ||
809 | struct fsl_diu_data *machine_data = mfbi->parent; | ||
810 | struct diu_ad *ad = mfbi->ad; | ||
811 | struct diu *hw; | ||
812 | |||
813 | hw = dr.diu_reg; | ||
814 | |||
815 | set_fix(info); | ||
816 | mfbi->cursor_reset = 1; | ||
817 | |||
818 | len = info->var.yres_virtual * info->fix.line_length; | ||
819 | /* Alloc & dealloc each time resolution/bpp change */ | ||
820 | if (len != info->fix.smem_len) { | ||
821 | if (info->fix.smem_start) | ||
822 | unmap_video_memory(info); | ||
823 | pr_debug("SET PAR: smem_len = %d\n", info->fix.smem_len); | ||
824 | |||
825 | /* Memory allocation for framebuffer */ | ||
826 | if (map_video_memory(info)) { | ||
827 | printk(KERN_ERR "Unable to allocate fb memory 1\n"); | ||
828 | return -ENOMEM; | ||
829 | } | ||
830 | } | ||
831 | |||
832 | ad->pix_fmt = | ||
833 | diu_ops.get_pixel_format(var->bits_per_pixel, | ||
834 | machine_data->monitor_port); | ||
835 | ad->addr = cpu_to_le32(info->fix.smem_start); | ||
836 | ad->src_size_g_alpha = cpu_to_le32((var->yres << 12) | | ||
837 | var->xres) | mfbi->g_alpha; | ||
838 | /* fix me. AOI should not be greater than display size */ | ||
839 | ad->aoi_size = cpu_to_le32((var->yres << 16) | var->xres); | ||
840 | ad->offset_xyi = 0; | ||
841 | ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d); | ||
842 | |||
843 | /* Disable chroma keying function */ | ||
844 | ad->ckmax_r = 0; | ||
845 | ad->ckmax_g = 0; | ||
846 | ad->ckmax_b = 0; | ||
847 | |||
848 | ad->ckmin_r = 255; | ||
849 | ad->ckmin_g = 255; | ||
850 | ad->ckmin_b = 255; | ||
851 | |||
852 | if (mfbi->index == 0) | ||
853 | update_lcdc(info); | ||
854 | return 0; | ||
855 | } | ||
856 | |||
857 | static inline __u32 CNVT_TOHW(__u32 val, __u32 width) | ||
858 | { | ||
859 | return ((val<<width) + 0x7FFF - val)>>16; | ||
860 | } | ||
861 | |||
862 | /* | ||
863 | * Set a single color register. The values supplied have a 16 bit magnitude | ||
864 | * which needs to be scaled in this function for the hardware. Things to take | ||
865 | * into consideration are how many color registers, if any, are supported with | ||
866 | * the current color visual. With truecolor mode no color palettes are | ||
867 | * supported. Here a psuedo palette is created which we store the value in | ||
868 | * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited | ||
869 | * color palette. | ||
870 | */ | ||
871 | static int fsl_diu_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
872 | unsigned blue, unsigned transp, struct fb_info *info) | ||
873 | { | ||
874 | int ret = 1; | ||
875 | |||
876 | /* | ||
877 | * If greyscale is true, then we convert the RGB value | ||
878 | * to greyscale no matter what visual we are using. | ||
879 | */ | ||
880 | if (info->var.grayscale) | ||
881 | red = green = blue = (19595 * red + 38470 * green + | ||
882 | 7471 * blue) >> 16; | ||
883 | switch (info->fix.visual) { | ||
884 | case FB_VISUAL_TRUECOLOR: | ||
885 | /* | ||
886 | * 16-bit True Colour. We encode the RGB value | ||
887 | * according to the RGB bitfield information. | ||
888 | */ | ||
889 | if (regno < 16) { | ||
890 | u32 *pal = info->pseudo_palette; | ||
891 | u32 v; | ||
892 | |||
893 | red = CNVT_TOHW(red, info->var.red.length); | ||
894 | green = CNVT_TOHW(green, info->var.green.length); | ||
895 | blue = CNVT_TOHW(blue, info->var.blue.length); | ||
896 | transp = CNVT_TOHW(transp, info->var.transp.length); | ||
897 | |||
898 | v = (red << info->var.red.offset) | | ||
899 | (green << info->var.green.offset) | | ||
900 | (blue << info->var.blue.offset) | | ||
901 | (transp << info->var.transp.offset); | ||
902 | |||
903 | pal[regno] = v; | ||
904 | ret = 0; | ||
905 | } | ||
906 | break; | ||
907 | case FB_VISUAL_STATIC_PSEUDOCOLOR: | ||
908 | case FB_VISUAL_PSEUDOCOLOR: | ||
909 | break; | ||
910 | } | ||
911 | |||
912 | return ret; | ||
913 | } | ||
914 | |||
915 | /* | ||
916 | * Pan (or wrap, depending on the `vmode' field) the display using the | ||
917 | * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values | ||
918 | * don't fit, return -EINVAL. | ||
919 | */ | ||
920 | static int fsl_diu_pan_display(struct fb_var_screeninfo *var, | ||
921 | struct fb_info *info) | ||
922 | { | ||
923 | if ((info->var.xoffset == var->xoffset) && | ||
924 | (info->var.yoffset == var->yoffset)) | ||
925 | return 0; /* No change, do nothing */ | ||
926 | |||
927 | if (var->xoffset < 0 || var->yoffset < 0 | ||
928 | || var->xoffset + info->var.xres > info->var.xres_virtual | ||
929 | || var->yoffset + info->var.yres > info->var.yres_virtual) | ||
930 | return -EINVAL; | ||
931 | |||
932 | info->var.xoffset = var->xoffset; | ||
933 | info->var.yoffset = var->yoffset; | ||
934 | |||
935 | if (var->vmode & FB_VMODE_YWRAP) | ||
936 | info->var.vmode |= FB_VMODE_YWRAP; | ||
937 | else | ||
938 | info->var.vmode &= ~FB_VMODE_YWRAP; | ||
939 | |||
940 | return 0; | ||
941 | } | ||
942 | |||
943 | /* | ||
944 | * Blank the screen if blank_mode != 0, else unblank. Return 0 if blanking | ||
945 | * succeeded, != 0 if un-/blanking failed. | ||
946 | * blank_mode == 2: suspend vsync | ||
947 | * blank_mode == 3: suspend hsync | ||
948 | * blank_mode == 4: powerdown | ||
949 | */ | ||
950 | static int fsl_diu_blank(int blank_mode, struct fb_info *info) | ||
951 | { | ||
952 | struct mfb_info *mfbi = info->par; | ||
953 | |||
954 | mfbi->blank = blank_mode; | ||
955 | |||
956 | switch (blank_mode) { | ||
957 | case FB_BLANK_VSYNC_SUSPEND: | ||
958 | case FB_BLANK_HSYNC_SUSPEND: | ||
959 | /* FIXME: fixes to enable_panel and enable lcdc needed */ | ||
960 | case FB_BLANK_NORMAL: | ||
961 | /* fsl_diu_disable_panel(info);*/ | ||
962 | break; | ||
963 | case FB_BLANK_POWERDOWN: | ||
964 | /* disable_lcdc(info); */ | ||
965 | break; | ||
966 | case FB_BLANK_UNBLANK: | ||
967 | /* fsl_diu_enable_panel(info);*/ | ||
968 | break; | ||
969 | } | ||
970 | |||
971 | return 0; | ||
972 | } | ||
973 | |||
974 | static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, | ||
975 | unsigned long arg) | ||
976 | { | ||
977 | struct mfb_info *mfbi = info->par; | ||
978 | struct diu_ad *ad = mfbi->ad; | ||
979 | struct mfb_chroma_key ck; | ||
980 | unsigned char global_alpha; | ||
981 | struct aoi_display_offset aoi_d; | ||
982 | __u32 pix_fmt; | ||
983 | void __user *buf = (void __user *)arg; | ||
984 | |||
985 | if (!arg) | ||
986 | return -EINVAL; | ||
987 | switch (cmd) { | ||
988 | case MFB_SET_PIXFMT: | ||
989 | if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt))) | ||
990 | return -EFAULT; | ||
991 | ad->pix_fmt = pix_fmt; | ||
992 | pr_debug("Set pixel format to 0x%08x\n", ad->pix_fmt); | ||
993 | break; | ||
994 | case MFB_GET_PIXFMT: | ||
995 | pix_fmt = ad->pix_fmt; | ||
996 | if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt))) | ||
997 | return -EFAULT; | ||
998 | pr_debug("get pixel format 0x%08x\n", ad->pix_fmt); | ||
999 | break; | ||
1000 | case MFB_SET_AOID: | ||
1001 | if (copy_from_user(&aoi_d, buf, sizeof(aoi_d))) | ||
1002 | return -EFAULT; | ||
1003 | mfbi->x_aoi_d = aoi_d.x_aoi_d; | ||
1004 | mfbi->y_aoi_d = aoi_d.y_aoi_d; | ||
1005 | pr_debug("set AOI display offset of index %d to (%d,%d)\n", | ||
1006 | mfbi->index, aoi_d.x_aoi_d, aoi_d.y_aoi_d); | ||
1007 | fsl_diu_check_var(&info->var, info); | ||
1008 | fsl_diu_set_par(info); | ||
1009 | break; | ||
1010 | case MFB_GET_AOID: | ||
1011 | aoi_d.x_aoi_d = mfbi->x_aoi_d; | ||
1012 | aoi_d.y_aoi_d = mfbi->y_aoi_d; | ||
1013 | if (copy_to_user(buf, &aoi_d, sizeof(aoi_d))) | ||
1014 | return -EFAULT; | ||
1015 | pr_debug("get AOI display offset of index %d (%d,%d)\n", | ||
1016 | mfbi->index, aoi_d.x_aoi_d, aoi_d.y_aoi_d); | ||
1017 | break; | ||
1018 | case MFB_GET_ALPHA: | ||
1019 | global_alpha = mfbi->g_alpha; | ||
1020 | if (copy_to_user(buf, &global_alpha, sizeof(global_alpha))) | ||
1021 | return -EFAULT; | ||
1022 | pr_debug("get global alpha of index %d\n", mfbi->index); | ||
1023 | break; | ||
1024 | case MFB_SET_ALPHA: | ||
1025 | /* set panel information */ | ||
1026 | if (copy_from_user(&global_alpha, buf, sizeof(global_alpha))) | ||
1027 | return -EFAULT; | ||
1028 | ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) | | ||
1029 | (global_alpha & 0xff); | ||
1030 | mfbi->g_alpha = global_alpha; | ||
1031 | pr_debug("set global alpha for index %d\n", mfbi->index); | ||
1032 | break; | ||
1033 | case MFB_SET_CHROMA_KEY: | ||
1034 | /* set panel winformation */ | ||
1035 | if (copy_from_user(&ck, buf, sizeof(ck))) | ||
1036 | return -EFAULT; | ||
1037 | |||
1038 | if (ck.enable && | ||
1039 | (ck.red_max < ck.red_min || | ||
1040 | ck.green_max < ck.green_min || | ||
1041 | ck.blue_max < ck.blue_min)) | ||
1042 | return -EINVAL; | ||
1043 | |||
1044 | if (!ck.enable) { | ||
1045 | ad->ckmax_r = 0; | ||
1046 | ad->ckmax_g = 0; | ||
1047 | ad->ckmax_b = 0; | ||
1048 | ad->ckmin_r = 255; | ||
1049 | ad->ckmin_g = 255; | ||
1050 | ad->ckmin_b = 255; | ||
1051 | } else { | ||
1052 | ad->ckmax_r = ck.red_max; | ||
1053 | ad->ckmax_g = ck.green_max; | ||
1054 | ad->ckmax_b = ck.blue_max; | ||
1055 | ad->ckmin_r = ck.red_min; | ||
1056 | ad->ckmin_g = ck.green_min; | ||
1057 | ad->ckmin_b = ck.blue_min; | ||
1058 | } | ||
1059 | pr_debug("set chroma key\n"); | ||
1060 | break; | ||
1061 | case FBIOGET_GWINFO: | ||
1062 | if (mfbi->type == MFB_TYPE_OFF) | ||
1063 | return -ENODEV; | ||
1064 | /* get graphic window information */ | ||
1065 | if (copy_to_user(buf, ad, sizeof(*ad))) | ||
1066 | return -EFAULT; | ||
1067 | break; | ||
1068 | case FBIOGET_HWCINFO: | ||
1069 | pr_debug("FBIOGET_HWCINFO:0x%08x\n", FBIOGET_HWCINFO); | ||
1070 | break; | ||
1071 | case FBIOPUT_MODEINFO: | ||
1072 | pr_debug("FBIOPUT_MODEINFO:0x%08x\n", FBIOPUT_MODEINFO); | ||
1073 | break; | ||
1074 | case FBIOGET_DISPINFO: | ||
1075 | pr_debug("FBIOGET_DISPINFO:0x%08x\n", FBIOGET_DISPINFO); | ||
1076 | break; | ||
1077 | |||
1078 | default: | ||
1079 | printk(KERN_ERR "Unknown ioctl command (0x%08X)\n", cmd); | ||
1080 | return -ENOIOCTLCMD; | ||
1081 | } | ||
1082 | |||
1083 | return 0; | ||
1084 | } | ||
1085 | |||
1086 | /* turn on fb if count == 1 | ||
1087 | */ | ||
1088 | static int fsl_diu_open(struct fb_info *info, int user) | ||
1089 | { | ||
1090 | struct mfb_info *mfbi = info->par; | ||
1091 | int res = 0; | ||
1092 | |||
1093 | spin_lock(&diu_lock); | ||
1094 | mfbi->count++; | ||
1095 | if (mfbi->count == 1) { | ||
1096 | pr_debug("open plane index %d\n", mfbi->index); | ||
1097 | fsl_diu_check_var(&info->var, info); | ||
1098 | res = fsl_diu_set_par(info); | ||
1099 | if (res < 0) | ||
1100 | mfbi->count--; | ||
1101 | else { | ||
1102 | res = fsl_diu_enable_panel(info); | ||
1103 | if (res < 0) | ||
1104 | mfbi->count--; | ||
1105 | } | ||
1106 | } | ||
1107 | |||
1108 | spin_unlock(&diu_lock); | ||
1109 | return res; | ||
1110 | } | ||
1111 | |||
1112 | /* turn off fb if count == 0 | ||
1113 | */ | ||
1114 | static int fsl_diu_release(struct fb_info *info, int user) | ||
1115 | { | ||
1116 | struct mfb_info *mfbi = info->par; | ||
1117 | int res = 0; | ||
1118 | |||
1119 | spin_lock(&diu_lock); | ||
1120 | mfbi->count--; | ||
1121 | if (mfbi->count == 0) { | ||
1122 | pr_debug("release plane index %d\n", mfbi->index); | ||
1123 | res = fsl_diu_disable_panel(info); | ||
1124 | if (res < 0) | ||
1125 | mfbi->count++; | ||
1126 | } | ||
1127 | spin_unlock(&diu_lock); | ||
1128 | return res; | ||
1129 | } | ||
1130 | |||
1131 | static struct fb_ops fsl_diu_ops = { | ||
1132 | .owner = THIS_MODULE, | ||
1133 | .fb_check_var = fsl_diu_check_var, | ||
1134 | .fb_set_par = fsl_diu_set_par, | ||
1135 | .fb_setcolreg = fsl_diu_setcolreg, | ||
1136 | .fb_blank = fsl_diu_blank, | ||
1137 | .fb_pan_display = fsl_diu_pan_display, | ||
1138 | .fb_fillrect = cfb_fillrect, | ||
1139 | .fb_copyarea = cfb_copyarea, | ||
1140 | .fb_imageblit = cfb_imageblit, | ||
1141 | .fb_ioctl = fsl_diu_ioctl, | ||
1142 | .fb_open = fsl_diu_open, | ||
1143 | .fb_release = fsl_diu_release, | ||
1144 | }; | ||
1145 | |||
1146 | static int init_fbinfo(struct fb_info *info) | ||
1147 | { | ||
1148 | struct mfb_info *mfbi = info->par; | ||
1149 | |||
1150 | info->device = NULL; | ||
1151 | info->var.activate = FB_ACTIVATE_NOW; | ||
1152 | info->fbops = &fsl_diu_ops; | ||
1153 | info->flags = FBINFO_FLAG_DEFAULT; | ||
1154 | info->pseudo_palette = &mfbi->pseudo_palette; | ||
1155 | |||
1156 | /* Allocate colormap */ | ||
1157 | fb_alloc_cmap(&info->cmap, 16, 0); | ||
1158 | return 0; | ||
1159 | } | ||
1160 | |||
1161 | static int install_fb(struct fb_info *info) | ||
1162 | { | ||
1163 | int rc; | ||
1164 | struct mfb_info *mfbi = info->par; | ||
1165 | const char *aoi_mode, *init_aoi_mode = "320x240"; | ||
1166 | |||
1167 | if (init_fbinfo(info)) | ||
1168 | return -EINVAL; | ||
1169 | |||
1170 | if (mfbi->index == 0) /* plane 0 */ | ||
1171 | aoi_mode = fb_mode; | ||
1172 | else | ||
1173 | aoi_mode = init_aoi_mode; | ||
1174 | pr_debug("mode used = %s\n", aoi_mode); | ||
1175 | rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db, | ||
1176 | ARRAY_SIZE(fsl_diu_mode_db), &fsl_diu_default_mode, default_bpp); | ||
1177 | |||
1178 | switch (rc) { | ||
1179 | case 1: | ||
1180 | pr_debug("using mode specified in @mode\n"); | ||
1181 | break; | ||
1182 | case 2: | ||
1183 | pr_debug("using mode specified in @mode " | ||
1184 | "with ignored refresh rate\n"); | ||
1185 | break; | ||
1186 | case 3: | ||
1187 | pr_debug("using mode default mode\n"); | ||
1188 | break; | ||
1189 | case 4: | ||
1190 | pr_debug("using mode from list\n"); | ||
1191 | break; | ||
1192 | default: | ||
1193 | pr_debug("rc = %d\n", rc); | ||
1194 | pr_debug("failed to find mode\n"); | ||
1195 | return -EINVAL; | ||
1196 | break; | ||
1197 | } | ||
1198 | |||
1199 | pr_debug("xres_virtual %d\n", info->var.xres_virtual); | ||
1200 | pr_debug("bits_per_pixel %d\n", info->var.bits_per_pixel); | ||
1201 | |||
1202 | pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual); | ||
1203 | pr_debug("info->fix.line_length = %d\n", info->fix.line_length); | ||
1204 | |||
1205 | if (mfbi->type == MFB_TYPE_OFF) | ||
1206 | mfbi->blank = FB_BLANK_NORMAL; | ||
1207 | else | ||
1208 | mfbi->blank = FB_BLANK_UNBLANK; | ||
1209 | |||
1210 | if (fsl_diu_check_var(&info->var, info)) { | ||
1211 | printk(KERN_ERR "fb_check_var failed"); | ||
1212 | fb_dealloc_cmap(&info->cmap); | ||
1213 | return -EINVAL; | ||
1214 | } | ||
1215 | |||
1216 | if (fsl_diu_set_par(info)) { | ||
1217 | printk(KERN_ERR "fb_set_par failed"); | ||
1218 | fb_dealloc_cmap(&info->cmap); | ||
1219 | return -EINVAL; | ||
1220 | } | ||
1221 | |||
1222 | if (register_framebuffer(info) < 0) { | ||
1223 | printk(KERN_ERR "register_framebuffer failed"); | ||
1224 | unmap_video_memory(info); | ||
1225 | fb_dealloc_cmap(&info->cmap); | ||
1226 | return -EINVAL; | ||
1227 | } | ||
1228 | |||
1229 | mfbi->registered = 1; | ||
1230 | printk(KERN_INFO "fb%d: %s fb device registered successfully.\n", | ||
1231 | info->node, info->fix.id); | ||
1232 | |||
1233 | return 0; | ||
1234 | } | ||
1235 | |||
1236 | static void __exit uninstall_fb(struct fb_info *info) | ||
1237 | { | ||
1238 | struct mfb_info *mfbi = info->par; | ||
1239 | |||
1240 | if (!mfbi->registered) | ||
1241 | return; | ||
1242 | |||
1243 | unregister_framebuffer(info); | ||
1244 | unmap_video_memory(info); | ||
1245 | if (&info->cmap) | ||
1246 | fb_dealloc_cmap(&info->cmap); | ||
1247 | |||
1248 | mfbi->registered = 0; | ||
1249 | } | ||
1250 | |||
1251 | static irqreturn_t fsl_diu_isr(int irq, void *dev_id) | ||
1252 | { | ||
1253 | struct diu *hw = dr.diu_reg; | ||
1254 | unsigned int status = in_be32(&hw->int_status); | ||
1255 | |||
1256 | if (status) { | ||
1257 | /* This is the workaround for underrun */ | ||
1258 | if (status & INT_UNDRUN) { | ||
1259 | out_be32(&hw->diu_mode, 0); | ||
1260 | pr_debug("Err: DIU occurs underrun!\n"); | ||
1261 | udelay(1); | ||
1262 | out_be32(&hw->diu_mode, 1); | ||
1263 | } | ||
1264 | #if defined(CONFIG_NOT_COHERENT_CACHE) | ||
1265 | else if (status & INT_VSYNC) { | ||
1266 | unsigned int i; | ||
1267 | for (i = 0; i < coherence_data_size; | ||
1268 | i += d_cache_line_size) | ||
1269 | __asm__ __volatile__ ( | ||
1270 | "dcbz 0, %[input]" | ||
1271 | ::[input]"r"(&coherence_data[i])); | ||
1272 | } | ||
1273 | #endif | ||
1274 | return IRQ_HANDLED; | ||
1275 | } | ||
1276 | return IRQ_NONE; | ||
1277 | } | ||
1278 | |||
1279 | static int request_irq_local(int irq) | ||
1280 | { | ||
1281 | unsigned long status, ints; | ||
1282 | struct diu *hw; | ||
1283 | int ret; | ||
1284 | |||
1285 | hw = dr.diu_reg; | ||
1286 | |||
1287 | /* Read to clear the status */ | ||
1288 | status = in_be32(&hw->int_status); | ||
1289 | |||
1290 | ret = request_irq(irq, fsl_diu_isr, 0, "diu", 0); | ||
1291 | if (ret) | ||
1292 | pr_info("Request diu IRQ failed.\n"); | ||
1293 | else { | ||
1294 | ints = INT_PARERR | INT_LS_BF_VS; | ||
1295 | #if !defined(CONFIG_NOT_COHERENT_CACHE) | ||
1296 | ints |= INT_VSYNC; | ||
1297 | #endif | ||
1298 | if (dr.mode == MFB_MODE2 || dr.mode == MFB_MODE3) | ||
1299 | ints |= INT_VSYNC_WB; | ||
1300 | |||
1301 | /* Read to clear the status */ | ||
1302 | status = in_be32(&hw->int_status); | ||
1303 | out_be32(&hw->int_mask, ints); | ||
1304 | } | ||
1305 | return ret; | ||
1306 | } | ||
1307 | |||
1308 | static void free_irq_local(int irq) | ||
1309 | { | ||
1310 | struct diu *hw = dr.diu_reg; | ||
1311 | |||
1312 | /* Disable all LCDC interrupt */ | ||
1313 | out_be32(&hw->int_mask, 0x1f); | ||
1314 | |||
1315 | free_irq(irq, 0); | ||
1316 | } | ||
1317 | |||
1318 | #ifdef CONFIG_PM | ||
1319 | /* | ||
1320 | * Power management hooks. Note that we won't be called from IRQ context, | ||
1321 | * unlike the blank functions above, so we may sleep. | ||
1322 | */ | ||
1323 | static int fsl_diu_suspend(struct of_device *dev, pm_message_t state) | ||
1324 | { | ||
1325 | struct fsl_diu_data *machine_data; | ||
1326 | |||
1327 | machine_data = dev_get_drvdata(&ofdev->dev); | ||
1328 | disable_lcdc(machine_data->fsl_diu_info[0]); | ||
1329 | |||
1330 | return 0; | ||
1331 | } | ||
1332 | |||
1333 | static int fsl_diu_resume(struct of_device *dev) | ||
1334 | { | ||
1335 | struct fsl_diu_data *machine_data; | ||
1336 | |||
1337 | machine_data = dev_get_drvdata(&ofdev->dev); | ||
1338 | enable_lcdc(machine_data->fsl_diu_info[0]); | ||
1339 | |||
1340 | return 0; | ||
1341 | } | ||
1342 | |||
1343 | #else | ||
1344 | #define fsl_diu_suspend NULL | ||
1345 | #define fsl_diu_resume NULL | ||
1346 | #endif /* CONFIG_PM */ | ||
1347 | |||
1348 | /* Align to 64-bit(8-byte), 32-byte, etc. */ | ||
1349 | static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align) | ||
1350 | { | ||
1351 | u32 offset, ssize; | ||
1352 | u32 mask; | ||
1353 | dma_addr_t paddr = 0; | ||
1354 | |||
1355 | ssize = size + bytes_align; | ||
1356 | buf->vaddr = dma_alloc_coherent(0, ssize, &paddr, GFP_DMA | __GFP_ZERO); | ||
1357 | if (!buf->vaddr) | ||
1358 | return -ENOMEM; | ||
1359 | |||
1360 | buf->paddr = (__u32) paddr; | ||
1361 | |||
1362 | mask = bytes_align - 1; | ||
1363 | offset = (u32)buf->paddr & mask; | ||
1364 | if (offset) { | ||
1365 | buf->offset = bytes_align - offset; | ||
1366 | buf->paddr = (u32)buf->paddr + offset; | ||
1367 | } else | ||
1368 | buf->offset = 0; | ||
1369 | return 0; | ||
1370 | } | ||
1371 | |||
1372 | static void free_buf(struct diu_addr *buf, u32 size, u32 bytes_align) | ||
1373 | { | ||
1374 | dma_free_coherent(0, size + bytes_align, | ||
1375 | buf->vaddr, (buf->paddr - buf->offset)); | ||
1376 | return; | ||
1377 | } | ||
1378 | |||
1379 | static ssize_t store_monitor(struct device *device, | ||
1380 | struct device_attribute *attr, const char *buf, size_t count) | ||
1381 | { | ||
1382 | int old_monitor_port; | ||
1383 | unsigned long val; | ||
1384 | struct fsl_diu_data *machine_data = | ||
1385 | container_of(attr, struct fsl_diu_data, dev_attr); | ||
1386 | |||
1387 | if (strict_strtoul(buf, 10, &val)) | ||
1388 | return 0; | ||
1389 | |||
1390 | old_monitor_port = machine_data->monitor_port; | ||
1391 | machine_data->monitor_port = diu_ops.set_sysfs_monitor_port(val); | ||
1392 | |||
1393 | if (old_monitor_port != machine_data->monitor_port) { | ||
1394 | /* All AOIs need adjust pixel format | ||
1395 | * fsl_diu_set_par only change the pixsel format here | ||
1396 | * unlikely to fail. */ | ||
1397 | fsl_diu_set_par(machine_data->fsl_diu_info[0]); | ||
1398 | fsl_diu_set_par(machine_data->fsl_diu_info[1]); | ||
1399 | fsl_diu_set_par(machine_data->fsl_diu_info[2]); | ||
1400 | fsl_diu_set_par(machine_data->fsl_diu_info[3]); | ||
1401 | fsl_diu_set_par(machine_data->fsl_diu_info[4]); | ||
1402 | } | ||
1403 | return count; | ||
1404 | } | ||
1405 | |||
1406 | static ssize_t show_monitor(struct device *device, | ||
1407 | struct device_attribute *attr, char *buf) | ||
1408 | { | ||
1409 | struct fsl_diu_data *machine_data = | ||
1410 | container_of(attr, struct fsl_diu_data, dev_attr); | ||
1411 | return diu_ops.show_monitor_port(machine_data->monitor_port, buf); | ||
1412 | } | ||
1413 | |||
1414 | static int fsl_diu_probe(struct of_device *ofdev, | ||
1415 | const struct of_device_id *match) | ||
1416 | { | ||
1417 | struct device_node *np = ofdev->node; | ||
1418 | struct mfb_info *mfbi; | ||
1419 | phys_addr_t dummy_ad_addr; | ||
1420 | int ret, i, error = 0; | ||
1421 | struct resource res; | ||
1422 | struct fsl_diu_data *machine_data; | ||
1423 | |||
1424 | machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL); | ||
1425 | if (!machine_data) | ||
1426 | return -ENOMEM; | ||
1427 | |||
1428 | for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) { | ||
1429 | machine_data->fsl_diu_info[i] = | ||
1430 | framebuffer_alloc(sizeof(struct mfb_info), &ofdev->dev); | ||
1431 | if (!machine_data->fsl_diu_info[i]) { | ||
1432 | dev_err(&ofdev->dev, "cannot allocate memory\n"); | ||
1433 | ret = -ENOMEM; | ||
1434 | goto error2; | ||
1435 | } | ||
1436 | mfbi = machine_data->fsl_diu_info[i]->par; | ||
1437 | memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); | ||
1438 | mfbi->parent = machine_data; | ||
1439 | } | ||
1440 | |||
1441 | ret = of_address_to_resource(np, 0, &res); | ||
1442 | if (ret) { | ||
1443 | dev_err(&ofdev->dev, "could not obtain DIU address\n"); | ||
1444 | goto error; | ||
1445 | } | ||
1446 | if (!res.start) { | ||
1447 | dev_err(&ofdev->dev, "invalid DIU address\n"); | ||
1448 | goto error; | ||
1449 | } | ||
1450 | dev_dbg(&ofdev->dev, "%s, res.start: 0x%08x\n", __func__, res.start); | ||
1451 | |||
1452 | dr.diu_reg = ioremap(res.start, sizeof(struct diu)); | ||
1453 | if (!dr.diu_reg) { | ||
1454 | dev_err(&ofdev->dev, "Err: can't map DIU registers!\n"); | ||
1455 | ret = -EFAULT; | ||
1456 | goto error2; | ||
1457 | } | ||
1458 | |||
1459 | out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU anyway*/ | ||
1460 | |||
1461 | /* Get the IRQ of the DIU */ | ||
1462 | machine_data->irq = irq_of_parse_and_map(np, 0); | ||
1463 | |||
1464 | if (!machine_data->irq) { | ||
1465 | dev_err(&ofdev->dev, "could not get DIU IRQ\n"); | ||
1466 | ret = -EINVAL; | ||
1467 | goto error; | ||
1468 | } | ||
1469 | machine_data->monitor_port = monitor_port; | ||
1470 | |||
1471 | /* Area descriptor memory pool aligns to 64-bit boundary */ | ||
1472 | if (allocate_buf(&pool.ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8)) | ||
1473 | return -ENOMEM; | ||
1474 | |||
1475 | /* Get memory for Gamma Table - 32-byte aligned memory */ | ||
1476 | if (allocate_buf(&pool.gamma, 768, 32)) { | ||
1477 | ret = -ENOMEM; | ||
1478 | goto error; | ||
1479 | } | ||
1480 | |||
1481 | /* For performance, cursor bitmap buffer aligns to 32-byte boundary */ | ||
1482 | if (allocate_buf(&pool.cursor, MAX_CURS * MAX_CURS * 2, 32)) { | ||
1483 | ret = -ENOMEM; | ||
1484 | goto error; | ||
1485 | } | ||
1486 | |||
1487 | i = ARRAY_SIZE(machine_data->fsl_diu_info); | ||
1488 | machine_data->dummy_ad = (struct diu_ad *) | ||
1489 | ((u32)pool.ad.vaddr + pool.ad.offset) + i; | ||
1490 | machine_data->dummy_ad->paddr = pool.ad.paddr + | ||
1491 | i * sizeof(struct diu_ad); | ||
1492 | machine_data->dummy_aoi_virt = fsl_diu_alloc(64, &dummy_ad_addr); | ||
1493 | if (!machine_data->dummy_aoi_virt) { | ||
1494 | ret = -ENOMEM; | ||
1495 | goto error; | ||
1496 | } | ||
1497 | machine_data->dummy_ad->addr = cpu_to_le32(dummy_ad_addr); | ||
1498 | machine_data->dummy_ad->pix_fmt = 0x88882317; | ||
1499 | machine_data->dummy_ad->src_size_g_alpha = cpu_to_le32((4 << 12) | 4); | ||
1500 | machine_data->dummy_ad->aoi_size = cpu_to_le32((4 << 16) | 2); | ||
1501 | machine_data->dummy_ad->offset_xyi = 0; | ||
1502 | machine_data->dummy_ad->offset_xyd = 0; | ||
1503 | machine_data->dummy_ad->next_ad = 0; | ||
1504 | |||
1505 | out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->paddr); | ||
1506 | out_be32(&dr.diu_reg->desc[1], machine_data->dummy_ad->paddr); | ||
1507 | out_be32(&dr.diu_reg->desc[2], machine_data->dummy_ad->paddr); | ||
1508 | |||
1509 | for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) { | ||
1510 | machine_data->fsl_diu_info[i]->fix.smem_start = 0; | ||
1511 | mfbi = machine_data->fsl_diu_info[i]->par; | ||
1512 | mfbi->ad = (struct diu_ad *)((u32)pool.ad.vaddr | ||
1513 | + pool.ad.offset) + i; | ||
1514 | mfbi->ad->paddr = pool.ad.paddr + i * sizeof(struct diu_ad); | ||
1515 | ret = install_fb(machine_data->fsl_diu_info[i]); | ||
1516 | if (ret) { | ||
1517 | dev_err(&ofdev->dev, | ||
1518 | "Failed to register framebuffer %d\n", | ||
1519 | i); | ||
1520 | goto error; | ||
1521 | } | ||
1522 | } | ||
1523 | |||
1524 | if (request_irq_local(machine_data->irq)) { | ||
1525 | dev_err(machine_data->fsl_diu_info[0]->dev, | ||
1526 | "could not request irq for diu."); | ||
1527 | goto error; | ||
1528 | } | ||
1529 | |||
1530 | machine_data->dev_attr.attr.name = "monitor"; | ||
1531 | machine_data->dev_attr.attr.mode = S_IRUGO|S_IWUSR; | ||
1532 | machine_data->dev_attr.show = show_monitor; | ||
1533 | machine_data->dev_attr.store = store_monitor; | ||
1534 | error = device_create_file(machine_data->fsl_diu_info[0]->dev, | ||
1535 | &machine_data->dev_attr); | ||
1536 | if (error) { | ||
1537 | dev_err(machine_data->fsl_diu_info[0]->dev, | ||
1538 | "could not create sysfs %s file\n", | ||
1539 | machine_data->dev_attr.attr.name); | ||
1540 | } | ||
1541 | |||
1542 | dev_set_drvdata(&ofdev->dev, machine_data); | ||
1543 | return 0; | ||
1544 | |||
1545 | error: | ||
1546 | for (i = ARRAY_SIZE(machine_data->fsl_diu_info); | ||
1547 | i > 0; i--) | ||
1548 | uninstall_fb(machine_data->fsl_diu_info[i - 1]); | ||
1549 | if (pool.ad.vaddr) | ||
1550 | free_buf(&pool.ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8); | ||
1551 | if (pool.gamma.vaddr) | ||
1552 | free_buf(&pool.gamma, 768, 32); | ||
1553 | if (pool.cursor.vaddr) | ||
1554 | free_buf(&pool.cursor, MAX_CURS * MAX_CURS * 2, 32); | ||
1555 | if (machine_data->dummy_aoi_virt) | ||
1556 | fsl_diu_free(machine_data->dummy_aoi_virt, 64); | ||
1557 | iounmap(dr.diu_reg); | ||
1558 | |||
1559 | error2: | ||
1560 | for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) | ||
1561 | if (machine_data->fsl_diu_info[i]) | ||
1562 | framebuffer_release(machine_data->fsl_diu_info[i]); | ||
1563 | kfree(machine_data); | ||
1564 | |||
1565 | return ret; | ||
1566 | } | ||
1567 | |||
1568 | |||
1569 | static int fsl_diu_remove(struct of_device *ofdev) | ||
1570 | { | ||
1571 | struct fsl_diu_data *machine_data; | ||
1572 | int i; | ||
1573 | |||
1574 | machine_data = dev_get_drvdata(&ofdev->dev); | ||
1575 | disable_lcdc(machine_data->fsl_diu_info[0]); | ||
1576 | free_irq_local(machine_data->irq); | ||
1577 | for (i = ARRAY_SIZE(machine_data->fsl_diu_info); i > 0; i--) | ||
1578 | uninstall_fb(machine_data->fsl_diu_info[i - 1]); | ||
1579 | if (pool.ad.vaddr) | ||
1580 | free_buf(&pool.ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8); | ||
1581 | if (pool.gamma.vaddr) | ||
1582 | free_buf(&pool.gamma, 768, 32); | ||
1583 | if (pool.cursor.vaddr) | ||
1584 | free_buf(&pool.cursor, MAX_CURS * MAX_CURS * 2, 32); | ||
1585 | if (machine_data->dummy_aoi_virt) | ||
1586 | fsl_diu_free(machine_data->dummy_aoi_virt, 64); | ||
1587 | iounmap(dr.diu_reg); | ||
1588 | for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) | ||
1589 | if (machine_data->fsl_diu_info[i]) | ||
1590 | framebuffer_release(machine_data->fsl_diu_info[i]); | ||
1591 | kfree(machine_data); | ||
1592 | |||
1593 | return 0; | ||
1594 | } | ||
1595 | |||
1596 | #ifndef MODULE | ||
1597 | static int __init fsl_diu_setup(char *options) | ||
1598 | { | ||
1599 | char *opt; | ||
1600 | unsigned long val; | ||
1601 | |||
1602 | if (!options || !*options) | ||
1603 | return 0; | ||
1604 | |||
1605 | while ((opt = strsep(&options, ",")) != NULL) { | ||
1606 | if (!*opt) | ||
1607 | continue; | ||
1608 | if (!strncmp(opt, "monitor=", 8)) { | ||
1609 | if (!strict_strtoul(opt + 8, 10, &val) && (val <= 2)) | ||
1610 | monitor_port = val; | ||
1611 | } else if (!strncmp(opt, "bpp=", 4)) { | ||
1612 | if (!strict_strtoul(opt + 4, 10, &val)) | ||
1613 | default_bpp = val; | ||
1614 | } else | ||
1615 | fb_mode = opt; | ||
1616 | } | ||
1617 | |||
1618 | return 0; | ||
1619 | } | ||
1620 | #endif | ||
1621 | |||
1622 | static struct of_device_id fsl_diu_match[] = { | ||
1623 | { | ||
1624 | .compatible = "fsl,diu", | ||
1625 | }, | ||
1626 | {} | ||
1627 | }; | ||
1628 | MODULE_DEVICE_TABLE(of, fsl_diu_match); | ||
1629 | |||
1630 | static struct of_platform_driver fsl_diu_driver = { | ||
1631 | .owner = THIS_MODULE, | ||
1632 | .name = "fsl_diu", | ||
1633 | .match_table = fsl_diu_match, | ||
1634 | .probe = fsl_diu_probe, | ||
1635 | .remove = fsl_diu_remove, | ||
1636 | .suspend = fsl_diu_suspend, | ||
1637 | .resume = fsl_diu_resume, | ||
1638 | }; | ||
1639 | |||
1640 | static int __init fsl_diu_init(void) | ||
1641 | { | ||
1642 | #ifdef CONFIG_NOT_COHERENT_CACHE | ||
1643 | struct device_node *np; | ||
1644 | const u32 *prop; | ||
1645 | #endif | ||
1646 | int ret; | ||
1647 | #ifndef MODULE | ||
1648 | char *option; | ||
1649 | |||
1650 | /* | ||
1651 | * For kernel boot options (in 'video=xxxfb:<options>' format) | ||
1652 | */ | ||
1653 | if (fb_get_options("fslfb", &option)) | ||
1654 | return -ENODEV; | ||
1655 | fsl_diu_setup(option); | ||
1656 | #endif | ||
1657 | printk(KERN_INFO "Freescale DIU driver\n"); | ||
1658 | |||
1659 | #ifdef CONFIG_NOT_COHERENT_CACHE | ||
1660 | np = of_find_node_by_type(NULL, "cpu"); | ||
1661 | if (!np) { | ||
1662 | printk(KERN_ERR "Err: can't find device node 'cpu'\n"); | ||
1663 | return -ENODEV; | ||
1664 | } | ||
1665 | |||
1666 | prop = of_get_property(np, "d-cache-size", NULL); | ||
1667 | if (prop == NULL) | ||
1668 | return -ENODEV; | ||
1669 | |||
1670 | /* Freescale PLRU requires 13/8 times the cache size to do a proper | ||
1671 | displacement flush | ||
1672 | */ | ||
1673 | coherence_data_size = *prop * 13; | ||
1674 | coherence_data_size /= 8; | ||
1675 | |||
1676 | prop = of_get_property(np, "d-cache-line-size", NULL); | ||
1677 | if (prop == NULL) | ||
1678 | return -ENODEV; | ||
1679 | d_cache_line_size = *prop; | ||
1680 | |||
1681 | of_node_put(np); | ||
1682 | coherence_data = vmalloc(coherence_data_size); | ||
1683 | if (!coherence_data) | ||
1684 | return -ENOMEM; | ||
1685 | #endif | ||
1686 | ret = of_register_platform_driver(&fsl_diu_driver); | ||
1687 | if (ret) { | ||
1688 | printk(KERN_ERR | ||
1689 | "fsl-diu: failed to register platform driver\n"); | ||
1690 | #if defined(CONFIG_NOT_COHERENT_CACHE) | ||
1691 | vfree(coherence_data); | ||
1692 | #endif | ||
1693 | iounmap(dr.diu_reg); | ||
1694 | } | ||
1695 | return ret; | ||
1696 | } | ||
1697 | |||
1698 | static void __exit fsl_diu_exit(void) | ||
1699 | { | ||
1700 | of_unregister_platform_driver(&fsl_diu_driver); | ||
1701 | #if defined(CONFIG_NOT_COHERENT_CACHE) | ||
1702 | vfree(coherence_data); | ||
1703 | #endif | ||
1704 | } | ||
1705 | |||
1706 | module_init(fsl_diu_init); | ||
1707 | module_exit(fsl_diu_exit); | ||
1708 | |||
1709 | MODULE_AUTHOR("York Sun <yorksun@freescale.com>"); | ||
1710 | MODULE_DESCRIPTION("Freescale DIU framebuffer driver"); | ||
1711 | MODULE_LICENSE("GPL"); | ||
1712 | |||
1713 | module_param_named(mode, fb_mode, charp, 0); | ||
1714 | MODULE_PARM_DESC(mode, | ||
1715 | "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); | ||
1716 | module_param_named(bpp, default_bpp, ulong, 0); | ||
1717 | MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified mode"); | ||
1718 | module_param_named(monitor, monitor_port, int, 0); | ||
1719 | MODULE_PARM_DESC(monitor, | ||
1720 | "Specify the monitor port (0, 1 or 2) if supported by the platform"); | ||
1721 | |||