2 * Copyright (C) 2008 Maarten Maathuis.
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:
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.
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.
27 #include <linux/module.h>
28 #include <linux/kernel.h>
29 #include <linux/errno.h>
30 #include <linux/string.h>
32 #include <linux/tty.h>
33 #include <linux/slab.h>
34 #include <linux/delay.h>
36 #include <linux/init.h>
38 #include "nv50_fbcon.h"
40 static int nv50_fbcon_setcolreg(unsigned regno, unsigned red, unsigned green,
41 unsigned blue, unsigned transp,
44 struct nv50_fbcon_par *par = info->par;
45 struct drm_device *dev = par->dev;
46 struct drm_framebuffer *drm_fb;
48 list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
52 /* TODO: 8 bit support */
54 switch (drm_fb->depth) {
56 drm_fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
57 ((green & 0xf800) >> 6) |
58 ((blue & 0xf800) >> 11);
61 drm_fb->pseudo_palette[regno] = (red & 0xf800) |
62 ((green & 0xfc00) >> 5) |
63 ((blue & 0xf800) >> 11);
67 drm_fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
69 ((blue & 0xff00) >> 8);
77 static int nv50_fbcon_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
79 struct nv50_fbcon_par *par = info->par;
80 struct drm_framebuffer *drm_fb = par->fb;
85 if (!var || !drm_fb || !info) {
86 DRM_ERROR("No var, drm_fb or info\n");
89 par->use_preferred_mode = false;
91 if (var->pixclock == -1 || !var->pixclock) {
92 DRM_INFO("Using preferred mode.\n");
93 par->use_preferred_mode = true;
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");
103 switch (var->bits_per_pixel) {
105 depth = (var->green.length == 6) ? 16 : 15;
108 depth = (var->transp.length > 0) ? 32 : 24;
111 depth = var->bits_per_pixel;
117 var->red.offset = 10;
118 var->green.offset = 5;
119 var->blue.offset = 0;
121 var->green.length = 5;
122 var->blue.length = 5;
123 var->transp.length = 1;
124 var->transp.offset = 15;
127 var->red.offset = 11;
128 var->green.offset = 5;
129 var->blue.offset = 0;
131 var->green.length = 6;
132 var->blue.length = 5;
133 var->transp.length = 0;
134 var->transp.offset = 0;
137 var->red.offset = 16;
138 var->green.offset = 8;
139 var->blue.offset = 0;
141 var->green.length = 8;
142 var->blue.length = 8;
143 var->transp.length = 0;
144 var->transp.offset = 0;
147 var->red.offset = 16;
148 var->green.offset = 8;
149 var->blue.offset = 0;
151 var->green.length = 8;
152 var->blue.length = 8;
153 var->transp.length = 8;
154 var->transp.offset = 24;
157 DRM_ERROR("Invalid depth %d\n", depth);
164 static int nv50_fbcon_set_par(struct fb_info *info)
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;
174 bool crtc_used[2] = {false, false};
179 DRM_ERROR("No fb_info\n");
186 DRM_ERROR("No nv50_fbcon_par\n");
194 if (!drm_fb || !var || !dev) {
195 DRM_ERROR("No drm_fb, var or dev\n");
199 par->use_preferred_mode = false;
201 if (var->pixclock == -1 || !var->pixclock) {
202 DRM_INFO("Using preferred mode.\n");
203 par->use_preferred_mode = true;
207 switch (var->bits_per_pixel) {
209 drm_fb->depth = (var->green.length == 6) ? 16 : 15;
212 drm_fb->depth = (var->transp.length > 0) ? 32 : 24;
215 drm_fb->depth = var->bits_per_pixel;
219 drm_fb->bits_per_pixel = var->bits_per_pixel;
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;
226 info->screen_size = info->fix.smem_len; /* ??? */
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);
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;
245 drm_mode_set_name(drm_mode);
246 drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V);
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;
255 status = drm_connector->funcs->detect(drm_connector);
257 if (status != connector_status_connected)
260 memset(&mode_set, 0, sizeof(struct drm_mode_set));
263 mode_set.num_connectors = 1;
264 mode_set.connectors = kzalloc(sizeof(struct drm_connector *), GFP_KERNEL);
265 if (!mode_set.connectors) {
269 mode_set.connectors[0] = drm_connector;
272 list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
273 break; /* first entry is the only entry */
275 mode_set.fb = drm_fb;
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)
285 mode_set.mode = drm_mode;
287 /* choose crtc it already has, if possible */
288 if (drm_connector->encoder) {
289 struct drm_encoder *drm_encoder = drm_connector->encoder;
291 if (drm_encoder->crtc) {
292 list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
295 if (drm_crtc == drm_encoder->crtc) {
296 if (!crtc_used[crtc_count]) /* still available? */
297 mode_set.crtc = drm_crtc;
304 /* proceed as planned */
306 mode_set.crtc->funcs->set_config(&mode_set);
307 crtc_used[crtc_count] = true;
310 if (!mode_set.crtc) {
311 crtc_count = 0; /* reset */
313 /* choose a "random" crtc */
314 list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
315 if (crtc_used[crtc_count]) {
321 mode_set.crtc = drm_crtc;
326 /* proceed as planned */
328 mode_set.crtc->funcs->set_config(&mode_set);
329 crtc_used[crtc_count] = true;
333 kfree(mode_set.connectors);
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,
358 static int nv50_fbcon_initial_config(struct drm_device *dev)
360 struct drm_connector *drm_connector;
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;
371 list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
372 status = drm_connector->funcs->detect(drm_connector);
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;
388 /* allocate framebuffer */
389 file_priv = kzalloc(sizeof(struct drm_file), GFP_KERNEL);
395 pitch = (max_width + 63) & ~63;
396 pitch *= 4; /* TODO */
398 flags = NOUVEAU_MEM_FB | NOUVEAU_MEM_MAPPED;
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);
407 memset(&drm_fb_cmd, 0, sizeof(struct drm_mode_fb_cmd));
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 */
416 drm_fb = dev->mode_config.funcs->fb_create(dev, file_priv, &drm_fb_cmd);
422 list_add(&drm_fb->filp_head, &dev->mode_config.fb_kernel_list);
430 drm_fb->funcs->destroy(drm_fb);
436 * Single framebuffer, ideally operating in clone mode across various connectors.
438 int nv50_fbcon_init(struct drm_device *dev)
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;
449 rval = nv50_fbcon_initial_config(dev);
451 DRM_ERROR("nv50_fbcon_initial_config failed\n");
455 list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
456 break; /* first entry is the only entry */
460 DRM_ERROR("no drm_fb found\n");
464 block = find_block_by_handle(dev_priv->fb_heap, drm_fb->mm_handle);
466 DRM_ERROR("can't find mem_block\n");
470 info = framebuffer_alloc(sizeof(struct nv50_fbcon_par), device);
472 DRM_ERROR("framebuffer_alloc failed\n");
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;
488 info->flags = FBINFO_DEFAULT;
490 info->fbops = &nv50_fb_ops;
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;
496 info->flags = FBINFO_DEFAULT;
498 fb = ioremap(dev_priv->fb_phys + block->start, block->size);
500 DRM_ERROR("Unable to ioremap framebuffer\n");
504 info->screen_base = fb;
505 info->screen_size = info->fix.smem_len; /* FIXME */
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;
517 /* TODO: improve this */
518 info->var.xres = drm_fb->width;
519 info->var.yres = drm_fb->height;
521 info->fix.mmio_start = drm_get_resource_start(dev, 0);
522 info->fix.mmio_len = drm_get_resource_len(dev, 0);
524 DRM_DEBUG("fb depth is %d\n", drm_fb->depth);
525 DRM_DEBUG(" pitch is %d\n", drm_fb->pitch);
527 switch(drm_fb->depth) {
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;
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;
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;
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;
571 drm_fb->fbdev = info;
575 register_framebuffer(info);
577 DRM_INFO("nv50drmfb initialised\n");
582 int nv50_fbcon_destroy(struct drm_device *dev)
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;
590 list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
591 break; /* first entry is the only entry */
595 DRM_ERROR("No framebuffer to destroy\n");
599 info = drm_fb->fbdev;
601 DRM_ERROR("No fb_info\n");
605 unregister_framebuffer(info);
607 block = find_block_by_handle(dev_priv->fb_heap, drm_fb->mm_handle);
609 DRM_ERROR("can't find mem_block\n");
613 /* we need to free this after memory is freed */
614 file_priv = block->file_priv;
617 nouveau_mem_free(dev, block);
624 framebuffer_release(info);