Commit b8c00ac5 authored by Dave Airlie's avatar Dave Airlie

drm/fb: add more correct 8/16/24/32 bpp fb support.

The previous patches had some unwanted side effects, I've fixed
the lack of 32bpp working, and fixed up 16bpp so it should also work.

this also adds the interface to allow the driver to set a preferred
console depth so for example low memory rn50 can set it to 8bpp.
It also catches 24bpp on cards that can't do it and forces 32bpp.

Tested on r100/r600/i945.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 068143d3
...@@ -454,6 +454,54 @@ int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, ...@@ -454,6 +454,54 @@ int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count,
} }
EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); 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) int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
{ {
struct drm_fb_helper *fb_helper = info->par; 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) ...@@ -488,7 +536,7 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
if (transp) if (transp)
htransp = *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); crtc_funcs->load_lut(crtc);
} }
...@@ -508,9 +556,11 @@ int drm_fb_helper_setcolreg(unsigned regno, ...@@ -508,9 +556,11 @@ int drm_fb_helper_setcolreg(unsigned regno,
struct drm_crtc *crtc; struct drm_crtc *crtc;
int i; int i;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (regno > 255)
struct drm_framebuffer *fb = fb_helper->fb; 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++) { for (i = 0; i < fb_helper->crtc_count; i++) {
if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
break; break;
...@@ -518,36 +568,9 @@ int drm_fb_helper_setcolreg(unsigned regno, ...@@ -518,36 +568,9 @@ int drm_fb_helper_setcolreg(unsigned regno,
if (i == fb_helper->crtc_count) if (i == fb_helper->crtc_count)
continue; continue;
if (regno > 255)
return 1;
if (fb->depth == 8) { setcolreg(crtc, red, green, blue, regno, info);
fb_helper->funcs->gamma_set(crtc, red, green, blue, regno); crtc_funcs->load_lut(crtc);
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;
}
}
} }
return 0; return 0;
} }
...@@ -717,6 +740,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, ...@@ -717,6 +740,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
EXPORT_SYMBOL(drm_fb_helper_pan_display); EXPORT_SYMBOL(drm_fb_helper_pan_display);
int drm_fb_helper_single_fb_probe(struct drm_device *dev, int drm_fb_helper_single_fb_probe(struct drm_device *dev,
int preferred_bpp,
int (*fb_create)(struct drm_device *dev, int (*fb_create)(struct drm_device *dev,
uint32_t fb_width, uint32_t fb_width,
uint32_t fb_height, uint32_t fb_height,
...@@ -739,6 +763,11 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev, ...@@ -739,6 +763,11 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
struct drm_fb_helper *fb_helper; struct drm_fb_helper *fb_helper;
uint32_t surface_depth = 24, surface_bpp = 32; 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 */ /* 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) { list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; 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, ...@@ -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.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
FB_VISUAL_TRUECOLOR; FB_VISUAL_DIRECTCOLOR;
info->fix.type_aux = 0; info->fix.type_aux = 0;
info->fix.xpanstep = 1; /* doing it in hw */ info->fix.xpanstep = 1; /* doing it in hw */
info->fix.ypanstep = 1; /* doing it in hw */ info->fix.ypanstep = 1; /* doing it in hw */
......
...@@ -2922,6 +2922,16 @@ void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, ...@@ -2922,6 +2922,16 @@ void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
intel_crtc->lut_b[regno] = blue >> 8; intel_crtc->lut_b[regno] = blue >> 8;
} }
void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, int regno)
{
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
*red = intel_crtc->lut_r[regno] << 8;
*green = intel_crtc->lut_g[regno] << 8;
*blue = intel_crtc->lut_b[regno] << 8;
}
static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, uint32_t size) u16 *blue, uint32_t size)
{ {
......
...@@ -173,6 +173,8 @@ extern int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc); ...@@ -173,6 +173,8 @@ extern int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc);
extern void intelfb_restore(void); extern void intelfb_restore(void);
extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
u16 blue, int regno); u16 blue, int regno);
extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, int regno);
extern int intel_framebuffer_create(struct drm_device *dev, extern int intel_framebuffer_create(struct drm_device *dev,
struct drm_mode_fb_cmd *mode_cmd, struct drm_mode_fb_cmd *mode_cmd,
......
...@@ -65,6 +65,7 @@ static struct fb_ops intelfb_ops = { ...@@ -65,6 +65,7 @@ static struct fb_ops intelfb_ops = {
static struct drm_fb_helper_funcs intel_fb_helper_funcs = { static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
.gamma_set = intel_crtc_fb_gamma_set, .gamma_set = intel_crtc_fb_gamma_set,
.gamma_get = intel_crtc_fb_gamma_get,
}; };
...@@ -124,6 +125,10 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, ...@@ -124,6 +125,10 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
struct device *device = &dev->pdev->dev; struct device *device = &dev->pdev->dev;
int size, ret, mmio_bar = IS_I9XX(dev) ? 0 : 1; int size, ret, mmio_bar = IS_I9XX(dev) ? 0 : 1;
/* we don't do packed 24bpp */
if (surface_bpp == 24)
surface_bpp = 32;
mode_cmd.width = surface_width; mode_cmd.width = surface_width;
mode_cmd.height = surface_height; mode_cmd.height = surface_height;
...@@ -245,7 +250,7 @@ int intelfb_probe(struct drm_device *dev) ...@@ -245,7 +250,7 @@ int intelfb_probe(struct drm_device *dev)
int ret; int ret;
DRM_DEBUG("\n"); DRM_DEBUG("\n");
ret = drm_fb_helper_single_fb_probe(dev, intelfb_create); ret = drm_fb_helper_single_fb_probe(dev, 32, intelfb_create);
return ret; return ret;
} }
EXPORT_SYMBOL(intelfb_probe); EXPORT_SYMBOL(intelfb_probe);
......
...@@ -106,24 +106,33 @@ void radeon_crtc_load_lut(struct drm_crtc *crtc) ...@@ -106,24 +106,33 @@ void radeon_crtc_load_lut(struct drm_crtc *crtc)
legacy_crtc_load_lut(crtc); legacy_crtc_load_lut(crtc);
} }
/** Sets the color ramps on behalf of RandR */ /** Sets the color ramps on behalf of fbcon */
void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
u16 blue, int regno) u16 blue, int regno)
{ {
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
if (regno == 0)
DRM_DEBUG("gamma set %d\n", radeon_crtc->crtc_id);
radeon_crtc->lut_r[regno] = red >> 6; radeon_crtc->lut_r[regno] = red >> 6;
radeon_crtc->lut_g[regno] = green >> 6; radeon_crtc->lut_g[regno] = green >> 6;
radeon_crtc->lut_b[regno] = blue >> 6; radeon_crtc->lut_b[regno] = blue >> 6;
} }
/** Gets the color ramps on behalf of fbcon */
void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, int regno)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
*red = radeon_crtc->lut_r[regno] << 6;
*green = radeon_crtc->lut_g[regno] << 6;
*blue = radeon_crtc->lut_b[regno] << 6;
}
static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, uint32_t size) u16 *blue, uint32_t size)
{ {
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
int i, j; int i;
if (size != 256) { if (size != 256) {
return; return;
...@@ -132,24 +141,12 @@ static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, ...@@ -132,24 +141,12 @@ static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
return; return;
} }
if (crtc->fb->depth == 16) { /* userspace palettes are always correct as is */
for (i = 0; i < 64; i++) {
if (i <= 31) {
for (j = 0; j < 8; j++) {
radeon_crtc->lut_r[i * 8 + j] = red[i] >> 6;
radeon_crtc->lut_b[i * 8 + j] = blue[i] >> 6;
}
}
for (j = 0; j < 4; j++)
radeon_crtc->lut_g[i * 4 + j] = green[i] >> 6;
}
} else {
for (i = 0; i < 256; i++) { for (i = 0; i < 256; i++) {
radeon_crtc->lut_r[i] = red[i] >> 6; radeon_crtc->lut_r[i] = red[i] >> 6;
radeon_crtc->lut_g[i] = green[i] >> 6; radeon_crtc->lut_g[i] = green[i] >> 6;
radeon_crtc->lut_b[i] = blue[i] >> 6; radeon_crtc->lut_b[i] = blue[i] >> 6;
} }
}
radeon_crtc_load_lut(crtc); radeon_crtc_load_lut(crtc);
} }
......
...@@ -124,6 +124,7 @@ static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bo ...@@ -124,6 +124,7 @@ static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bo
static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
.gamma_set = radeon_crtc_fb_gamma_set, .gamma_set = radeon_crtc_fb_gamma_set,
.gamma_get = radeon_crtc_fb_gamma_get,
}; };
int radeonfb_create(struct drm_device *dev, int radeonfb_create(struct drm_device *dev,
...@@ -151,6 +152,11 @@ int radeonfb_create(struct drm_device *dev, ...@@ -151,6 +152,11 @@ int radeonfb_create(struct drm_device *dev,
mode_cmd.width = surface_width; mode_cmd.width = surface_width;
mode_cmd.height = surface_height; mode_cmd.height = surface_height;
/* avivo can't scanout real 24bpp */
if ((surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
surface_bpp = 32;
mode_cmd.bpp = surface_bpp; mode_cmd.bpp = surface_bpp;
/* need to align pitch with crtc limits */ /* need to align pitch with crtc limits */
mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8); mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8);
...@@ -315,7 +321,7 @@ int radeon_parse_options(char *options) ...@@ -315,7 +321,7 @@ int radeon_parse_options(char *options)
int radeonfb_probe(struct drm_device *dev) int radeonfb_probe(struct drm_device *dev)
{ {
return drm_fb_helper_single_fb_probe(dev, &radeonfb_create); return drm_fb_helper_single_fb_probe(dev, 32, &radeonfb_create);
} }
int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
......
...@@ -407,6 +407,8 @@ extern void ...@@ -407,6 +407,8 @@ extern void
radeon_combios_encoder_dpms_scratch_regs(struct drm_encoder *encoder, bool on); radeon_combios_encoder_dpms_scratch_regs(struct drm_encoder *encoder, bool on);
extern void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, extern void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
u16 blue, int regno); u16 blue, int regno);
extern void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, int regno);
struct drm_framebuffer *radeon_framebuffer_create(struct drm_device *dev, struct drm_framebuffer *radeon_framebuffer_create(struct drm_device *dev,
struct drm_mode_fb_cmd *mode_cmd, struct drm_mode_fb_cmd *mode_cmd,
struct drm_gem_object *obj); struct drm_gem_object *obj);
......
...@@ -39,6 +39,8 @@ struct drm_fb_helper_crtc { ...@@ -39,6 +39,8 @@ struct drm_fb_helper_crtc {
struct drm_fb_helper_funcs { struct drm_fb_helper_funcs {
void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green,
u16 blue, int regno); u16 blue, int regno);
void (*gamma_get)(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, int regno);
}; };
/* mode specified on the command line */ /* mode specified on the command line */
...@@ -71,6 +73,7 @@ struct drm_fb_helper { ...@@ -71,6 +73,7 @@ struct drm_fb_helper {
}; };
int drm_fb_helper_single_fb_probe(struct drm_device *dev, int drm_fb_helper_single_fb_probe(struct drm_device *dev,
int preferred_bpp,
int (*fb_create)(struct drm_device *dev, int (*fb_create)(struct drm_device *dev,
uint32_t fb_width, uint32_t fb_width,
uint32_t fb_height, uint32_t fb_height,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment