This patch reimplements fb_blank(), fb_pan_display(), fb_set_cmap() and fb_set_var() for fbconv helpers. The goal is to have all calls to driver callback functions located within fbconv and to reduce the amount of contained work to a minimum. Some noteable differences to fbdev include: * Code related to fbcon has been left out. Console support is emulated by DRM and the drivers don't interact directly with it. * No events are sent out. As the fbconv helpers are not part of the fbdev framework, there are no event listeners anyway. * Code related to ioctl and user-space has been left out as well. User-space interfaces are provided by DRM. * Error messages have been added. Signed-off-by: Thomas Zimmermann <tzimmermann@xxxxxxx> --- drivers/gpu/drm/drm_fbconv_helper.c | 240 +++++++++++++++++++++++++--- 1 file changed, 220 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index ca8b43c91266..f7f247e30a3d 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -737,6 +737,55 @@ static const struct drm_connector_funcs connector_funcs = { * Colormap updates */ +static int drm_fbconv_set_cmap(struct fb_cmap *cmap, struct fb_info *fb_info) +{ + int i, start, res; + u16 *red, *green, *blue, *transp; + u_int hred, hgreen, hblue, htransp = 0xffff; + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + start = cmap->start; + + if (start < 0 || (!fb_info->fbops->fb_setcolreg && + !fb_info->fbops->fb_setcmap)) { + DRM_ERROR("fbconv: Palette not supported.\n"); + return -EINVAL; + } + + if (fb_info->fbops->fb_setcmap) { + res = fb_info->fbops->fb_setcmap(cmap, fb_info); + if (res) { + DRM_ERROR("fbconv: fbops->fb_setcmap() failed: %d\n", + res); + return res; + } + } else { + for (i = 0; i < cmap->len; i++) { + hred = *red++; + hgreen = *green++; + hblue = *blue++; + if (transp) + htransp = *transp++; + res = fb_info->fbops->fb_setcolreg(start++, + hred, hgreen, hblue, + htransp, fb_info); + if (res) { + DRM_ERROR("fbconv: fbops->fb_setcolreg() failed: %d\n", + res); + /* cmap handling is a mess; don't err here */ + break; + } + } + } + + fb_copy_cmap(cmap, &fb_info->cmap); + + return 0; +} + /* provides a default colormap for palette modes */ static int create_palette_cmap(struct fb_cmap *cmap, const struct fb_var_screeninfo *fb_var) @@ -856,11 +905,9 @@ static int set_cmap(struct fb_info *fb_info) if (ret) return ret; - ret = fb_set_cmap(&cmap, fb_info); - if (ret) { - DRM_ERROR("fbconv: fb_set_cmap() failed: %d\n", ret); + ret = drm_fbconv_set_cmap(&cmap, fb_info); + if (ret) goto err_fb_dealloc_cmap; - } fb_dealloc_cmap(&cmap); return 0; @@ -891,7 +938,7 @@ static int drm_fbconv_update_fb_var_screeninfo_from_framebuffer( /* Our virtual screen covers all the graphics memory (sans some * trailing bytes). This allows for setting the scanout buffer's - * address with fb_pan_display(). + * address with drm_fbconv_pan_display(). */ width = fb->pitches[0]; @@ -937,6 +984,165 @@ static int drm_fbconv_update_fb_var_screeninfo_from_simple_display_pipe( return 0; } +static int drm_fbconv_blank(struct fb_info *fb_info, int blank) +{ + int ret = -EINVAL; + + if (fb_info->fbops->fb_blank) { + ret = fb_info->fbops->fb_blank(blank, fb_info); + if (ret) { + DRM_ERROR("fbconv: fbops->fb_blank() failed: %d\n", + ret); + } + } + return ret; +} + +static int drm_fbconv_pan_display(struct fb_info *fb_info, + struct fb_var_screeninfo *var) +{ + struct fb_fix_screeninfo *fix = &fb_info->fix; + unsigned int yres = fb_info->var.yres; + int err; + + if (var->yoffset > 0) { + if (var->vmode & FB_VMODE_YWRAP) { + if (!fix->ywrapstep || + (var->yoffset % fix->ywrapstep)) { + DRM_ERROR("fbconv: Invalid fix->ywrapstep: %d\n", + fix->ywrapstep); + return -EINVAL; + } + yres = 0; + } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep)) { + DRM_ERROR("fbconv: Invalid fix->ypanstep: %d\n", + fix->ypanstep); + return -EINVAL; + } + } + + if (var->xoffset > 0) { + if (!fix->xpanstep || (var->xoffset % fix->xpanstep)) { + DRM_ERROR("fbconv: Invalid fix->xpanstep: %d\n", + fix->xpanstep); + return -EINVAL; + } + } + + if (!fb_info->fbops->fb_pan_display || + var->yoffset > fb_info->var.yres_virtual - yres || + var->xoffset > fb_info->var.xres_virtual - fb_info->var.xres) { + DRM_ERROR("fbconv: Display panning unsupported\n"); + return -EINVAL; + } + + err = fb_info->fbops->fb_pan_display(var, fb_info); + if (err) { + DRM_ERROR("fbconv: fbops->pan_display() failed: %d", err); + return err; + } + + fb_info->var.xoffset = var->xoffset; + fb_info->var.yoffset = var->yoffset; + + if (var->vmode & FB_VMODE_YWRAP) + fb_info->var.vmode |= FB_VMODE_YWRAP; + else + fb_info->var.vmode &= ~FB_VMODE_YWRAP; + + return 0; +} + +static int drm_fbconv_set_var(struct fb_info *fb_info, + struct fb_var_screeninfo *var) +{ + int ret = 0; + u32 activate; + struct fb_var_screeninfo old_var; + struct fb_videomode mode; + + if (var->activate & FB_ACTIVATE_INV_MODE) { + struct fb_videomode mode1, mode2; + + fb_var_to_videomode(&mode1, var); + fb_var_to_videomode(&mode2, &fb_info->var); + /* make sure we don't delete the videomode of current var */ + ret = fb_mode_is_equal(&mode1, &mode2); + if (ret) { + DRM_ERROR("fbconv: fb_mode_is_equal() failed: %d\n", + ret); + return -EINVAL; + } + + fb_delete_videomode(&mode1, &fb_info->modelist); + + return 0; + } + + if (!(var->activate & FB_ACTIVATE_FORCE) && + !memcmp(&fb_info->var, var, sizeof(*var))) + return 0; + + activate = var->activate; + + /* When using FOURCC mode, make sure the red, green, blue and + * transp fields are set to 0. + */ + if ((fb_info->fix.capabilities & FB_CAP_FOURCC) && var->grayscale > 1) { + if (var->red.offset || var->green.offset || + var->blue.offset || var->transp.offset || + var->red.length || var->green.length || + var->blue.length || var->transp.length || + var->red.msb_right || var->green.msb_right || + var->blue.msb_right || var->transp.msb_right) { + DRM_ERROR("fbconv: Invalid color offsets in FOURCC mode\n"); + return -EINVAL; + } + } + + if (!fb_info->fbops->fb_check_var) { + *var = fb_info->var; + return 0; + } + + ret = fb_info->fbops->fb_check_var(var, fb_info); + if (ret) { + DRM_ERROR("fbconv: fbops->fb_check_var() failed: %d\n", ret); + return ret; + } + + if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) + return 0; + + old_var = fb_info->var; + fb_info->var = *var; + + if (fb_info->fbops->fb_set_par) { + ret = fb_info->fbops->fb_set_par(fb_info); + if (ret) { + fb_info->var = old_var; + DRM_ERROR("fbconv: fbops->fb_set_par() failed: %d\n", + ret); + return ret; + } + } + + drm_fbconv_pan_display(fb_info, &fb_info->var); + drm_fbconv_set_cmap(&fb_info->cmap, fb_info); + fb_var_to_videomode(&mode, &fb_info->var); + + if (fb_info->modelist.prev && fb_info->modelist.next && + !list_empty(&fb_info->modelist)) + ret = fb_add_videomode(&mode, &fb_info->modelist); + + if (ret) { + DRM_ERROR("fbconv: fb_add_videomode() failed: %d\n", ret); + return ret; + } + + return 0; +} + /** * drm_fbconv_simple_display_pipe_mode_valid - default implementation for * struct drm_simple_display_pipe_funcs.mode_valid @@ -1105,13 +1311,11 @@ drm_fbconv_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, fb_var.activate = FB_ACTIVATE_NOW; - ret = fb_set_var(modeset->fb_info, &fb_var); - if (ret) { - DRM_ERROR("fbconv: fb_set_var() failed: %d\n", ret); + ret = drm_fbconv_set_var(modeset->fb_info, &fb_var); + if (ret) return; - } - fb_blank(modeset->fb_info, FB_BLANK_UNBLANK); + drm_fbconv_blank(modeset->fb_info, FB_BLANK_UNBLANK); drm_fbconv_blit_fullscreen(modeset->blit.screen_base, modeset->blit.vmap, @@ -1129,7 +1333,7 @@ drm_fbconv_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe) { struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe); - fb_blank(modeset->fb_info, FB_BLANK_POWERDOWN); + drm_fbconv_blank(modeset->fb_info, FB_BLANK_POWERDOWN); } EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_disable); @@ -1295,7 +1499,7 @@ drm_fbconv_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, if (!pipe->plane.state->fb) { /* No framebuffer installed; blank display. */ - fb_blank(modeset->fb_info, FB_BLANK_NORMAL); + drm_fbconv_blank(modeset->fb_info, FB_BLANK_NORMAL); return; } @@ -1315,11 +1519,9 @@ drm_fbconv_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, fb_var.activate = FB_ACTIVATE_NOW; - ret = fb_set_var(modeset->fb_info, &fb_var); - if (ret) { - DRM_ERROR("fbconv: fb_set_var() failed: %d\n", ret); + ret = drm_fbconv_set_var(modeset->fb_info, &fb_var); + if (ret) return; - } } if (!old_plane_state->fb || /* first-time update */ @@ -1344,11 +1546,9 @@ drm_fbconv_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, fb_var.xoffset = 0; fb_var.yoffset = 0; - ret = fb_pan_display(modeset->fb_info, &fb_var); - if (ret) { - DRM_ERROR("fbconv: fb_pan_display() failed: %d\n", ret); + ret = drm_fbconv_pan_display(modeset->fb_info, &fb_var); + if (ret) return; - } do_blit = drm_atomic_helper_damage_merged(old_plane_state, pipe->plane.state, -- 2.23.0 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel