upload tizen1.0 source
[kernel/linux-2.6.36.git] / drivers / media / video / samsung / tvout / s5p_tvout_fb.c
1 /* linux/drivers/media/video/samsung/tvout/s5p_tvout_fb.c
2  *
3  * Copyright (c) 2009 Samsung Electronics
4  *              http://www.samsung.com/
5  *
6  * Frame buffer ftn. file for Samsung TVOUT driver
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11 */
12
13 #include <linux/fb.h>
14 #include <linux/dma-mapping.h>
15 #include <linux/uaccess.h>
16
17 #include "s5p_tvout_common_lib.h"
18 #include "s5p_tvout_ctrl.h"
19 #include "s5p_tvout_v4l2.h"
20
21 #ifdef CONFIG_UMP_VCM_ALLOC
22 #include "ump_kernel_interface.h"
23 #include "ump_kernel_interface_ref_drv.h"
24 #include "ump_kernel_interface_vcm.h"
25 #endif
26
27 #define S5PTVFB_NAME            "s5ptvfb"
28
29 #define S5PTV_FB_LAYER0_MINOR   10
30 #define S5PTV_FB_LAYER1_MINOR   11
31
32 #define FB_INDEX(id)    (id - S5PTV_FB_LAYER0_MINOR)
33
34 #define S5PTVFB_CHROMA(r, g, b) \
35         (((r & 0xff) << 16) | ((g & 0xff) << 8) | ((b & 0xff) << 0))
36
37 #define S5PTVFB_WIN_POSITION    \
38         _IOW('F', 213, struct s5ptvfb_user_window)
39 #define S5PTVFB_WIN_SET_PLANE_ALPHA     \
40         _IOW('F', 214, struct s5ptvfb_user_plane_alpha)
41 #define S5PTVFB_WIN_SET_CHROMA  \
42         _IOW('F', 215, struct s5ptvfb_user_chroma)
43 #define S5PTVFB_WIN_SET_ADDR    \
44         _IOW('F', 219, u32)
45 #define S5PTVFB_SCALING \
46         _IOW('F', 222, struct s5ptvfb_user_scaling)
47
48 struct s5ptvfb_window {
49         int                             id;
50         struct device                   *dev_fb;
51         int                             enabled;
52         atomic_t                        in_use;
53         int                             x;
54         int                             y;
55         enum s5ptvfb_data_path_t        path;
56         int                             local_channel;
57         int                             dma_burst;
58         unsigned int                    pseudo_pal[16];
59         struct s5ptvfb_alpha            alpha;
60         struct s5ptvfb_chroma           chroma;
61         int                             (*suspend_fifo)(void);
62         int                             (*resume_fifo)(void);
63 };
64
65 struct s5ptvfb_lcd_timing {
66         int     h_fp;
67         int     h_bp;
68         int     h_sw;
69         int     v_fp;
70         int     v_fpe;
71         int     v_bp;
72         int     v_bpe;
73         int     v_sw;
74 };
75
76 struct s5ptvfb_lcd_polarity {
77         int     rise_vclk;
78         int     inv_hsync;
79         int     inv_vsync;
80         int     inv_vden;
81 };
82
83 struct s5ptvfb_lcd {
84         int                             width;
85         int                             height;
86         int                             bpp;
87         int                             freq;
88         struct s5ptvfb_lcd_timing       timing;
89         struct s5ptvfb_lcd_polarity     polarity;
90
91         void                            (*init_ldi)(void);
92 };
93
94 static struct mutex fb_lock;
95
96 static struct fb_info *fb[S5PTV_FB_CNT];
97 static struct s5ptvfb_lcd lcd = {
98         .width = 1920,
99         .height = 1080,
100         .bpp = 32,
101         .freq = 60,
102
103         .timing = {
104                 .h_fp = 49,
105                 .h_bp = 17,
106                 .h_sw = 33,
107                 .v_fp = 4,
108                 .v_fpe = 1,
109                 .v_bp = 15,
110                 .v_bpe = 1,
111                 .v_sw = 6,
112         },
113
114         .polarity = {
115                 .rise_vclk = 0,
116                 .inv_hsync = 1,
117                 .inv_vsync = 1,
118                 .inv_vden = 0,
119         },
120 };
121
122 static inline unsigned int s5p_tvout_fb_chan_to_field(unsigned int chan,
123                                                 struct fb_bitfield bf)
124 {
125         chan &= 0xffff;
126         chan >>= 16 - bf.length;
127
128         return chan << bf.offset;
129 }
130
131 static int s5p_tvout_fb_set_alpha_info(struct fb_var_screeninfo *var,
132                                 struct s5ptvfb_window *win)
133 {
134         if (var->transp.length > 0)
135                 win->alpha.mode = PIXEL_BLENDING;
136         else
137                 win->alpha.mode = NONE_BLENDING;
138
139         return 0;
140 }
141 #if 0 /* This function will be used in the future */
142 static int s5p_tvout_fb_map_video_memory(int id)
143 {
144         enum s5p_mixer_layer layer;
145         struct s5ptvfb_window *win = fb[FB_INDEX(id)]->par;
146         struct fb_fix_screeninfo *fix = &fb[FB_INDEX(id)]->fix;
147
148         if (win->path == DATA_PATH_FIFO)
149                 return 0;
150
151         fb[FB_INDEX(id)]->screen_base = dma_alloc_writecombine(win->dev_fb,
152                         PAGE_ALIGN(fix->smem_len),
153                         (unsigned int *) &fix->smem_start, GFP_KERNEL);
154
155         switch (id) {
156         case S5PTV_FB_LAYER0_MINOR:
157                 layer = MIXER_GPR0_LAYER;
158                 break;
159         case S5PTV_FB_LAYER1_MINOR:
160                 layer = MIXER_GPR1_LAYER;
161                 break;
162         default:
163                 tvout_err("invalid layer\n");
164                 return -1;
165         }
166         s5p_mixer_ctrl_init_fb_addr_phy(layer, fix->smem_start);
167
168         if (!fb[FB_INDEX(id)]->screen_base)
169                 return -1;
170         else
171                 tvout_dbg("[fb%d] dma: 0x%08x, cpu: 0x%08x,size: 0x%08x\n",
172                         win->id, (unsigned int) fix->smem_start,
173                         (unsigned int) fb[FB_INDEX(id)]->screen_base,
174                         fix->smem_len);
175
176         memset(fb[FB_INDEX(id)]->screen_base, 0, fix->smem_len);
177
178         return 0;
179 }
180 #endif
181 static int s5p_tvout_fb_set_bitfield(struct fb_var_screeninfo *var)
182 {
183         switch (var->bits_per_pixel) {
184         case 16:
185                 if (var->transp.length == 1) {
186                         var->red.offset = 10;
187                         var->red.length = 5;
188                         var->green.offset = 5;
189                         var->green.length = 5;
190                         var->blue.offset = 0;
191                         var->blue.length = 5;
192                         var->transp.offset = 15;
193                 } else if (var->transp.length == 4) {
194                         var->red.offset = 8;
195                         var->red.length = 4;
196                         var->green.offset = 4;
197                         var->green.length = 4;
198                         var->blue.offset = 0;
199                         var->blue.length = 4;
200                         var->transp.offset = 12;
201                 } else {
202                         var->red.offset = 11;
203                         var->red.length = 5;
204                         var->green.offset = 5;
205                         var->green.length = 6;
206                         var->blue.offset = 0;
207                         var->blue.length = 5;
208                         var->transp.offset = 0;
209                 }
210                 break;
211
212         case 24:
213                 var->red.offset = 16;
214                 var->red.length = 8;
215                 var->green.offset = 8;
216                 var->green.length = 8;
217                 var->blue.offset = 0;
218                 var->blue.length = 8;
219                 var->transp.offset = 0;
220                 var->transp.length = 0;
221                 break;
222
223         case 32:
224                 var->red.offset = 16;
225                 var->red.length = 8;
226                 var->green.offset = 8;
227                 var->green.length = 8;
228                 var->blue.offset = 0;
229                 var->blue.length = 8;
230                 var->transp.offset = 24;
231                 break;
232         }
233
234         return 0;
235 }
236
237 static int s5p_tvout_fb_setcolreg(unsigned int regno, unsigned int red,
238                                 unsigned int green, unsigned int blue,
239                                 unsigned int transp, struct fb_info *fb)
240 {
241         unsigned int *pal = (unsigned int *) fb->pseudo_palette;
242         unsigned int val = 0;
243
244         if (regno < 16) {
245                 /* fake palette of 16 colors */
246                 val |= s5p_tvout_fb_chan_to_field(red, fb->var.red);
247                 val |= s5p_tvout_fb_chan_to_field(green, fb->var.green);
248                 val |= s5p_tvout_fb_chan_to_field(blue, fb->var.blue);
249                 val |= s5p_tvout_fb_chan_to_field(transp, fb->var.transp);
250
251                 pal[regno] = val;
252         }
253
254         return 0;
255 }
256
257 static int s5p_tvout_fb_pan_display(struct fb_var_screeninfo *var,
258                                 struct fb_info *fb)
259 {
260         dma_addr_t start_addr;
261         enum s5p_mixer_layer layer;
262         struct fb_fix_screeninfo *fix = &fb->fix;
263
264         if (var->yoffset + var->yres > var->yres_virtual) {
265                 tvout_err("invalid y offset value\n");
266                 return -1;
267         }
268
269         fb->var.yoffset = var->yoffset;
270
271         switch (fb->node) {
272         case S5PTV_FB_LAYER0_MINOR:
273                 layer = MIXER_GPR0_LAYER;
274                 break;
275         case S5PTV_FB_LAYER1_MINOR:
276                 layer = MIXER_GPR1_LAYER;
277                 break;
278         default:
279                 tvout_err("invalid layer\n");
280                 return -1;
281         }
282
283         start_addr = fix->smem_start + (var->xres_virtual *
284                                 (var->bits_per_pixel / 8) * var->yoffset);
285
286         s5p_mixer_ctrl_set_buffer_address(layer, start_addr);
287
288         return 0;
289 }
290
291 static int s5p_tvout_fb_blank(int blank_mode, struct fb_info *fb)
292 {
293         enum s5p_mixer_layer layer = MIXER_GPR0_LAYER;
294
295         tvout_dbg("change blank mode\n");
296
297         switch (fb->node) {
298         case S5PTV_FB_LAYER0_MINOR:
299                 layer = MIXER_GPR0_LAYER;
300                 break;
301         case S5PTV_FB_LAYER1_MINOR:
302                 layer = MIXER_GPR1_LAYER;
303                 break;
304         default:
305                 tvout_err("not supported layer\n");
306                 return -1;
307         }
308
309         switch (blank_mode) {
310         case FB_BLANK_UNBLANK:
311                 if (fb->fix.smem_start)
312                         s5p_mixer_ctrl_enable_layer(layer);
313                 else
314                         tvout_dbg("[fb%d] no alloc memory for unblank\n",
315                                 fb->node);
316                 break;
317
318         case FB_BLANK_POWERDOWN:
319                 s5p_mixer_ctrl_disable_layer(layer);
320                 break;
321
322         default:
323                 tvout_err("not supported blank mode\n");
324                 return -1;
325         }
326
327         return 0;
328 }
329
330 static int s5p_tvout_fb_set_par(struct fb_info *fb)
331 {
332         u32 bpp, trans_len;
333         u32 src_x, src_y, w, h;
334         struct s5ptvfb_window *win = fb->par;
335         enum s5p_mixer_layer layer = MIXER_GPR0_LAYER;
336
337         tvout_dbg("[fb%d] set_par\n", win->id);
338
339         if (!fb->fix.smem_start) {
340 #ifndef CONFIG_USER_ALLOC_TVOUT
341                 printk(KERN_INFO " The frame buffer is allocated here\n");
342                 s5p_tvout_fb_map_video_memory(win->id);
343 #else
344                 printk(KERN_ERR
345                 "[Warning] The frame buffer should be allocated by ioctl\n");
346 #endif
347         }
348
349         bpp = fb->var.bits_per_pixel;
350         trans_len = fb->var.transp.length;
351         w = fb->var.xres;
352         h = fb->var.yres;
353         src_x = fb->var.xoffset;
354         src_y = fb->var.yoffset;
355
356         switch (fb->node) {
357         case S5PTV_FB_LAYER0_MINOR:
358                 layer = MIXER_GPR0_LAYER;
359                 break;
360         case S5PTV_FB_LAYER1_MINOR:
361                 layer = MIXER_GPR1_LAYER;
362                 break;
363         }
364
365         s5p_mixer_ctrl_init_grp_layer(layer);
366         s5p_mixer_ctrl_set_pixel_format(layer, bpp, trans_len);
367         s5p_mixer_ctrl_set_src_win_pos(layer, src_x, src_y, w, h);
368         s5p_mixer_ctrl_set_alpha_blending(layer, win->alpha.mode,
369                                 win->alpha.value);
370         return 0;
371 }
372
373 static int s5p_tvout_fb_check_var(struct fb_var_screeninfo *var,
374                                         struct fb_info *fb)
375 {
376         struct fb_fix_screeninfo *fix = &fb->fix;
377         struct s5ptvfb_window *win = fb->par;
378
379         tvout_dbg("[fb%d] check_var\n", win->id);
380
381         if (var->bits_per_pixel != 16 && var->bits_per_pixel != 24 &&
382                 var->bits_per_pixel != 32) {
383                 tvout_err("invalid bits per pixel\n");
384                 return -1;
385         }
386
387         if (var->xres > lcd.width)
388                 var->xres = lcd.width;
389
390         if (var->yres > lcd.height)
391                 var->yres = lcd.height;
392
393         if (var->xres_virtual != var->xres)
394                 var->xres_virtual = var->xres;
395
396         if (var->yres_virtual > var->yres * (fb->fix.ypanstep + 1))
397                 var->yres_virtual = var->yres * (fb->fix.ypanstep + 1);
398
399         if (var->xoffset != 0)
400                 var->xoffset = 0;
401
402         if (var->yoffset + var->yres > var->yres_virtual)
403                 var->yoffset = var->yres_virtual - var->yres;
404
405         if (win->x + var->xres > lcd.width)
406                 win->x = lcd.width - var->xres;
407
408         if (win->y + var->yres > lcd.height)
409                 win->y = lcd.height - var->yres;
410
411         /* modify the fix info */
412         fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
413         fix->smem_len = fix->line_length * var->yres_virtual;
414
415         s5p_tvout_fb_set_bitfield(var);
416         s5p_tvout_fb_set_alpha_info(var, win);
417
418         return 0;
419 }
420
421 static int s5p_tvout_fb_release(struct fb_info *fb, int user)
422 {
423         struct s5ptvfb_window *win = fb->par;
424
425         atomic_dec(&win->in_use);
426
427         return 0;
428 }
429
430 static int s5p_tvout_fb_ioctl(struct fb_info *fb, unsigned int cmd,
431                         unsigned long arg)
432 {
433         dma_addr_t start_addr;
434         enum s5p_mixer_layer layer;
435         struct fb_var_screeninfo *var = &fb->var;
436         struct s5ptvfb_window *win = fb->par;
437         int ret = 0;
438         void *argp = (void *) arg;
439 #if defined(CONFIG_S5P_SYSMMU_TV) && defined(CONFIG_UMP_VCM_ALLOC)
440         struct vcm_res *vcm_res;
441 #endif
442
443         union {
444                 struct s5ptvfb_user_window user_window;
445                 struct s5ptvfb_user_plane_alpha user_alpha;
446                 struct s5ptvfb_user_chroma user_chroma;
447                 struct s5ptvfb_user_scaling user_scaling;
448                 int vsync;
449         } p;
450
451         switch (fb->node) {
452         case S5PTV_FB_LAYER0_MINOR:
453                 layer = MIXER_GPR0_LAYER;
454                 break;
455         case S5PTV_FB_LAYER1_MINOR:
456                 layer = MIXER_GPR1_LAYER;
457                 break;
458         default:
459                 printk(KERN_ERR "[Error] invalid layer\n");
460                 return -1;
461         }
462
463         switch (cmd) {
464         case S5PTVFB_WIN_POSITION:
465                 if (copy_from_user(&p.user_window,
466                         (struct s5ptvfb_user_window __user *) arg,
467                         sizeof(p.user_window)))
468                         ret = -EFAULT;
469                 else {
470                         s5p_mixer_ctrl_set_dst_win_pos(layer, p.user_window.x,
471                                 p.user_window.y, var->xres, var->yres);
472                 }
473                 break;
474
475         case S5PTVFB_WIN_SET_PLANE_ALPHA:
476                 if (copy_from_user(&p.user_alpha,
477                         (struct s5ptvfb_user_plane_alpha __user *) arg,
478                         sizeof(p.user_alpha)))
479                         ret = -EFAULT;
480                 else {
481                         win->alpha.mode = LAYER_BLENDING;
482                         win->alpha.value = p.user_alpha.alpha;
483                         s5p_mixer_ctrl_set_alpha_blending(layer,
484                                 win->alpha.mode, win->alpha.value);
485                 }
486                 break;
487         case S5PTVFB_WIN_SET_CHROMA:
488                 if (copy_from_user(&p.user_chroma,
489                         (struct s5ptvfb_user_chroma __user *) arg,
490                         sizeof(p.user_chroma)))
491                         ret = -EFAULT;
492                 else {
493                         win->chroma.enabled = p.user_chroma.enabled;
494                         win->chroma.key = S5PTVFB_CHROMA(p.user_chroma.red,
495                                                 p.user_chroma.green,
496                                                 p.user_chroma.blue);
497
498                         s5p_mixer_ctrl_set_chroma_key(layer, win->chroma);
499                 }
500                 break;
501
502         case S5PTVFB_WIN_SET_ADDR:
503 #if defined(CONFIG_S5P_SYSMMU_TV) && defined(CONFIG_UMP_VCM_ALLOC)
504                 vcm_res = (struct vcm_res *)
505                         ump_dd_meminfo_get((unsigned int)argp, (void*)VCM_DEV_TV);
506                 if (vcm_res)
507                         fb->fix.smem_start = vcm_res->start;
508                 else
509                         return -1;
510  #else
511                 fb->fix.smem_start = (unsigned long)argp;
512 #endif
513                 start_addr = fb->fix.smem_start + (var->xres_virtual *
514                                 (var->bits_per_pixel / 8) * var->yoffset);
515
516                 s5p_mixer_ctrl_set_buffer_address(layer, start_addr);
517                 break;
518         case S5PTVFB_SCALING:
519                 if (copy_from_user(&p.user_scaling,
520                         (struct s5ptvfb_user_scaling __user *) arg,
521                         sizeof(p.user_scaling)))
522                         ret = -EFAULT;
523                 else
524                         s5p_mixer_ctrl_scaling(layer, p.user_scaling);
525                 break;
526         }
527
528         return 0;
529 }
530
531 static int s5p_tvout_fb_open(struct fb_info *fb, int user)
532 {
533         struct s5ptvfb_window *win = fb->par;
534         int ret = 0;
535
536         mutex_lock(&fb_lock);
537
538         if (atomic_read(&win->in_use)) {
539                 tvout_dbg("do not allow multiple open for tvout framebuffer\n");
540                 ret = -EBUSY;
541         } else
542                 atomic_inc(&win->in_use);
543
544         mutex_unlock(&fb_lock);
545
546         return ret;
547 }
548
549 struct fb_ops s5ptvfb_ops = {
550         .owner = THIS_MODULE,
551         .fb_fillrect = cfb_fillrect,
552         .fb_copyarea = cfb_copyarea,
553         .fb_imageblit = cfb_imageblit,
554         .fb_check_var = s5p_tvout_fb_check_var,
555         .fb_set_par = s5p_tvout_fb_set_par,
556         .fb_blank = s5p_tvout_fb_blank,
557         .fb_pan_display = s5p_tvout_fb_pan_display,
558         .fb_setcolreg = s5p_tvout_fb_setcolreg,
559         .fb_ioctl = s5p_tvout_fb_ioctl,
560         .fb_open = s5p_tvout_fb_open,
561         .fb_release = s5p_tvout_fb_release,
562 };
563
564 static int s5p_tvout_fb_init_fbinfo(int id, struct device *dev_fb)
565 {
566         struct fb_fix_screeninfo *fix = &fb[FB_INDEX(id)]->fix;
567         struct fb_var_screeninfo *var = &fb[FB_INDEX(id)]->var;
568         struct s5ptvfb_window *win = fb[FB_INDEX(id)]->par;
569         struct s5ptvfb_alpha *alpha = &win->alpha;
570
571         memset(win, 0, sizeof(struct s5ptvfb_window));
572
573         platform_set_drvdata(to_platform_device(dev_fb), fb[FB_INDEX(id)]);
574
575         strcpy(fix->id, S5PTVFB_NAME);
576
577         /* fimd specific */
578         win->id = id;
579         win->path = DATA_PATH_DMA;
580         win->dma_burst = 16;
581         win->dev_fb = dev_fb;
582         alpha->mode = LAYER_BLENDING;
583         alpha->value = 0xff;
584
585         /* fbinfo */
586         fb[FB_INDEX(id)]->fbops = &s5ptvfb_ops;
587         fb[FB_INDEX(id)]->flags = FBINFO_FLAG_DEFAULT;
588         fb[FB_INDEX(id)]->pseudo_palette = &win->pseudo_pal;
589         fix->xpanstep = 0;
590         fix->ypanstep = 0;
591         fix->type = FB_TYPE_PACKED_PIXELS;
592         fix->accel = FB_ACCEL_NONE;
593         fix->visual = FB_VISUAL_TRUECOLOR;
594         var->xres = lcd.width;
595         var->yres = lcd.height;
596         var->xres_virtual = var->xres;
597         var->yres_virtual = var->yres + (var->yres * fix->ypanstep);
598         var->bits_per_pixel = 32;
599         var->xoffset = 0;
600         var->yoffset = 0;
601         var->width = 0;
602         var->height = 0;
603         var->transp.length = 0;
604
605         fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
606         fix->smem_len = fix->line_length * var->yres_virtual;
607
608         var->nonstd = 0;
609         var->activate = FB_ACTIVATE_NOW;
610         var->vmode = FB_VMODE_NONINTERLACED;
611         var->hsync_len = lcd.timing.h_sw;
612         var->vsync_len = lcd.timing.v_sw;
613         var->left_margin = lcd.timing.h_fp;
614         var->right_margin = lcd.timing.h_bp;
615         var->upper_margin = lcd.timing.v_fp;
616         var->lower_margin = lcd.timing.v_bp;
617
618         var->pixclock = lcd.freq * (var->left_margin + var->right_margin +
619                                 var->hsync_len + var->xres) *
620                                 (var->upper_margin + var->lower_margin +
621                                 var->vsync_len + var->yres);
622
623         tvout_dbg("pixclock: %d\n", var->pixclock);
624
625         s5p_tvout_fb_set_bitfield(var);
626         s5p_tvout_fb_set_alpha_info(var, win);
627
628         mutex_init(&fb_lock);
629
630         return 0;
631 }
632
633 int s5p_tvout_fb_alloc_framebuffer(struct device *dev_fb)
634 {
635         int ret, i;
636
637         /* alloc for each framebuffer */
638         for (i = 0; i < S5PTV_FB_CNT; i++) {
639                 fb[i] = framebuffer_alloc(sizeof(struct s5ptvfb_window),
640                         dev_fb);
641                 if (!fb[i]) {
642                         tvout_err("not enough memory\n");
643                         ret = -1;
644                         goto err_alloc_fb;
645                 }
646
647                 ret = s5p_tvout_fb_init_fbinfo(i + S5PTV_FB_LAYER0_MINOR,
648                                                 dev_fb);
649                 if (ret) {
650                         tvout_err("fail to allocate memory for tv fb\n");
651                         ret = -1;
652                         goto err_alloc_fb;
653                 }
654
655 #ifndef CONFIG_USER_ALLOC_TVOUT
656                 if (s5p_tvout_fb_map_video_memory(i + S5PTV_FB_LAYER0_MINOR)) {
657                         tvout_err("fail to map video mem for default window\n");
658                         ret = -1;
659                         goto err_alloc_fb;
660                 }
661 #endif
662         }
663
664         return 0;
665
666 err_alloc_fb:
667         for (i = 0; i < S5PTV_FB_CNT; i++) {
668                 if (fb[i])
669                         framebuffer_release(fb[i]);
670         }
671
672         return ret;
673 }
674
675 int s5p_tvout_fb_register_framebuffer(struct device *dev_fb)
676 {
677         int ret, i = 0;
678
679         do {
680                 ret = register_framebuffer(fb[0]);
681                 if (ret) {
682                         tvout_err("fail to register framebuffer device\n");
683                         return -1;
684                 }
685         } while (fb[0]->node < S5PTV_FB_LAYER0_MINOR);
686
687         for (i = 1; i < S5PTV_FB_CNT; i++) {
688                 ret = register_framebuffer(fb[i]);
689                 if (ret) {
690                         tvout_err("fail to register framebuffer device\n");
691                         return -1;
692                 }
693         }
694
695         for (i = 0; i < S5PTV_FB_CNT; i++)
696                 tvout_dbg("fb[%d] = %d\n", i, fb[i]->node);
697
698         for (i = 0; i < S5PTV_FB_CNT; i++) {
699 #ifndef CONFIG_FRAMEBUFFER_CONSOLE
700 #ifndef CONFIG_USER_ALLOC_TVOUT
701                 s5p_tvout_fb_check_var(&fb[i]->var, fb[i]);
702                 s5p_tvout_fb_set_par(fb[i]);
703 #endif
704 #endif
705         }
706
707         return 0;
708 }