aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Dooks <ben@fluff.org.uk>2007-02-20 16:58:21 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-20 20:10:16 -0500
commit5fc404e47bdf2d34ffc2edc16070cda410838291 (patch)
treef044aad04b7b622a123071d4c4c786780f76a94b
parent5b7e42b2d38e4c4d0cb105a2ad83d43f6957f59e (diff)
[PATCH] fb: SM501 framebuffer driver
Driver for the Silicon Motion SM501 multifunction device framebuffer subsystem. This driver supports both the CRT and LCD panel heads, with some simple acceleration for the cursor plotting and support for screen panning. There is no current support for bitblt/drawing engines, which should be added at a later date. This has been tested on a number of configurations, including PCI and generic-bus, on PPC, ARM and SH4 [akpm@linux-foundation.org: fix warnings] Signed-off-by: Ben Dooks <ben-linux@fluff.org> Signed-off-by: Vincent Sanders <vince@arm.linux.org.u.> Acked-by: James Simmons <jsimmons@infradead.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/video/Kconfig18
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/sm501fb.c1786
3 files changed, 1805 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index f8bc43c1e7a7..c1536d785551 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1573,6 +1573,24 @@ config FB_S3C2410_DEBUG
1573 Turn on debugging messages. Note that you can set/unset at run time 1573 Turn on debugging messages. Note that you can set/unset at run time
1574 through sysfs 1574 through sysfs
1575 1575
1576config FB_SM501
1577 tristate "Silicon Motion SM501 framebuffer support"
1578 depends on FB && MFD_SM501
1579 select FB_CFB_FILLRECT
1580 select FB_CFB_COPYAREA
1581 select FB_CFB_IMAGEBLIT
1582 ---help---
1583 Frame buffer driver for the CRT and LCD controllers in the Silicon
1584 Motion SM501.
1585
1586 This driver is also available as a module ( = code which can be
1587 inserted and removed from the running kernel whenever you want). The
1588 module will be called sm501fb. If you want to compile it as a module,
1589 say M here and read <file:Documentation/modules.txt>.
1590
1591 If unsure, say N.
1592
1593
1576config FB_PNX4008_DUM 1594config FB_PNX4008_DUM
1577 tristate "Display Update Module support on Philips PNX4008 board" 1595 tristate "Display Update Module support on Philips PNX4008 board"
1578 depends on FB && ARCH_PNX4008 1596 depends on FB && ARCH_PNX4008
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 1b79a6f13f0c..760305c8a841 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -98,6 +98,7 @@ obj-$(CONFIG_FB_PNX4008_DUM) += pnx4008/
98obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/ 98obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/
99obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o 99obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
100obj-$(CONFIG_FB_PS3) += ps3fb.o 100obj-$(CONFIG_FB_PS3) += ps3fb.o
101obj-$(CONFIG_FB_SM501) += sm501fb.o
101 102
102# Platform or fallback drivers go here 103# Platform or fallback drivers go here
103obj-$(CONFIG_FB_VESA) += vesafb.o 104obj-$(CONFIG_FB_VESA) += vesafb.o
diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c
new file mode 100644
index 000000000000..02b290ca01e1
--- /dev/null
+++ b/drivers/video/sm501fb.c
@@ -0,0 +1,1786 @@
1/* linux/drivers/video/sm501fb.c
2 *
3 * Copyright (c) 2006 Simtec Electronics
4 * Vincent Sanders <vince@simtec.co.uk>
5 * Ben Dooks <ben@simtec.co.uk>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Framebuffer driver for the Silicon Motion SM501
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/errno.h>
17#include <linux/string.h>
18#include <linux/mm.h>
19#include <linux/tty.h>
20#include <linux/slab.h>
21#include <linux/delay.h>
22#include <linux/fb.h>
23#include <linux/init.h>
24#include <linux/vmalloc.h>
25#include <linux/dma-mapping.h>
26#include <linux/interrupt.h>
27#include <linux/workqueue.h>
28#include <linux/wait.h>
29#include <linux/platform_device.h>
30#include <linux/clk.h>
31
32#include <asm/io.h>
33#include <asm/uaccess.h>
34#include <asm/div64.h>
35
36#ifdef CONFIG_PM
37#include <linux/pm.h>
38#endif
39
40#include <linux/sm501.h>
41#include <linux/sm501-regs.h>
42
43#define NR_PALETTE 256
44
45enum sm501_controller {
46 HEAD_CRT = 0,
47 HEAD_PANEL = 1,
48};
49
50/* SM501 memory adress */
51struct sm501_mem {
52 unsigned long size;
53 unsigned long sm_addr;
54 void __iomem *k_addr;
55};
56
57/* private data that is shared between all frambuffers* */
58struct sm501fb_info {
59 struct device *dev;
60 struct fb_info *fb[2]; /* fb info for both heads */
61 struct resource *fbmem_res; /* framebuffer resource */
62 struct resource *regs_res; /* registers resource */
63 struct sm501_platdata_fb *pdata; /* our platform data */
64
65 int irq;
66 int swap_endian; /* set to swap rgb=>bgr */
67 void __iomem *regs; /* remapped registers */
68 void __iomem *fbmem; /* remapped framebuffer */
69 size_t fbmem_len; /* length of remapped region */
70};
71
72/* per-framebuffer private data */
73struct sm501fb_par {
74 u32 pseudo_palette[16];
75
76 enum sm501_controller head;
77 struct sm501_mem cursor;
78 struct sm501_mem screen;
79 struct fb_ops ops;
80
81 void *store_fb;
82 void *store_cursor;
83 void __iomem *cursor_regs;
84 struct sm501fb_info *info;
85};
86
87/* Helper functions */
88
89static inline int h_total(struct fb_var_screeninfo *var)
90{
91 return var->xres + var->left_margin +
92 var->right_margin + var->hsync_len;
93}
94
95static inline int v_total(struct fb_var_screeninfo *var)
96{
97 return var->yres + var->upper_margin +
98 var->lower_margin + var->vsync_len;
99}
100
101/* sm501fb_sync_regs()
102 *
103 * This call is mainly for PCI bus systems where we need to
104 * ensure that any writes to the bus are completed before the
105 * next phase, or after completing a function.
106*/
107
108static inline void sm501fb_sync_regs(struct sm501fb_info *info)
109{
110 readl(info->regs);
111}
112
113/* sm501_alloc_mem
114 *
115 * This is an attempt to lay out memory for the two framebuffers and
116 * everything else
117 *
118 * |fbmem_res->start fbmem_res->end|
119 * | |
120 * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K |
121 * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-|
122 *
123 * The "spare" space is for the 2d engine data
124 * the fixed is space for the cursors (2x1Kbyte)
125 *
126 * we need to allocate memory for the 2D acceleration engine
127 * command list and the data for the engine to deal with.
128 *
129 * - all allocations must be 128bit aligned
130 * - cursors are 64x64x2 bits (1Kbyte)
131 *
132 */
133
134#define SM501_MEMF_CURSOR (1)
135#define SM501_MEMF_PANEL (2)
136#define SM501_MEMF_CRT (4)
137#define SM501_MEMF_ACCEL (8)
138
139int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem,
140 unsigned int why, size_t size)
141{
142 unsigned int ptr = 0;
143
144 switch (why) {
145 case SM501_MEMF_CURSOR:
146 ptr = inf->fbmem_len - size;
147 inf->fbmem_len = ptr;
148 break;
149
150 case SM501_MEMF_PANEL:
151 ptr = inf->fbmem_len - size;
152 if (ptr < inf->fb[0]->fix.smem_len)
153 return -ENOMEM;
154
155 break;
156
157 case SM501_MEMF_CRT:
158 ptr = 0;
159 break;
160
161 case SM501_MEMF_ACCEL:
162 ptr = inf->fb[0]->fix.smem_len;
163
164 if ((ptr + size) >
165 (inf->fb[1]->fix.smem_start - inf->fbmem_res->start))
166 return -ENOMEM;
167 break;
168
169 default:
170 return -EINVAL;
171 }
172
173 mem->size = size;
174 mem->sm_addr = ptr;
175 mem->k_addr = inf->fbmem + ptr;
176
177 dev_dbg(inf->dev, "%s: result %08lx, %p - %u, %zd\n",
178 __func__, mem->sm_addr, mem->k_addr, why, size);
179
180 return 0;
181}
182
183/* sm501fb_ps_to_hz
184 *
185 * Converts a period in picoseconds to Hz.
186 *
187 * Note, we try to keep this in Hz to minimise rounding with
188 * the limited PLL settings on the SM501.
189*/
190
191static unsigned long sm501fb_ps_to_hz(unsigned long psvalue)
192{
193 unsigned long long numerator=1000000000000ULL;
194
195 /* 10^12 / picosecond period gives frequency in Hz */
196 do_div(numerator, psvalue);
197 return (unsigned long)numerator;
198}
199
200/* sm501fb_hz_to_ps is identical to the oposite transform */
201
202#define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x)
203
204/* sm501fb_setup_gamma
205 *
206 * Programs a linear 1.0 gamma ramp in case the gamma
207 * correction is enabled without programming anything else.
208*/
209
210static void sm501fb_setup_gamma(struct sm501fb_info *fbi,
211 unsigned long palette)
212{
213 unsigned long value = 0;
214 int offset;
215
216 /* set gamma values */
217 for (offset = 0; offset < 256 * 4; offset += 4) {
218 writel(value, fbi->regs + palette + offset);
219 value += 0x010101; /* Advance RGB by 1,1,1.*/
220 }
221}
222
223/* sm501fb_check_var
224 *
225 * check common variables for both panel and crt
226*/
227
228static int sm501fb_check_var(struct fb_var_screeninfo *var,
229 struct fb_info *info)
230{
231 struct sm501fb_par *par = info->par;
232 struct sm501fb_info *sm = par->info;
233 unsigned long tmp;
234
235 /* check we can fit these values into the registers */
236
237 if (var->hsync_len > 255 || var->vsync_len > 255)
238 return -EINVAL;
239
240 if ((var->xres + var->right_margin) >= 4096)
241 return -EINVAL;
242
243 if ((var->yres + var->lower_margin) > 2048)
244 return -EINVAL;
245
246 /* hard limits of device */
247
248 if (h_total(var) > 4096 || v_total(var) > 2048)
249 return -EINVAL;
250
251 /* check our line length is going to be 128 bit aligned */
252
253 tmp = (var->xres * var->bits_per_pixel) / 8;
254 if ((tmp & 15) != 0)
255 return -EINVAL;
256
257 /* check the virtual size */
258
259 if (var->xres_virtual > 4096 || var->yres_virtual > 2048)
260 return -EINVAL;
261
262 /* can cope with 8,16 or 32bpp */
263
264 if (var->bits_per_pixel <= 8)
265 var->bits_per_pixel = 8;
266 else if (var->bits_per_pixel <= 16)
267 var->bits_per_pixel = 16;
268 else if (var->bits_per_pixel == 24)
269 var->bits_per_pixel = 32;
270
271 /* set r/g/b positions and validate bpp */
272 switch(var->bits_per_pixel) {
273 case 8:
274 var->red.length = var->bits_per_pixel;
275 var->red.offset = 0;
276 var->green.length = var->bits_per_pixel;
277 var->green.offset = 0;
278 var->blue.length = var->bits_per_pixel;
279 var->blue.offset = 0;
280 var->transp.length = 0;
281
282 break;
283
284 case 16:
285 if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) {
286 var->red.offset = 11;
287 var->green.offset = 5;
288 var->blue.offset = 0;
289 } else {
290 var->blue.offset = 11;
291 var->green.offset = 5;
292 var->red.offset = 0;
293 }
294
295 var->red.length = 5;
296 var->green.length = 6;
297 var->blue.length = 5;
298 var->transp.length = 0;
299 break;
300
301 case 32:
302 if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) {
303 var->transp.offset = 0;
304 var->red.offset = 8;
305 var->green.offset = 16;
306 var->blue.offset = 24;
307 } else {
308 var->transp.offset = 24;
309 var->red.offset = 16;
310 var->green.offset = 8;
311 var->blue.offset = 0;
312 }
313
314 var->red.length = 8;
315 var->green.length = 8;
316 var->blue.length = 8;
317 var->transp.length = 0;
318 break;
319
320 default:
321 return -EINVAL;
322 }
323
324 return 0;
325}
326
327/*
328 * sm501fb_check_var_crt():
329 *
330 * check the parameters for the CRT head, and either bring them
331 * back into range, or return -EINVAL.
332*/
333
334static int sm501fb_check_var_crt(struct fb_var_screeninfo *var,
335 struct fb_info *info)
336{
337 return sm501fb_check_var(var, info);
338}
339
340/* sm501fb_check_var_pnl():
341 *
342 * check the parameters for the CRT head, and either bring them
343 * back into range, or return -EINVAL.
344*/
345
346static int sm501fb_check_var_pnl(struct fb_var_screeninfo *var,
347 struct fb_info *info)
348{
349 return sm501fb_check_var(var, info);
350}
351
352/* sm501fb_set_par_common
353 *
354 * set common registers for framebuffers
355*/
356
357static int sm501fb_set_par_common(struct fb_info *info,
358 struct fb_var_screeninfo *var)
359{
360 struct sm501fb_par *par = info->par;
361 struct sm501fb_info *fbi = par->info;
362 unsigned long pixclock; /* pixelclock in Hz */
363 unsigned long sm501pixclock; /* pixelclock the 501 can achive in Hz */
364 unsigned int mem_type;
365 unsigned int clock_type;
366 unsigned int head_addr;
367
368 dev_dbg(fbi->dev, "%s: %dx%d, bpp = %d, virtual %dx%d\n",
369 __func__, var->xres, var->yres, var->bits_per_pixel,
370 var->xres_virtual, var->yres_virtual);
371
372 switch (par->head) {
373 case HEAD_CRT:
374 mem_type = SM501_MEMF_CRT;
375 clock_type = SM501_CLOCK_V2XCLK;
376 head_addr = SM501_DC_CRT_FB_ADDR;
377 break;
378
379 case HEAD_PANEL:
380 mem_type = SM501_MEMF_PANEL;
381 clock_type = SM501_CLOCK_P2XCLK;
382 head_addr = SM501_DC_PANEL_FB_ADDR;
383 break;
384
385 default:
386 mem_type = 0; /* stop compiler warnings */
387 head_addr = 0;
388 clock_type = 0;
389 }
390
391 switch (var->bits_per_pixel) {
392 case 8:
393 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
394 break;
395
396 case 16:
397 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
398 break;
399
400 case 32:
401 info->fix.visual = FB_VISUAL_TRUECOLOR;
402 break;
403 }
404
405 /* allocate fb memory within 501 */
406 info->fix.line_length = (var->xres_virtual * var->bits_per_pixel)/8;
407 info->fix.smem_len = info->fix.line_length * var->yres_virtual;
408
409 dev_dbg(fbi->dev, "%s: line length = %u\n", __func__,
410 info->fix.line_length);
411
412 if (sm501_alloc_mem(fbi, &par->screen, mem_type,
413 info->fix.smem_len)) {
414 dev_err(fbi->dev, "no memory available\n");
415 return -ENOMEM;
416 }
417
418 info->fix.smem_start = fbi->fbmem_res->start + par->screen.sm_addr;
419
420 info->screen_base = fbi->fbmem + par->screen.sm_addr;
421 info->screen_size = info->fix.smem_len;
422
423 /* set start of framebuffer to the screen */
424
425 writel(par->screen.sm_addr | SM501_ADDR_FLIP, fbi->regs + head_addr);
426
427 /* program CRT clock */
428
429 pixclock = sm501fb_ps_to_hz(var->pixclock);
430
431 sm501pixclock = sm501_set_clock(fbi->dev->parent, clock_type,
432 pixclock);
433
434 /* update fb layer with actual clock used */
435 var->pixclock = sm501fb_hz_to_ps(sm501pixclock);
436
437 dev_dbg(fbi->dev, "%s: pixclock(ps) = %u, pixclock(Hz) = %lu, "
438 "sm501pixclock = %lu, error = %ld%%\n",
439 __func__, var->pixclock, pixclock, sm501pixclock,
440 ((pixclock - sm501pixclock)*100)/pixclock);
441
442 return 0;
443}
444
445/* sm501fb_set_par_geometry
446 *
447 * set the geometry registers for specified framebuffer.
448*/
449
450static void sm501fb_set_par_geometry(struct fb_info *info,
451 struct fb_var_screeninfo *var)
452{
453 struct sm501fb_par *par = info->par;
454 struct sm501fb_info *fbi = par->info;
455 void __iomem *base = fbi->regs;
456 unsigned long reg;
457
458 if (par->head == HEAD_CRT)
459 base += SM501_DC_CRT_H_TOT;
460 else
461 base += SM501_DC_PANEL_H_TOT;
462
463 /* set framebuffer width and display width */
464
465 reg = info->fix.line_length;
466 reg |= ((var->xres * var->bits_per_pixel)/8) << 16;
467
468 writel(reg, fbi->regs + (par->head == HEAD_CRT ?
469 SM501_DC_CRT_FB_OFFSET : SM501_DC_PANEL_FB_OFFSET));
470
471 /* program horizontal total */
472
473 reg = (h_total(var) - 1) << 16;
474 reg |= (var->xres - 1);
475
476 writel(reg, base + SM501_OFF_DC_H_TOT);
477
478 /* program horizontal sync */
479
480 reg = var->hsync_len << 16;
481 reg |= var->xres + var->right_margin - 1;
482
483 writel(reg, base + SM501_OFF_DC_H_SYNC);
484
485 /* program vertical total */
486
487 reg = (v_total(var) - 1) << 16;
488 reg |= (var->yres - 1);
489
490 writel(reg, base + SM501_OFF_DC_V_TOT);
491
492 /* program vertical sync */
493 reg = var->vsync_len << 16;
494 reg |= var->yres + var->lower_margin - 1;
495
496 writel(reg, base + SM501_OFF_DC_V_SYNC);
497}
498
499/* sm501fb_pan_crt
500 *
501 * pan the CRT display output within an virtual framebuffer
502*/
503
504static int sm501fb_pan_crt(struct fb_var_screeninfo *var,
505 struct fb_info *info)
506{
507 struct sm501fb_par *par = info->par;
508 struct sm501fb_info *fbi = par->info;
509 unsigned int bytes_pixel = var->bits_per_pixel / 8;
510 unsigned long reg;
511 unsigned long xoffs;
512
513 xoffs = var->xoffset * bytes_pixel;
514
515 reg = readl(fbi->regs + SM501_DC_CRT_CONTROL);
516
517 reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK;
518 reg |= ((xoffs & 15) / bytes_pixel) << 4;
519 writel(reg, fbi->regs + SM501_DC_CRT_CONTROL);
520
521 reg = (par->screen.sm_addr + xoffs +
522 var->yoffset * info->fix.line_length);
523 writel(reg | SM501_ADDR_FLIP, fbi->regs + SM501_DC_CRT_FB_ADDR);
524
525 sm501fb_sync_regs(fbi);
526 return 0;
527}
528
529/* sm501fb_pan_pnl
530 *
531 * pan the panel display output within an virtual framebuffer
532*/
533
534static int sm501fb_pan_pnl(struct fb_var_screeninfo *var,
535 struct fb_info *info)
536{
537 struct sm501fb_par *par = info->par;
538 struct sm501fb_info *fbi = par->info;
539 unsigned long reg;
540
541 reg = var->xoffset | (var->xres_virtual << 16);
542 writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH);
543
544 reg = var->yoffset | (var->yres_virtual << 16);
545 writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT);
546
547 sm501fb_sync_regs(fbi);
548 return 0;
549}
550
551/* sm501fb_set_par_crt
552 *
553 * Set the CRT video mode from the fb_info structure
554*/
555
556static int sm501fb_set_par_crt(struct fb_info *info)
557{
558 struct sm501fb_par *par = info->par;
559 struct sm501fb_info *fbi = par->info;
560 struct fb_var_screeninfo *var = &info->var;
561 unsigned long control; /* control register */
562 int ret;
563
564 /* activate new configuration */
565
566 dev_dbg(fbi->dev, "%s(%p)\n", __func__, info);
567
568 /* enable CRT DAC - note 0 is on!*/
569 sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER);
570
571 control = readl(fbi->regs + SM501_DC_CRT_CONTROL);
572
573 control &= (SM501_DC_CRT_CONTROL_PIXEL_MASK |
574 SM501_DC_CRT_CONTROL_GAMMA |
575 SM501_DC_CRT_CONTROL_BLANK |
576 SM501_DC_CRT_CONTROL_SEL |
577 SM501_DC_CRT_CONTROL_CP |
578 SM501_DC_CRT_CONTROL_TVP);
579
580 /* set the sync polarities before we check data source */
581
582 if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
583 control |= SM501_DC_CRT_CONTROL_HSP;
584
585 if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
586 control |= SM501_DC_CRT_CONTROL_VSP;
587
588 if ((control & SM501_DC_CRT_CONTROL_SEL) == 0) {
589 /* the head is displaying panel data... */
590
591 sm501_alloc_mem(fbi, &par->screen, SM501_MEMF_CRT, 0);
592 goto out_update;
593 }
594
595 ret = sm501fb_set_par_common(info, var);
596 if (ret) {
597 dev_err(fbi->dev, "failed to set common parameters\n");
598 return ret;
599 }
600
601 sm501fb_pan_crt(var, info);
602 sm501fb_set_par_geometry(info, var);
603
604 control |= SM501_FIFO_3; /* fill if >3 free slots */
605
606 switch(var->bits_per_pixel) {
607 case 8:
608 control |= SM501_DC_CRT_CONTROL_8BPP;
609 break;
610
611 case 16:
612 control |= SM501_DC_CRT_CONTROL_16BPP;
613 break;
614
615 case 32:
616 control |= SM501_DC_CRT_CONTROL_32BPP;
617 sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE);
618 break;
619
620 default:
621 BUG();
622 }
623
624 control |= SM501_DC_CRT_CONTROL_SEL; /* CRT displays CRT data */
625 control |= SM501_DC_CRT_CONTROL_TE; /* enable CRT timing */
626 control |= SM501_DC_CRT_CONTROL_ENABLE; /* enable CRT plane */
627
628 out_update:
629 dev_dbg(fbi->dev, "new control is %08lx\n", control);
630
631 writel(control, fbi->regs + SM501_DC_CRT_CONTROL);
632 sm501fb_sync_regs(fbi);
633
634 return 0;
635}
636
637static void sm501fb_panel_power(struct sm501fb_info *fbi, int to)
638{
639 unsigned long control;
640 void __iomem *ctrl_reg = fbi->regs + SM501_DC_PANEL_CONTROL;
641
642 control = readl(ctrl_reg);
643
644 if (to && (control & SM501_DC_PANEL_CONTROL_VDD) == 0) {
645 /* enable panel power */
646
647 control |= SM501_DC_PANEL_CONTROL_VDD; /* FPVDDEN */
648 writel(control, ctrl_reg);
649 sm501fb_sync_regs(fbi);
650 mdelay(10);
651
652 control |= SM501_DC_PANEL_CONTROL_DATA; /* DATA */
653 writel(control, ctrl_reg);
654 sm501fb_sync_regs(fbi);
655 mdelay(10);
656
657 control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */
658 writel(control, ctrl_reg);
659 sm501fb_sync_regs(fbi);
660 mdelay(10);
661
662 control |= SM501_DC_PANEL_CONTROL_FPEN;
663 writel(control, ctrl_reg);
664
665 } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) {
666 /* disable panel power */
667
668 control &= ~SM501_DC_PANEL_CONTROL_FPEN;
669 writel(control, ctrl_reg);
670 sm501fb_sync_regs(fbi);
671 mdelay(10);
672
673 control &= ~SM501_DC_PANEL_CONTROL_BIAS;
674 writel(control, ctrl_reg);
675 sm501fb_sync_regs(fbi);
676 mdelay(10);
677
678 control &= ~SM501_DC_PANEL_CONTROL_DATA;
679 writel(control, ctrl_reg);
680 sm501fb_sync_regs(fbi);
681 mdelay(10);
682
683 control &= ~SM501_DC_PANEL_CONTROL_VDD;
684 writel(control, ctrl_reg);
685 sm501fb_sync_regs(fbi);
686 mdelay(10);
687 }
688
689 sm501fb_sync_regs(fbi);
690}
691
692/* sm501fb_set_par_pnl
693 *
694 * Set the panel video mode from the fb_info structure
695*/
696
697static int sm501fb_set_par_pnl(struct fb_info *info)
698{
699 struct sm501fb_par *par = info->par;
700 struct sm501fb_info *fbi = par->info;
701 struct fb_var_screeninfo *var = &info->var;
702 unsigned long control;
703 unsigned long reg;
704 int ret;
705
706 dev_dbg(fbi->dev, "%s(%p)\n", __func__, info);
707
708 /* activate this new configuration */
709
710 ret = sm501fb_set_par_common(info, var);
711 if (ret)
712 return ret;
713
714 sm501fb_pan_pnl(var, info);
715 sm501fb_set_par_geometry(info, var);
716
717 /* update control register */
718
719 control = readl(fbi->regs + SM501_DC_PANEL_CONTROL);
720 control &= (SM501_DC_PANEL_CONTROL_GAMMA |
721 SM501_DC_PANEL_CONTROL_VDD |
722 SM501_DC_PANEL_CONTROL_DATA |
723 SM501_DC_PANEL_CONTROL_BIAS |
724 SM501_DC_PANEL_CONTROL_FPEN |
725 SM501_DC_PANEL_CONTROL_CP |
726 SM501_DC_PANEL_CONTROL_CK |
727 SM501_DC_PANEL_CONTROL_HP |
728 SM501_DC_PANEL_CONTROL_VP |
729 SM501_DC_PANEL_CONTROL_HPD |
730 SM501_DC_PANEL_CONTROL_VPD);
731
732 control |= SM501_FIFO_3; /* fill if >3 free slots */
733
734 switch(var->bits_per_pixel) {
735 case 8:
736 control |= SM501_DC_PANEL_CONTROL_8BPP;
737 break;
738
739 case 16:
740 control |= SM501_DC_PANEL_CONTROL_16BPP;
741 break;
742
743 case 32:
744 control |= SM501_DC_PANEL_CONTROL_32BPP;
745 sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE);
746 break;
747
748 default:
749 BUG();
750 }
751
752 writel(0x0, fbi->regs + SM501_DC_PANEL_PANNING_CONTROL);
753
754 /* panel plane top left and bottom right location */
755
756 writel(0x00, fbi->regs + SM501_DC_PANEL_TL_LOC);
757
758 reg = var->xres - 1;
759 reg |= (var->yres - 1) << 16;
760
761 writel(reg, fbi->regs + SM501_DC_PANEL_BR_LOC);
762
763 /* program panel control register */
764
765 control |= SM501_DC_PANEL_CONTROL_TE; /* enable PANEL timing */
766 control |= SM501_DC_PANEL_CONTROL_EN; /* enable PANEL gfx plane */
767
768 if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
769 control |= SM501_DC_PANEL_CONTROL_HSP;
770
771 if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
772 control |= SM501_DC_PANEL_CONTROL_VSP;
773
774 writel(control, fbi->regs + SM501_DC_PANEL_CONTROL);
775 sm501fb_sync_regs(fbi);
776
777 /* power the panel up */
778 sm501fb_panel_power(fbi, 1);
779 return 0;
780}
781
782
783/* chan_to_field
784 *
785 * convert a colour value into a field position
786 *
787 * from pxafb.c
788*/
789
790static inline unsigned int chan_to_field(unsigned int chan,
791 struct fb_bitfield *bf)
792{
793 chan &= 0xffff;
794 chan >>= 16 - bf->length;
795 return chan << bf->offset;
796}
797
798/* sm501fb_setcolreg
799 *
800 * set the colour mapping for modes that support palettised data
801*/
802
803static int sm501fb_setcolreg(unsigned regno,
804 unsigned red, unsigned green, unsigned blue,
805 unsigned transp, struct fb_info *info)
806{
807 struct sm501fb_par *par = info->par;
808 struct sm501fb_info *fbi = par->info;
809 void __iomem *base = fbi->regs;
810 unsigned int val;
811
812 if (par->head == HEAD_CRT)
813 base += SM501_DC_CRT_PALETTE;
814 else
815 base += SM501_DC_PANEL_PALETTE;
816
817 switch (info->fix.visual) {
818 case FB_VISUAL_TRUECOLOR:
819 /* true-colour, use pseuo-palette */
820
821 if (regno < 16) {
822 u32 *pal = par->pseudo_palette;
823
824 val = chan_to_field(red, &info->var.red);
825 val |= chan_to_field(green, &info->var.green);
826 val |= chan_to_field(blue, &info->var.blue);
827
828 pal[regno] = val;
829 }
830 break;
831
832 case FB_VISUAL_PSEUDOCOLOR:
833 if (regno < 256) {
834 val = (red >> 8) << 16;
835 val |= (green >> 8) << 8;
836 val |= blue >> 8;
837
838 writel(val, base + (regno * 4));
839 }
840
841 break;
842
843 default:
844 return 1; /* unknown type */
845 }
846
847 return 0;
848}
849
850/* sm501fb_blank_pnl
851 *
852 * Blank or un-blank the panel interface
853*/
854
855static int sm501fb_blank_pnl(int blank_mode, struct fb_info *info)
856{
857 struct sm501fb_par *par = info->par;
858 struct sm501fb_info *fbi = par->info;
859
860 dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info);
861
862 switch (blank_mode) {
863 case FB_BLANK_POWERDOWN:
864 sm501fb_panel_power(fbi, 0);
865 break;
866
867 case FB_BLANK_UNBLANK:
868 sm501fb_panel_power(fbi, 1);
869 break;
870
871 case FB_BLANK_NORMAL:
872 case FB_BLANK_VSYNC_SUSPEND:
873 case FB_BLANK_HSYNC_SUSPEND:
874 default:
875 return 1;
876 }
877
878 return 0;
879}
880
881/* sm501fb_blank_crt
882 *
883 * Blank or un-blank the crt interface
884*/
885
886static int sm501fb_blank_crt(int blank_mode, struct fb_info *info)
887{
888 struct sm501fb_par *par = info->par;
889 struct sm501fb_info *fbi = par->info;
890 unsigned long ctrl;
891
892 dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info);
893
894 ctrl = readl(fbi->regs + SM501_DC_CRT_CONTROL);
895
896 switch (blank_mode) {
897 case FB_BLANK_POWERDOWN:
898 ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE;
899 sm501_misc_control(fbi->dev->parent, SM501_MISC_DAC_POWER, 0);
900
901 case FB_BLANK_NORMAL:
902 ctrl |= SM501_DC_CRT_CONTROL_BLANK;
903 break;
904
905 case FB_BLANK_UNBLANK:
906 ctrl &= ~SM501_DC_CRT_CONTROL_BLANK;
907 ctrl |= SM501_DC_CRT_CONTROL_ENABLE;
908 sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER);
909 break;
910
911 case FB_BLANK_VSYNC_SUSPEND:
912 case FB_BLANK_HSYNC_SUSPEND:
913 default:
914 return 1;
915
916 }
917
918 writel(ctrl, fbi->regs + SM501_DC_CRT_CONTROL);
919 sm501fb_sync_regs(fbi);
920
921 return 0;
922}
923
924/* sm501fb_cursor
925 *
926 * set or change the hardware cursor parameters
927*/
928
929int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
930{
931 struct sm501fb_par *par = info->par;
932 struct sm501fb_info *fbi = par->info;
933 void __iomem *base = fbi->regs;
934 unsigned long hwc_addr;
935 unsigned long fg, bg;
936
937 dev_dbg(fbi->dev, "%s(%p,%p)\n", __func__, info, cursor);
938
939 if (par->head == HEAD_CRT)
940 base += SM501_DC_CRT_HWC_BASE;
941 else
942 base += SM501_DC_PANEL_HWC_BASE;
943
944 /* check not being asked to exceed capabilities */
945
946 if (cursor->image.width > 64)
947 return -EINVAL;
948
949 if (cursor->image.height > 64)
950 return -EINVAL;
951
952 if (cursor->image.depth > 1)
953 return -EINVAL;
954
955 hwc_addr = readl(base + SM501_OFF_HWC_ADDR);
956
957 if (cursor->enable)
958 writel(hwc_addr | SM501_HWC_EN, base + SM501_OFF_HWC_ADDR);
959 else
960 writel(hwc_addr & ~SM501_HWC_EN, base + SM501_OFF_HWC_ADDR);
961
962 /* set data */
963 if (cursor->set & FB_CUR_SETPOS) {
964 unsigned int x = cursor->image.dx;
965 unsigned int y = cursor->image.dy;
966
967 if (x >= 2048 || y >= 2048 )
968 return -EINVAL;
969
970 dev_dbg(fbi->dev, "set position %d,%d\n", x, y);
971
972 //y += cursor->image.height;
973
974 writel(x | (y << 16), base + SM501_OFF_HWC_LOC);
975 }
976
977 if (cursor->set & FB_CUR_SETCMAP) {
978 unsigned int bg_col = cursor->image.bg_color;
979 unsigned int fg_col = cursor->image.fg_color;
980
981 dev_dbg(fbi->dev, "%s: update cmap (%08x,%08x)\n",
982 __func__, bg_col, fg_col);
983
984 bg = ((info->cmap.red[bg_col] & 0xF8) << 8) |
985 ((info->cmap.green[bg_col] & 0xFC) << 3) |
986 ((info->cmap.blue[bg_col] & 0xF8) >> 3);
987
988 fg = ((info->cmap.red[fg_col] & 0xF8) << 8) |
989 ((info->cmap.green[fg_col] & 0xFC) << 3) |
990 ((info->cmap.blue[fg_col] & 0xF8) >> 3);
991
992 dev_dbg(fbi->dev, "fgcol %08x, bgcol %08x\n", fg, bg);
993
994 writel(bg, base + SM501_OFF_HWC_COLOR_1_2);
995 writel(fg, base + SM501_OFF_HWC_COLOR_3);
996 }
997
998 if (cursor->set & FB_CUR_SETSIZE ||
999 cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) {
1000 /* SM501 cursor is a two bpp 64x64 bitmap this routine
1001 * clears it to transparent then combines the cursor
1002 * shape plane with the colour plane to set the
1003 * cursor */
1004 int x, y;
1005 const unsigned char *pcol = cursor->image.data;
1006 const unsigned char *pmsk = cursor->mask;
1007 void __iomem *dst = par->cursor.k_addr;
1008 unsigned char dcol = 0;
1009 unsigned char dmsk = 0;
1010 unsigned int op;
1011
1012 dev_dbg(fbi->dev, "%s: setting shape (%d,%d)\n",
1013 __func__, cursor->image.width, cursor->image.height);
1014
1015 for (op = 0; op < (64*64*2)/8; op+=4)
1016 writel(0x0, dst + op);
1017
1018 for (y = 0; y < cursor->image.height; y++) {
1019 for (x = 0; x < cursor->image.width; x++) {
1020 if ((x % 8) == 0) {
1021 dcol = *pcol++;
1022 dmsk = *pmsk++;
1023 } else {
1024 dcol >>= 1;
1025 dmsk >>= 1;
1026 }
1027
1028 if (dmsk & 1) {
1029 op = (dcol & 1) ? 1 : 3;
1030 op <<= ((x % 4) * 2);
1031
1032 op |= readb(dst + (x / 4));
1033 writeb(op, dst + (x / 4));
1034 }
1035 }
1036 dst += (64*2)/8;
1037 }
1038 }
1039
1040 sm501fb_sync_regs(fbi); /* ensure cursor data flushed */
1041 return 0;
1042}
1043
1044/* sm501fb_crtsrc_show
1045 *
1046 * device attribute code to show where the crt output is sourced from
1047*/
1048
1049static ssize_t sm501fb_crtsrc_show(struct device *dev,
1050 struct device_attribute *attr, char *buf)
1051{
1052 struct sm501fb_info *info = dev_get_drvdata(dev);
1053 unsigned long ctrl;
1054
1055 ctrl = readl(info->regs + SM501_DC_CRT_CONTROL);
1056 ctrl &= SM501_DC_CRT_CONTROL_SEL;
1057
1058 return snprintf(buf, PAGE_SIZE, "%s\n", ctrl ? "crt" : "panel");
1059}
1060
1061/* sm501fb_crtsrc_show
1062 *
1063 * device attribute code to set where the crt output is sourced from
1064*/
1065
1066static ssize_t sm501fb_crtsrc_store(struct device *dev,
1067 struct device_attribute *attr,
1068 const char *buf, size_t len)
1069{
1070 struct sm501fb_info *info = dev_get_drvdata(dev);
1071 enum sm501_controller head;
1072 unsigned long ctrl;
1073
1074 if (len < 1)
1075 return -EINVAL;
1076
1077 if (strnicmp(buf, "crt", sizeof("crt")) == 0)
1078 head = HEAD_CRT;
1079 else if (strnicmp(buf, "panel", sizeof("panel")) == 0)
1080 head = HEAD_PANEL;
1081 else
1082 return -EINVAL;
1083
1084 dev_info(dev, "setting crt source to head %d\n", head);
1085
1086 ctrl = readl(info->regs + SM501_DC_CRT_CONTROL);
1087
1088 if (head == HEAD_CRT) {
1089 ctrl |= SM501_DC_CRT_CONTROL_SEL;
1090 ctrl |= SM501_DC_CRT_CONTROL_ENABLE;
1091 ctrl |= SM501_DC_CRT_CONTROL_TE;
1092 } else {
1093 ctrl &= ~SM501_DC_CRT_CONTROL_SEL;
1094 ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE;
1095 ctrl &= ~SM501_DC_CRT_CONTROL_TE;
1096 }
1097
1098 writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
1099 sm501fb_sync_regs(info);
1100
1101 return (head == HEAD_CRT) ? 3 : 5;
1102}
1103
1104/* Prepare the device_attr for registration with sysfs later */
1105static DEVICE_ATTR(crt_src, 0666, sm501fb_crtsrc_show, sm501fb_crtsrc_store);
1106
1107/* sm501fb_show_regs
1108 *
1109 * show the primary sm501 registers
1110*/
1111static int sm501fb_show_regs(struct sm501fb_info *info, char *ptr,
1112 unsigned int start, unsigned int len)
1113{
1114 void __iomem *mem = info->regs;
1115 char *buf = ptr;
1116 unsigned int reg;
1117
1118 for (reg = start; reg < (len + start); reg += 4)
1119 ptr += sprintf(ptr, "%08x = %08x\n", reg, readl(mem + reg));
1120
1121 return ptr - buf;
1122}
1123
1124/* sm501fb_debug_show_crt
1125 *
1126 * show the crt control and cursor registers
1127*/
1128
1129static ssize_t sm501fb_debug_show_crt(struct device *dev,
1130 struct device_attribute *attr, char *buf)
1131{
1132 struct sm501fb_info *info = dev_get_drvdata(dev);
1133 char *ptr = buf;
1134
1135 ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_CONTROL, 0x40);
1136 ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_HWC_BASE, 0x10);
1137
1138 return ptr - buf;
1139}
1140
1141static DEVICE_ATTR(fbregs_crt, 0444, sm501fb_debug_show_crt, NULL);
1142
1143/* sm501fb_debug_show_pnl
1144 *
1145 * show the panel control and cursor registers
1146*/
1147
1148static ssize_t sm501fb_debug_show_pnl(struct device *dev,
1149 struct device_attribute *attr, char *buf)
1150{
1151 struct sm501fb_info *info = dev_get_drvdata(dev);
1152 char *ptr = buf;
1153
1154 ptr += sm501fb_show_regs(info, ptr, 0x0, 0x40);
1155 ptr += sm501fb_show_regs(info, ptr, SM501_DC_PANEL_HWC_BASE, 0x10);
1156
1157 return ptr - buf;
1158}
1159
1160static DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL);
1161
1162/* framebuffer ops */
1163
1164static struct fb_ops sm501fb_ops_crt = {
1165 .owner = THIS_MODULE,
1166 .fb_check_var = sm501fb_check_var_crt,
1167 .fb_set_par = sm501fb_set_par_crt,
1168 .fb_blank = sm501fb_blank_crt,
1169 .fb_setcolreg = sm501fb_setcolreg,
1170 .fb_pan_display = sm501fb_pan_crt,
1171 .fb_cursor = sm501fb_cursor,
1172 .fb_fillrect = cfb_fillrect,
1173 .fb_copyarea = cfb_copyarea,
1174 .fb_imageblit = cfb_imageblit,
1175};
1176
1177static struct fb_ops sm501fb_ops_pnl = {
1178 .owner = THIS_MODULE,
1179 .fb_check_var = sm501fb_check_var_pnl,
1180 .fb_set_par = sm501fb_set_par_pnl,
1181 .fb_pan_display = sm501fb_pan_pnl,
1182 .fb_blank = sm501fb_blank_pnl,
1183 .fb_setcolreg = sm501fb_setcolreg,
1184 .fb_cursor = sm501fb_cursor,
1185 .fb_fillrect = cfb_fillrect,
1186 .fb_copyarea = cfb_copyarea,
1187 .fb_imageblit = cfb_imageblit,
1188};
1189
1190/* sm501fb_info_alloc
1191 *
1192 * creates and initialises an sm501fb_info structure
1193*/
1194
1195static struct sm501fb_info *sm501fb_info_alloc(struct fb_info *fbinfo_crt,
1196 struct fb_info *fbinfo_pnl)
1197{
1198 struct sm501fb_info *info;
1199 struct sm501fb_par *par;
1200
1201 info = kzalloc(sizeof(struct sm501fb_info), GFP_KERNEL);
1202 if (info) {
1203 /* set the references back */
1204
1205 par = fbinfo_crt->par;
1206 par->info = info;
1207 par->head = HEAD_CRT;
1208 fbinfo_crt->pseudo_palette = &par->pseudo_palette;
1209
1210 par = fbinfo_pnl->par;
1211 par->info = info;
1212 par->head = HEAD_PANEL;
1213 fbinfo_pnl->pseudo_palette = &par->pseudo_palette;
1214
1215 /* store the two fbs into our info */
1216 info->fb[HEAD_CRT] = fbinfo_crt;
1217 info->fb[HEAD_PANEL] = fbinfo_pnl;
1218 }
1219
1220 return info;
1221}
1222
1223/* sm501_init_cursor
1224 *
1225 * initialise hw cursor parameters
1226*/
1227
1228int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base)
1229{
1230 struct sm501fb_par *par = fbi->par;
1231 struct sm501fb_info *info = par->info;
1232 int ret;
1233
1234 par->cursor_regs = info->regs + reg_base;
1235
1236 ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024);
1237 if (ret < 0)
1238 return ret;
1239
1240 /* initialise the colour registers */
1241
1242 writel(par->cursor.sm_addr, par->cursor_regs + SM501_OFF_HWC_ADDR);
1243
1244 writel(0x00, par->cursor_regs + SM501_OFF_HWC_LOC);
1245 writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_1_2);
1246 writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_3);
1247 sm501fb_sync_regs(info);
1248
1249 return 0;
1250}
1251
1252/* sm501fb_info_start
1253 *
1254 * fills the par structure claiming resources and remapping etc.
1255*/
1256
1257static int sm501fb_start(struct sm501fb_info *info,
1258 struct platform_device *pdev)
1259{
1260 struct resource *res;
1261 struct device *dev;
1262 int ret;
1263
1264 info->dev = dev = &pdev->dev;
1265 platform_set_drvdata(pdev, info);
1266
1267 info->irq = ret = platform_get_irq(pdev, 0);
1268 if (ret < 0) {
1269 /* we currently do not use the IRQ */
1270 dev_warn(dev, "no irq for device\n");
1271 }
1272
1273 /* allocate, reserve and remap resources for registers */
1274 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1275 if (res == NULL) {
1276 dev_err(dev, "no resource definition for registers\n");
1277 ret = -ENOENT;
1278 goto err_release;
1279 }
1280
1281 info->regs_res = request_mem_region(res->start,
1282 res->end - res->start,
1283 pdev->name);
1284
1285 if (info->regs_res == NULL) {
1286 dev_err(dev, "cannot claim registers\n");
1287 ret = -ENXIO;
1288 goto err_release;
1289 }
1290
1291 info->regs = ioremap(res->start, (res->end - res->start)+1);
1292 if (info->regs == NULL) {
1293 dev_err(dev, "cannot remap registers\n");
1294 ret = -ENXIO;
1295 goto err_regs_res;
1296 }
1297
1298 /* allocate, reserve resources for framebuffer */
1299 res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
1300 if (res == NULL) {
1301 dev_err(dev, "no memory resource defined\n");
1302 ret = -ENXIO;
1303 goto err_regs_map;
1304 }
1305
1306 info->fbmem_res = request_mem_region(res->start,
1307 (res->end - res->start)+1,
1308 pdev->name);
1309 if (info->fbmem_res == NULL) {
1310 dev_err(dev, "cannot claim framebuffer\n");
1311 ret = -ENXIO;
1312 goto err_regs_map;
1313 }
1314
1315 info->fbmem = ioremap(res->start, (res->end - res->start)+1);
1316 if (info->fbmem == NULL) {
1317 dev_err(dev, "cannot remap framebuffer\n");
1318 goto err_mem_res;
1319 }
1320
1321 info->fbmem_len = (res->end - res->start)+1;
1322
1323 /* enable display controller */
1324 sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1);
1325
1326 /* setup cursors */
1327
1328 sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR);
1329 sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR);
1330
1331 return 0; /* everything is setup */
1332
1333 err_mem_res:
1334 release_resource(info->fbmem_res);
1335 kfree(info->fbmem_res);
1336
1337 err_regs_map:
1338 iounmap(info->regs);
1339
1340 err_regs_res:
1341 release_resource(info->regs_res);
1342 kfree(info->regs_res);
1343
1344 err_release:
1345 return ret;
1346}
1347
1348static void sm501fb_stop(struct sm501fb_info *info)
1349{
1350 /* disable display controller */
1351 sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0);
1352
1353 iounmap(info->fbmem);
1354 release_resource(info->fbmem_res);
1355 kfree(info->fbmem_res);
1356
1357 iounmap(info->regs);
1358 release_resource(info->regs_res);
1359 kfree(info->regs_res);
1360}
1361
1362static void sm501fb_info_release(struct sm501fb_info *info)
1363{
1364 kfree(info);
1365}
1366
1367static int sm501fb_init_fb(struct fb_info *fb,
1368 enum sm501_controller head,
1369 const char *fbname)
1370{
1371 struct sm501_platdata_fbsub *pd;
1372 struct sm501fb_par *par = fb->par;
1373 struct sm501fb_info *info = par->info;
1374 unsigned long ctrl;
1375 unsigned int enable;
1376 int ret;
1377
1378 switch (head) {
1379 case HEAD_CRT:
1380 pd = info->pdata->fb_crt;
1381 ctrl = readl(info->regs + SM501_DC_CRT_CONTROL);
1382 enable = (ctrl & SM501_DC_CRT_CONTROL_ENABLE) ? 1 : 0;
1383
1384 /* ensure we set the correct source register */
1385 if (info->pdata->fb_route != SM501_FB_CRT_PANEL) {
1386 ctrl |= SM501_DC_CRT_CONTROL_SEL;
1387 writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
1388 }
1389
1390 break;
1391
1392 case HEAD_PANEL:
1393 pd = info->pdata->fb_pnl;
1394 ctrl = readl(info->regs + SM501_DC_PANEL_CONTROL);
1395 enable = (ctrl & SM501_DC_PANEL_CONTROL_EN) ? 1 : 0;
1396 break;
1397
1398 default:
1399 pd = NULL; /* stop compiler warnings */
1400 ctrl = 0;
1401 enable = 0;
1402 BUG();
1403 }
1404
1405 dev_info(info->dev, "fb %s %sabled at start\n",
1406 fbname, enable ? "en" : "dis");
1407
1408 /* check to see if our routing allows this */
1409
1410 if (head == HEAD_CRT && info->pdata->fb_route == SM501_FB_CRT_PANEL) {
1411 ctrl &= ~SM501_DC_CRT_CONTROL_SEL;
1412 writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
1413 enable = 0;
1414 }
1415
1416 strlcpy(fb->fix.id, fbname, sizeof(fb->fix.id));
1417
1418 memcpy(&par->ops,
1419 (head == HEAD_CRT) ? &sm501fb_ops_crt : &sm501fb_ops_pnl,
1420 sizeof(struct fb_ops));
1421
1422 /* update ops dependant on what we've been passed */
1423
1424 if ((pd->flags & SM501FB_FLAG_USE_HWCURSOR) == 0)
1425 par->ops.fb_cursor = NULL;
1426
1427 fb->fbops = &par->ops;
1428 fb->flags = FBINFO_FLAG_DEFAULT |
1429 FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
1430
1431 /* fixed data */
1432
1433 fb->fix.type = FB_TYPE_PACKED_PIXELS;
1434 fb->fix.type_aux = 0;
1435 fb->fix.xpanstep = 1;
1436 fb->fix.ypanstep = 1;
1437 fb->fix.ywrapstep = 0;
1438 fb->fix.accel = FB_ACCEL_NONE;
1439
1440 /* screenmode */
1441
1442 fb->var.nonstd = 0;
1443 fb->var.activate = FB_ACTIVATE_NOW;
1444 fb->var.accel_flags = 0;
1445 fb->var.vmode = FB_VMODE_NONINTERLACED;
1446 fb->var.bits_per_pixel = 16;
1447
1448 if (enable && (pd->flags & SM501FB_FLAG_USE_INIT_MODE) && 0) {
1449 /* TODO read the mode from the current display */
1450
1451 } else {
1452 if (pd->def_mode) {
1453 dev_info(info->dev, "using supplied mode\n");
1454 fb_videomode_to_var(&fb->var, pd->def_mode);
1455
1456 fb->var.bits_per_pixel = pd->def_bpp ? pd->def_bpp : 8;
1457 fb->var.xres_virtual = fb->var.xres;
1458 fb->var.yres_virtual = fb->var.yres;
1459 } else {
1460 ret = fb_find_mode(&fb->var, fb,
1461 NULL, NULL, 0, NULL, 8);
1462
1463 if (ret == 0 || ret == 4) {
1464 dev_err(info->dev,
1465 "failed to get initial mode\n");
1466 return -EINVAL;
1467 }
1468 }
1469 }
1470
1471 /* initialise and set the palette */
1472 fb_alloc_cmap(&fb->cmap, NR_PALETTE, 0);
1473 fb_set_cmap(&fb->cmap, fb);
1474
1475 ret = (fb->fbops->fb_check_var)(&fb->var, fb);
1476 if (ret)
1477 dev_err(info->dev, "check_var() failed on initial setup?\n");
1478
1479 /* ensure we've activated our new configuration */
1480 (fb->fbops->fb_set_par)(fb);
1481
1482 return 0;
1483}
1484
1485/* default platform data if none is supplied (ie, PCI device) */
1486
1487static struct sm501_platdata_fbsub sm501fb_pdata_crt = {
1488 .flags = (SM501FB_FLAG_USE_INIT_MODE |
1489 SM501FB_FLAG_USE_HWCURSOR |
1490 SM501FB_FLAG_USE_HWACCEL |
1491 SM501FB_FLAG_DISABLE_AT_EXIT),
1492
1493};
1494
1495static struct sm501_platdata_fbsub sm501fb_pdata_pnl = {
1496 .flags = (SM501FB_FLAG_USE_INIT_MODE |
1497 SM501FB_FLAG_USE_HWCURSOR |
1498 SM501FB_FLAG_USE_HWACCEL |
1499 SM501FB_FLAG_DISABLE_AT_EXIT),
1500};
1501
1502static struct sm501_platdata_fb sm501fb_def_pdata = {
1503 .fb_route = SM501_FB_OWN,
1504 .fb_crt = &sm501fb_pdata_crt,
1505 .fb_pnl = &sm501fb_pdata_pnl,
1506};
1507
1508static char driver_name_crt[] = "sm501fb-crt";
1509static char driver_name_pnl[] = "sm501fb-panel";
1510
1511static int __init sm501fb_probe(struct platform_device *pdev)
1512{
1513 struct sm501fb_info *info;
1514 struct device *dev = &pdev->dev;
1515 struct fb_info *fbinfo_crt;
1516 struct fb_info *fbinfo_pnl;
1517 int ret;
1518
1519 /* allocate our framebuffers */
1520
1521 fbinfo_crt = framebuffer_alloc(sizeof(struct sm501fb_par), dev);
1522 if (fbinfo_crt == NULL) {
1523 dev_err(dev, "cannot allocate crt framebuffer\n");
1524 return -ENOMEM;
1525 }
1526
1527 fbinfo_pnl = framebuffer_alloc(sizeof(struct sm501fb_par), dev);
1528 if (fbinfo_pnl == NULL) {
1529 dev_err(dev, "cannot allocate panel framebuffer\n");
1530 ret = -ENOMEM;
1531 goto fbinfo_crt_alloc_fail;
1532 }
1533
1534 info = sm501fb_info_alloc(fbinfo_crt, fbinfo_pnl);
1535 if (info == NULL) {
1536 dev_err(dev, "cannot allocate par\n");
1537 ret = -ENOMEM;
1538 goto sm501fb_alloc_fail;
1539 }
1540
1541 if (dev->parent->platform_data) {
1542 struct sm501_platdata *pd = dev->parent->platform_data;
1543 info->pdata = pd->fb;
1544 }
1545
1546 if (info->pdata == NULL) {
1547 dev_info(dev, "using default configuration data\n");
1548 info->pdata = &sm501fb_def_pdata;
1549 }
1550
1551 /* start the framebuffers */
1552
1553 ret = sm501fb_start(info, pdev);
1554 if (ret) {
1555 dev_err(dev, "cannot initialise SM501\n");
1556 goto sm501fb_start_fail;
1557 }
1558
1559 /* CRT framebuffer setup */
1560
1561 ret = sm501fb_init_fb(fbinfo_crt, HEAD_CRT, driver_name_crt);
1562 if (ret) {
1563 dev_err(dev, "cannot initialise CRT fb\n");
1564 goto sm501fb_start_fail;
1565 }
1566
1567 /* Panel framebuffer setup */
1568
1569 ret = sm501fb_init_fb(fbinfo_pnl, HEAD_PANEL, driver_name_pnl);
1570 if (ret) {
1571 dev_err(dev, "cannot initialise Panel fb\n");
1572 goto sm501fb_start_fail;
1573 }
1574
1575 /* register framebuffers */
1576
1577 ret = register_framebuffer(fbinfo_crt);
1578 if (ret < 0) {
1579 dev_err(dev, "failed to register CRT fb (%d)\n", ret);
1580 goto register_crt_fail;
1581 }
1582
1583 ret = register_framebuffer(fbinfo_pnl);
1584 if (ret < 0) {
1585 dev_err(dev, "failed to register panel fb (%d)\n", ret);
1586 goto register_pnl_fail;
1587 }
1588
1589 dev_info(dev, "fb%d: %s frame buffer device\n",
1590 fbinfo_crt->node, fbinfo_crt->fix.id);
1591
1592 dev_info(dev, "fb%d: %s frame buffer device\n",
1593 fbinfo_pnl->node, fbinfo_pnl->fix.id);
1594
1595 /* create device files */
1596
1597 ret = device_create_file(dev, &dev_attr_crt_src);
1598 if (ret)
1599 goto crtsrc_fail;
1600
1601 ret = device_create_file(dev, &dev_attr_fbregs_pnl);
1602 if (ret)
1603 goto fbregs_pnl_fail;
1604
1605 ret = device_create_file(dev, &dev_attr_fbregs_crt);
1606 if (ret)
1607 goto fbregs_crt_fail;
1608
1609 /* we registered, return ok */
1610 return 0;
1611
1612 fbregs_crt_fail:
1613 device_remove_file(dev, &dev_attr_fbregs_pnl);
1614
1615 fbregs_pnl_fail:
1616 device_remove_file(dev, &dev_attr_crt_src);
1617
1618 crtsrc_fail:
1619 unregister_framebuffer(fbinfo_pnl);
1620
1621 register_pnl_fail:
1622 unregister_framebuffer(fbinfo_crt);
1623
1624 register_crt_fail:
1625 sm501fb_stop(info);
1626
1627 sm501fb_start_fail:
1628 sm501fb_info_release(info);
1629
1630 sm501fb_alloc_fail:
1631 framebuffer_release(fbinfo_pnl);
1632
1633 fbinfo_crt_alloc_fail:
1634 framebuffer_release(fbinfo_crt);
1635
1636 return ret;
1637}
1638
1639
1640/*
1641 * Cleanup
1642 */
1643static int sm501fb_remove(struct platform_device *pdev)
1644{
1645 struct sm501fb_info *info = platform_get_drvdata(pdev);
1646 struct fb_info *fbinfo_crt = info->fb[0];
1647 struct fb_info *fbinfo_pnl = info->fb[1];
1648
1649 device_remove_file(&pdev->dev, &dev_attr_fbregs_crt);
1650 device_remove_file(&pdev->dev, &dev_attr_fbregs_pnl);
1651 device_remove_file(&pdev->dev, &dev_attr_crt_src);
1652
1653 unregister_framebuffer(fbinfo_crt);
1654 unregister_framebuffer(fbinfo_pnl);
1655
1656 sm501fb_stop(info);
1657 sm501fb_info_release(info);
1658
1659 framebuffer_release(fbinfo_pnl);
1660 framebuffer_release(fbinfo_crt);
1661
1662 return 0;
1663}
1664
1665#ifdef CONFIG_PM
1666
1667static int sm501fb_suspend_fb(struct sm501fb_info *info,
1668 enum sm501_controller head)
1669{
1670 struct fb_info *fbi = info->fb[head];
1671 struct sm501fb_par *par = fbi->par;
1672
1673 if (par->screen.size == 0)
1674 return 0;
1675
1676 /* backup copies in case chip is powered down over suspend */
1677
1678 par->store_fb = vmalloc(par->screen.size);
1679 if (par->store_fb == NULL) {
1680 dev_err(info->dev, "no memory to store screen\n");
1681 return -ENOMEM;
1682 }
1683
1684 par->store_cursor = vmalloc(par->cursor.size);
1685 if (par->store_cursor == NULL) {
1686 dev_err(info->dev, "no memory to store cursor\n");
1687 goto err_nocursor;
1688 }
1689
1690 memcpy_fromio(par->store_fb, par->screen.k_addr, par->screen.size);
1691 memcpy_fromio(par->store_cursor, par->cursor.k_addr, par->cursor.size);
1692
1693 /* blank the relevant interface to ensure unit power minimised */
1694 (par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi);
1695
1696 return 0;
1697
1698 err_nocursor:
1699 vfree(par->store_fb);
1700
1701 return -ENOMEM;
1702
1703}
1704
1705static void sm501fb_resume_fb(struct sm501fb_info *info,
1706 enum sm501_controller head)
1707{
1708 struct fb_info *fbi = info->fb[head];
1709 struct sm501fb_par *par = fbi->par;
1710
1711 if (par->screen.size == 0)
1712 return;
1713
1714 /* re-activate the configuration */
1715
1716 (par->ops.fb_set_par)(fbi);
1717
1718 /* restore the data */
1719
1720 memcpy_toio(par->screen.k_addr, par->store_fb, par->screen.size);
1721 memcpy_toio(par->cursor.k_addr, par->store_cursor, par->cursor.size);
1722
1723 vfree(par->store_fb);
1724 vfree(par->store_cursor);
1725}
1726
1727
1728/* suspend and resume support */
1729
1730static int sm501fb_suspend(struct platform_device *pdev, pm_message_t state)
1731{
1732 struct sm501fb_info *info = platform_get_drvdata(pdev);
1733
1734 sm501fb_suspend_fb(info, HEAD_CRT);
1735 sm501fb_suspend_fb(info, HEAD_PANEL);
1736
1737 /* turn off the clocks, in case the device is not powered down */
1738 sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0);
1739
1740 return 0;
1741}
1742
1743static int sm501fb_resume(struct platform_device *pdev)
1744{
1745 struct sm501fb_info *info = platform_get_drvdata(pdev);
1746
1747 sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 1);
1748
1749 sm501fb_resume_fb(info, HEAD_CRT);
1750 sm501fb_resume_fb(info, HEAD_PANEL);
1751
1752 return 0;
1753}
1754
1755#else
1756#define sm501fb_suspend NULL
1757#define sm501fb_resume NULL
1758#endif
1759
1760static struct platform_driver sm501fb_driver = {
1761 .probe = sm501fb_probe,
1762 .remove = sm501fb_remove,
1763 .suspend = sm501fb_suspend,
1764 .resume = sm501fb_resume,
1765 .driver = {
1766 .name = "sm501-fb",
1767 .owner = THIS_MODULE,
1768 },
1769};
1770
1771int __devinit sm501fb_init(void)
1772{
1773 return platform_driver_register(&sm501fb_driver);
1774}
1775
1776static void __exit sm501fb_cleanup(void)
1777{
1778 platform_driver_unregister(&sm501fb_driver);
1779}
1780
1781module_init(sm501fb_init);
1782module_exit(sm501fb_cleanup);
1783
1784MODULE_AUTHOR("Ben Dooks, Vincent Sanders");
1785MODULE_DESCRIPTION("SM501 Framebuffer driver");
1786MODULE_LICENSE("GPL v2");