LUT updates
[platform/upstream/libdrm.git] / linux-core / nv50_fbcon.c
1 /*
2  * Copyright (C) 2008 Maarten Maathuis.
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial
15  * portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  */
26
27 #include <linux/module.h>
28 #include <linux/kernel.h>
29 #include <linux/errno.h>
30 #include <linux/string.h>
31 #include <linux/mm.h>
32 #include <linux/tty.h>
33 #include <linux/slab.h>
34 #include <linux/delay.h>
35 #include <linux/fb.h>
36 #include <linux/init.h>
37
38 #include "nv50_fbcon.h"
39
40 static int nv50_fbcon_setcolreg(unsigned regno, unsigned red, unsigned green,
41                         unsigned blue, unsigned transp,
42                         struct fb_info *info)
43 {
44         struct nv50_fbcon_par *par = info->par;
45         struct drm_device *dev = par->dev;
46         struct drm_framebuffer *drm_fb; 
47
48         list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
49                 if (regno > 255)
50                         return 1;
51
52                 /* TODO: 8 bit support */
53                 if (regno < 16) {
54                         switch (drm_fb->depth) {
55                         case 15:
56                                 drm_fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
57                                         ((green & 0xf800) >>  6) |
58                                         ((blue & 0xf800) >> 11);
59                                 break;
60                         case 16:
61                                 drm_fb->pseudo_palette[regno] = (red & 0xf800) |
62                                         ((green & 0xfc00) >>  5) |
63                                         ((blue  & 0xf800) >> 11);
64                                 break;
65                         case 24:
66                         case 32:
67                                 drm_fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
68                                         (green & 0xff00) |
69                                         ((blue  & 0xff00) >> 8);
70                                 break;
71                         }
72                 }
73         }
74         return 0;
75 }
76
77 static int nv50_fbcon_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
78 {
79         struct nv50_fbcon_par *par = info->par;
80         struct drm_framebuffer *drm_fb = par->fb;
81         int depth;
82
83         NV50_DEBUG("\n");
84
85         if (!var || !drm_fb || !info) {
86                 DRM_ERROR("No var, drm_fb or info\n");
87         }
88
89         par->use_preferred_mode = false;
90
91         if (var->pixclock == -1 || !var->pixclock) {
92                 DRM_INFO("Using preferred mode.\n");
93                 par->use_preferred_mode = true;
94         }
95
96         /* Need to resize the fb object !!! */
97         if (var->xres > drm_fb->width || var->yres > drm_fb->height) {
98                 DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n", var->xres,var->yres, drm_fb->width, drm_fb->height);
99                 DRM_ERROR("Need resizing code.\n");
100                 return -EINVAL;
101         }
102
103         switch (var->bits_per_pixel) {
104         case 16:
105                 depth = (var->green.length == 6) ? 16 : 15;
106                 break;
107         case 32:
108                 depth = (var->transp.length > 0) ? 32 : 24;
109                 break;
110         default:
111                 depth = var->bits_per_pixel;
112                 break;
113         }
114
115         switch (depth) {
116         case 15:
117                 var->red.offset = 10;
118                 var->green.offset = 5;
119                 var->blue.offset = 0;
120                 var->red.length = 5;
121                 var->green.length = 5;
122                 var->blue.length = 5;
123                 var->transp.length = 1;
124                 var->transp.offset = 15;
125                 break;
126         case 16:
127                 var->red.offset = 11;
128                 var->green.offset = 5;
129                 var->blue.offset = 0;
130                 var->red.length = 5;
131                 var->green.length = 6;
132                 var->blue.length = 5;
133                 var->transp.length = 0;
134                 var->transp.offset = 0;
135                 break;
136         case 24:
137                 var->red.offset = 16;
138                 var->green.offset = 8;
139                 var->blue.offset = 0;
140                 var->red.length = 8;
141                 var->green.length = 8;
142                 var->blue.length = 8;
143                 var->transp.length = 0;
144                 var->transp.offset = 0;
145                 break;
146         case 32:
147                 var->red.offset = 16;
148                 var->green.offset = 8;
149                 var->blue.offset = 0;
150                 var->red.length = 8;
151                 var->green.length = 8;
152                 var->blue.length = 8;
153                 var->transp.length = 8;
154                 var->transp.offset = 24;
155                 break;
156         default:
157                 DRM_ERROR("Invalid depth %d\n", depth);
158                 return -EINVAL; 
159         }
160
161         return 0;
162 }
163
164 static int nv50_fbcon_set_par(struct fb_info *info)
165 {
166         struct nv50_fbcon_par *par;
167         struct drm_framebuffer *drm_fb;
168         struct drm_connector *drm_connector;
169         struct drm_crtc *drm_crtc;
170         struct fb_var_screeninfo *var;
171         struct drm_display_mode *drm_mode = NULL, *t;
172         struct drm_device *dev;
173         int rval;
174         bool crtc_used[2] = {false, false};
175
176         NV50_DEBUG("\n");
177
178         if (!info) {
179                 DRM_ERROR("No fb_info\n");
180                 return -EINVAL;
181         }
182
183         par = info->par;
184
185         if (!par) {
186                 DRM_ERROR("No nv50_fbcon_par\n");
187                 return -EINVAL;
188         }
189
190         drm_fb = par->fb;
191         var = &info->var;
192         dev = par->dev;
193
194         if (!drm_fb || !var || !dev) {
195                 DRM_ERROR("No drm_fb, var or dev\n");
196                 return -EINVAL;
197         }
198
199         par->use_preferred_mode = false;
200
201         if (var->pixclock == -1 || !var->pixclock) {
202                 DRM_INFO("Using preferred mode.\n");
203                 par->use_preferred_mode = true;
204         }
205
206         /* FB setup */
207         switch (var->bits_per_pixel) {
208         case 16:
209                 drm_fb->depth = (var->green.length == 6) ? 16 : 15;
210                 break;
211         case 32:
212                 drm_fb->depth = (var->transp.length > 0) ? 32 : 24;
213                 break;
214         default:
215                 drm_fb->depth = var->bits_per_pixel;
216                 break;
217         }
218
219         drm_fb->bits_per_pixel = var->bits_per_pixel;
220
221         info->fix.line_length = drm_fb->pitch;
222         info->fix.smem_len = info->fix.line_length * drm_fb->height;
223         /* ignoring 8bpp for the moment */
224         info->fix.visual = FB_VISUAL_TRUECOLOR;
225
226         info->screen_size = info->fix.smem_len; /* ??? */
227
228         /* create a drm mode */
229         if (!par->use_preferred_mode) {
230                 drm_mode = drm_mode_create(dev);
231                 drm_mode->hdisplay = var->xres;
232                 drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin;
233                 drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len;
234                 drm_mode->htotal = drm_mode->hsync_end + var->left_margin;
235                 drm_mode->vdisplay = var->yres;
236                 drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin;
237                 drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len;
238                 drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin;
239                 drm_mode->clock = PICOS2KHZ(var->pixclock);
240                 drm_mode->vrefresh = drm_mode_vrefresh(drm_mode);
241                 drm_mode->flags = 0;
242                 drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
243                 drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
244
245                 drm_mode_set_name(drm_mode);
246                 drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V);
247         }
248
249         /* hook up crtc's */
250         list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
251                 enum drm_connector_status status;
252                 struct drm_mode_set mode_set;
253                 int crtc_count = 0;
254
255                 status = drm_connector->funcs->detect(drm_connector);
256
257                 if (status != connector_status_connected)
258                         continue;
259
260                 memset(&mode_set, 0, sizeof(struct drm_mode_set));
261
262                 /* set connector */
263                 mode_set.num_connectors = 1;
264                 mode_set.connectors = kzalloc(sizeof(struct drm_connector *), GFP_KERNEL);
265                 if (!mode_set.connectors) {
266                         rval = -ENOMEM;
267                         goto out;
268                 }
269                 mode_set.connectors[0] = drm_connector;
270
271                 /* set fb */
272                 list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
273                         break; /* first entry is the only entry */
274                 }
275                 mode_set.fb = drm_fb;
276
277                 /* set mode */
278                 if (par->use_preferred_mode) {
279                         /* find preferred mode */
280                         list_for_each_entry_safe(drm_mode, t, &drm_connector->modes, head) {
281                                 if (drm_mode->type & DRM_MODE_TYPE_PREFERRED)
282                                         break;
283                         }
284                 }
285                 mode_set.mode = drm_mode;
286
287                 /* choose crtc it already has, if possible */
288                 if (drm_connector->encoder) {
289                         struct drm_encoder *drm_encoder = drm_connector->encoder;
290
291                         if (drm_encoder->crtc) {
292                                 list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
293                                         if (drm_crtc == drm_encoder->crtc) {
294                                                 if (!crtc_used[crtc_count]) /* still available? */
295                                                         mode_set.crtc = drm_crtc;
296                                                 break;
297                                         }
298
299                                         crtc_count++;
300                                 }
301                         }
302                 }
303
304                 /* proceed as planned */
305                 if (mode_set.crtc) {
306                         mode_set.crtc->funcs->set_config(&mode_set);
307                         crtc_used[crtc_count] = true;
308                 }
309
310                 if (!mode_set.crtc) {
311                         crtc_count = 0; /* reset */
312
313                         /* choose a "random" crtc */
314                         list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
315                                 if (crtc_used[crtc_count]) {
316                                         crtc_count++;
317                                         continue;
318                                 }
319
320                                 /* found a crtc */
321                                 mode_set.crtc = drm_crtc;
322
323                                 break;
324                         }
325
326                         /* proceed as planned */
327                         if (mode_set.crtc) {
328                                 mode_set.crtc->funcs->set_config(&mode_set);
329                                 crtc_used[crtc_count] = true;
330                         }
331                 }
332
333                 kfree(mode_set.connectors);
334         }
335
336         return 0;
337
338 out:
339         return rval;
340 }
341
342 static struct fb_ops nv50_fb_ops = {
343         .owner = THIS_MODULE,
344         //.fb_open = nv50_fb_open,
345         //.fb_read = nv50_fb_read,
346         //.fb_write = nv50_fb_write,
347         //.fb_release = nv50_fb_release,
348         //.fb_ioctl = nv50_fb_ioctl,
349         .fb_check_var = nv50_fbcon_check_var,
350         .fb_set_par = nv50_fbcon_set_par,
351         .fb_setcolreg = nv50_fbcon_setcolreg,
352         .fb_fillrect = cfb_fillrect,
353         .fb_copyarea = cfb_copyarea,
354         .fb_imageblit = cfb_imageblit,
355         //.fb_pan_display = nv50_fb_pan_display,
356 };
357
358 static int nv50_fbcon_initial_config(struct drm_device *dev)
359 {
360         struct drm_connector *drm_connector;
361
362         struct drm_framebuffer *drm_fb = NULL;
363         struct drm_mode_fb_cmd drm_fb_cmd;
364         enum drm_connector_status status;
365         uint32_t max_width = 0, max_height = 0, pitch = 0;
366         struct mem_block *block;
367         struct drm_file *file_priv;
368         uint32_t flags;
369         int rval = 0;
370
371         list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
372                 status = drm_connector->funcs->detect(drm_connector);
373
374                 /* find the framebuffer size */
375                 if (status == connector_status_connected) {
376                         struct drm_display_mode *mode, *t;
377                         list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
378                                 if (mode->type & DRM_MODE_TYPE_PREFERRED) {
379                                         if (mode->hdisplay > max_width)
380                                                 max_width = mode->hdisplay;
381                                         if (mode->vdisplay > max_height)
382                                                 max_height = mode->vdisplay;
383                                 }
384                         }
385                 }
386         }
387
388         /* allocate framebuffer */
389         file_priv = kzalloc(sizeof(struct drm_file), GFP_KERNEL);
390         if (!file_priv) {
391                 rval = -ENOMEM;
392                 goto out;
393         }
394
395         pitch = (max_width + 63) & ~63;
396         pitch *= 4; /* TODO */
397
398         flags = NOUVEAU_MEM_FB | NOUVEAU_MEM_MAPPED;
399
400         /* Any file_priv should do as it's pointer is used as identification. */
401         block = nouveau_mem_alloc(dev, 0, pitch * max_height, flags, file_priv);
402         if (!block) {
403                 rval = -ENOMEM;
404                 goto out;
405         }
406
407         memset(&drm_fb_cmd, 0, sizeof(struct drm_mode_fb_cmd));
408
409         drm_fb_cmd.width = max_width;
410         drm_fb_cmd.height = max_height;
411         drm_fb_cmd.pitch = pitch;
412         drm_fb_cmd.bpp = 32; /* TODO */
413         drm_fb_cmd.handle = block->map_handle;
414         drm_fb_cmd.depth = 24; /* TODO */
415
416         drm_fb = dev->mode_config.funcs->fb_create(dev, file_priv, &drm_fb_cmd);
417         if (!drm_fb) {
418                 rval = -EINVAL;
419                 goto out;
420         }
421
422         list_add(&drm_fb->filp_head, &dev->mode_config.fb_kernel_list);
423
424         return 0;
425
426 out:
427         if (file_priv)
428                 kfree(file_priv);
429         if (drm_fb)
430                 drm_fb->funcs->destroy(drm_fb);
431
432         return rval;
433 }
434
435 /*
436  * Single framebuffer, ideally operating in clone mode across various connectors.
437  */
438 int nv50_fbcon_init(struct drm_device *dev)
439 {
440         struct drm_nouveau_private *dev_priv = dev->dev_private;
441         struct fb_info *info;
442         struct nv50_fbcon_par *par;
443         struct device *device = &dev->pdev->dev;
444         struct drm_framebuffer *drm_fb;
445         struct mem_block *block;
446         void __iomem *fb = NULL;
447         int rval;
448
449         rval = nv50_fbcon_initial_config(dev);
450         if (rval != 0) {
451                 DRM_ERROR("nv50_fbcon_initial_config failed\n");
452                 return rval;
453         }
454
455         list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
456                 break; /* first entry is the only entry */
457         }
458
459         if (!drm_fb) {
460                 DRM_ERROR("no drm_fb found\n");
461                 return -EINVAL;
462         }
463
464         block = find_block_by_handle(dev_priv->fb_heap, drm_fb->mm_handle);
465         if (!block) {
466                 DRM_ERROR("can't find mem_block\n");
467                 return -EINVAL;
468         }
469
470         info = framebuffer_alloc(sizeof(struct nv50_fbcon_par), device);
471         if (!info) {
472                 DRM_ERROR("framebuffer_alloc failed\n");
473                 return -EINVAL;
474         }
475
476         par = info->par;
477
478         strcpy(info->fix.id, "nv50drmfb");
479         info->fix.type = FB_TYPE_PACKED_PIXELS;
480         info->fix.visual = FB_VISUAL_TRUECOLOR;
481         info->fix.type_aux = 0;
482         info->fix.xpanstep = 0; /* 1 is doing it in hw */
483         info->fix.ypanstep = 0;
484         info->fix.ywrapstep = 0;
485         info->fix.accel = FB_ACCEL_NONE;
486         info->fix.type_aux = 0;
487
488         info->flags = FBINFO_DEFAULT;
489
490         info->fbops = &nv50_fb_ops;
491
492         info->fix.line_length = drm_fb->pitch;
493         info->fix.smem_start = dev_priv->fb_phys + block->start;
494         info->fix.smem_len = info->fix.line_length * drm_fb->height;
495
496         info->flags = FBINFO_DEFAULT;
497
498         fb = ioremap(dev_priv->fb_phys + block->start, block->size);
499         if (!fb) {
500                 DRM_ERROR("Unable to ioremap framebuffer\n");
501                 return -EINVAL;
502         }
503
504         info->screen_base = fb;
505         info->screen_size = info->fix.smem_len; /* FIXME */
506
507         info->pseudo_palette = drm_fb->pseudo_palette;
508         info->var.xres_virtual = drm_fb->width;
509         info->var.yres_virtual = drm_fb->height;
510         info->var.bits_per_pixel = drm_fb->bits_per_pixel;
511         info->var.xoffset = 0;
512         info->var.yoffset = 0;
513         info->var.activate = FB_ACTIVATE_NOW;
514         info->var.height = -1;
515         info->var.width = -1;
516
517         /* TODO: improve this */
518         info->var.xres = drm_fb->width;
519         info->var.yres = drm_fb->height;
520
521         info->fix.mmio_start = drm_get_resource_start(dev, 0);
522         info->fix.mmio_len = drm_get_resource_len(dev, 0);
523
524         DRM_DEBUG("fb depth is %d\n", drm_fb->depth);
525         DRM_DEBUG("   pitch is %d\n", drm_fb->pitch);
526
527         switch(drm_fb->depth) {
528         case 15:
529                 info->var.red.offset = 10;
530                 info->var.green.offset = 5;
531                 info->var.blue.offset = 0;
532                 info->var.red.length = 5;
533                 info->var.green.length = 5;
534                 info->var.blue.length = 5;
535                 info->var.transp.offset = 15;
536                 info->var.transp.length = 1;
537                 break;
538         case 16:
539                 info->var.red.offset = 11;
540                 info->var.green.offset = 5;
541                 info->var.blue.offset = 0;
542                 info->var.red.length = 5;
543                 info->var.green.length = 6;
544                 info->var.blue.length = 5;
545                 info->var.transp.offset = 0;
546                 break;
547         case 24:
548                 info->var.red.offset = 16;
549                 info->var.green.offset = 8;
550                 info->var.blue.offset = 0;
551                 info->var.red.length = 8;
552                 info->var.green.length = 8;
553                 info->var.blue.length = 8;
554                 info->var.transp.offset = 0;
555                 info->var.transp.length = 0;
556                 break;
557         case 32:
558                 info->var.red.offset = 16;
559                 info->var.green.offset = 8;
560                 info->var.blue.offset = 0;
561                 info->var.red.length = 8;
562                 info->var.green.length = 8;
563                 info->var.blue.length = 8;
564                 info->var.transp.offset = 24;
565                 info->var.transp.length = 8;
566                 break;
567         default:
568                 break;
569         }
570
571         drm_fb->fbdev = info;
572         par->dev = dev;
573         par->fb = drm_fb;
574
575         register_framebuffer(info);
576
577         DRM_INFO("nv50drmfb initialised\n");
578
579         return 0;
580 }
581
582 int nv50_fbcon_destroy(struct drm_device *dev)
583 {
584         struct drm_nouveau_private *dev_priv = dev->dev_private;
585         struct drm_framebuffer *drm_fb;
586         struct fb_info *info;
587         struct mem_block *block;
588         struct drm_file *file_priv;
589
590         list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
591                 break; /* first entry is the only entry */
592         }
593
594         if (!drm_fb) {
595                 DRM_ERROR("No framebuffer to destroy\n");
596                 return -EINVAL;
597         }
598
599         info = drm_fb->fbdev;
600         if (!info) {
601                 DRM_ERROR("No fb_info\n");
602                 return -EINVAL;
603         }
604
605         unregister_framebuffer(info);
606
607         block = find_block_by_handle(dev_priv->fb_heap, drm_fb->mm_handle);
608         if (!block) {
609                 DRM_ERROR("can't find mem_block\n");
610                 return -EINVAL;
611         }
612
613         /* we need to free this after memory is freed */
614         file_priv = block->file_priv;
615
616         /* free memory */
617         nouveau_mem_free(dev, block);
618
619         if (file_priv) {
620                 kfree(file_priv);
621                 file_priv = NULL;
622         }
623
624         framebuffer_release(info);
625
626         return 0;
627 }