drm/fb: add more correct 8/16/24/32 bpp fb support.
[safe/jmp/linux-2.6] / drivers / gpu / drm / drm_fb_helper.c
index 3746bd2..23dc9c1 100644 (file)
@@ -454,6 +454,54 @@ out_free:
 }
 EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
 
+static void setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
+                    u16 blue, u16 regno, struct fb_info *info)
+{
+       struct drm_fb_helper *fb_helper = info->par;
+       struct drm_framebuffer *fb = fb_helper->fb;
+       int pindex;
+
+       pindex = regno;
+
+       if (fb->bits_per_pixel == 16) {
+               pindex = regno << 3;
+
+               if (fb->depth == 16 && regno > 63)
+                       return;
+               if (fb->depth == 15 && regno > 31)
+                       return;
+
+               if (fb->depth == 16) {
+                       u16 r, g, b;
+                       int i;
+                       if (regno < 32) {
+                               for (i = 0; i < 8; i++)
+                                       fb_helper->funcs->gamma_set(crtc, red,
+                                               green, blue, pindex + i);
+                       }
+
+                       fb_helper->funcs->gamma_get(crtc, &r,
+                                                   &g, &b,
+                                                   pindex >> 1);
+
+                       for (i = 0; i < 4; i++)
+                               fb_helper->funcs->gamma_set(crtc, r,
+                                                           green, b,
+                                                           (pindex >> 1) + i);
+               }
+       }
+
+       if (fb->depth != 16)
+               fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
+
+       if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+               ((u32 *) fb->pseudo_palette)[regno] =
+                       (regno << info->var.red.offset) |
+                       (regno << info->var.green.offset) |
+                       (regno << info->var.blue.offset);
+       }
+}
+
 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 {
        struct drm_fb_helper *fb_helper = info->par;
@@ -488,7 +536,7 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
                        if (transp)
                                htransp = *transp++;
 
-                       fb_helper->funcs->gamma_set(crtc, hred, hgreen, hblue, start++);
+                       setcolreg(crtc, hred, hgreen, hblue, start++, info);
                }
                crtc_funcs->load_lut(crtc);
        }
@@ -508,9 +556,11 @@ int drm_fb_helper_setcolreg(unsigned regno,
        struct drm_crtc *crtc;
        int i;
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               struct drm_framebuffer *fb = fb_helper->fb;
+       if (regno > 255)
+               return 1;
 
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
                for (i = 0; i < fb_helper->crtc_count; i++) {
                        if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
                                break;
@@ -518,36 +568,9 @@ int drm_fb_helper_setcolreg(unsigned regno,
                if (i == fb_helper->crtc_count)
                        continue;
 
-               if (regno > 255)
-                       return 1;
-
-               if (fb->depth == 8) {
-                       fb_helper->funcs->gamma_set(crtc, red, green, blue, regno);
-                       return 0;
-               }
 
-               if (regno < 16) {
-                       u32 *pal = fb->pseudo_palette;
-                       switch (fb->depth) {
-                       case 15:
-                               pal[regno] = ((red & 0xf800) >> 1) |
-                                       ((green & 0xf800) >>  6) |
-                                       ((blue & 0xf800) >> 11);
-                               break;
-                       case 16:
-                               pal[regno] = (red & 0xf800) |
-                                       ((green & 0xfc00) >>  5) |
-                                       ((blue  & 0xf800) >> 11);
-                               break;
-                       case 24:
-                       case 32:
-                               pal[regno] =
-                                       (((red >> 8) & 0xff) << info->var.red.offset) |
-                                       (((green >> 8) & 0xff) << info->var.green.offset) |
-                                       (((blue >> 8) & 0xff) << info->var.blue.offset);
-                               break;
-                       }
-               }
+               setcolreg(crtc, red, green, blue, regno, info);
+               crtc_funcs->load_lut(crtc);
        }
        return 0;
 }
@@ -717,6 +740,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
 EXPORT_SYMBOL(drm_fb_helper_pan_display);
 
 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
+                                 int preferred_bpp,
                                  int (*fb_create)(struct drm_device *dev,
                                                   uint32_t fb_width,
                                                   uint32_t fb_height,
@@ -739,6 +763,11 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
        struct drm_fb_helper *fb_helper;
        uint32_t surface_depth = 24, surface_bpp = 32;
 
+       /* if driver picks 8 or 16 by default use that
+          for both depth/bpp */
+       if (preferred_bpp != surface_bpp) {
+               surface_depth = surface_bpp = preferred_bpp;
+       }
        /* first up get a count of crtcs now in use and new min/maxes width/heights */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
@@ -899,7 +928,7 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 {
        info->fix.type = FB_TYPE_PACKED_PIXELS;
        info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
-               FB_VISUAL_TRUECOLOR;
+               FB_VISUAL_DIRECTCOLOR;
        info->fix.type_aux = 0;
        info->fix.xpanstep = 1; /* doing it in hw */
        info->fix.ypanstep = 1; /* doing it in hw */