diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/fbdev.c')
-rw-r--r-- | drivers/gpu/drm/tegra/fbdev.c | 177 |
1 files changed, 80 insertions, 97 deletions
diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index 6ff7106f440e..dca9eccae466 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -10,6 +10,9 @@ #include <linux/console.h> #include <linux/vmalloc.h> +#include <drm/drm_drv.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -33,6 +36,26 @@ static int tegra_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) return __tegra_gem_mmap(&bo->gem, vma); } +static void tegra_fbdev_fb_destroy(struct fb_info *info) +{ + struct drm_fb_helper *helper = info->par; + struct drm_framebuffer *fb = helper->fb; + struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); + + drm_fb_helper_fini(helper); + + /* Undo the special mapping we made in fbdev probe. */ + if (bo->pages) { + vunmap(bo->vaddr); + bo->vaddr = NULL; + } + drm_framebuffer_remove(fb); + + drm_client_release(&helper->client); + drm_fb_helper_unprepare(helper); + kfree(helper); +} + static const struct fb_ops tegra_fb_ops = { .owner = THIS_MODULE, DRM_FB_HELPER_DEFAULT_OPS, @@ -42,6 +65,7 @@ static const struct fb_ops tegra_fb_ops = { .fb_copyarea = drm_fb_helper_sys_copyarea, .fb_imageblit = drm_fb_helper_sys_imageblit, .fb_mmap = tegra_fb_mmap, + .fb_destroy = tegra_fbdev_fb_destroy, }; static int tegra_fbdev_probe(struct drm_fb_helper *helper, @@ -131,16 +155,52 @@ static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = { */ static void tegra_fbdev_client_unregister(struct drm_client_dev *client) -{ } +{ + struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); + + if (fb_helper->info) { + drm_fb_helper_unregister_info(fb_helper); + } else { + drm_client_release(&fb_helper->client); + drm_fb_helper_unprepare(fb_helper); + kfree(fb_helper); + } +} -static int tregra_fbdev_client_restore(struct drm_client_dev *client) +static int tegra_fbdev_client_restore(struct drm_client_dev *client) { + drm_fb_helper_lastclose(client->dev); + return 0; } static int tegra_fbdev_client_hotplug(struct drm_client_dev *client) { + struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); + struct drm_device *dev = client->dev; + int ret; + + if (dev->fb_helper) + return drm_fb_helper_hotplug_event(dev->fb_helper); + + ret = drm_fb_helper_init(dev, fb_helper); + if (ret) + goto err_drm_err; + + if (!drm_drv_uses_atomic_modeset(dev)) + drm_helper_disable_unused_functions(dev); + + ret = drm_fb_helper_initial_config(fb_helper); + if (ret) + goto err_drm_fb_helper_fini; + return 0; + +err_drm_fb_helper_fini: + drm_fb_helper_fini(fb_helper); +err_drm_err: + drm_err(dev, "Failed to setup fbdev emulation (ret=%d)\n", ret); + return ret; } static const struct drm_client_funcs tegra_fbdev_client_funcs = { @@ -150,109 +210,32 @@ static const struct drm_client_funcs tegra_fbdev_client_funcs = { .hotplug = tegra_fbdev_client_hotplug, }; -static struct drm_fb_helper *tegra_fbdev_create(struct drm_device *drm) +void tegra_fbdev_setup(struct drm_device *dev) { struct drm_fb_helper *helper; + int ret; + + drm_WARN(dev, !dev->registered, "Device has not been registered.\n"); + drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n"); helper = kzalloc(sizeof(*helper), GFP_KERNEL); if (!helper) - return ERR_PTR(-ENOMEM); - - drm_fb_helper_prepare(drm, helper, 32, &tegra_fb_helper_funcs); - - return helper; -} - -static void tegra_fbdev_free(struct drm_fb_helper *helper) -{ - drm_fb_helper_unprepare(helper); - kfree(helper); -} - -static int tegra_fbdev_init(struct drm_fb_helper *helper, - unsigned int num_crtc, - unsigned int max_connectors) -{ - struct drm_device *drm = helper->dev; - int err; - - err = drm_client_init(dev, &helper->client, "fbdev", &tegra_fbdev_client_funcs); - if (err) - return err; - - err = drm_fb_helper_init(drm, helper); - if (err < 0) { - dev_err(drm->dev, "failed to initialize DRM FB helper: %d\n", - err); - goto err_drm_client_release; - } + return; + drm_fb_helper_prepare(dev, helper, 32, &tegra_fb_helper_funcs); - err = drm_fb_helper_initial_config(helper); - if (err < 0) { - dev_err(drm->dev, "failed to set initial configuration: %d\n", - err); - goto fini; - } + ret = drm_client_init(dev, &helper->client, "fbdev", &tegra_fbdev_client_funcs); + if (ret) + goto err_drm_client_init; - return 0; + ret = tegra_fbdev_client_hotplug(&helper->client); + if (ret) + drm_dbg_kms(dev, "client hotplug ret=%d\n", ret); -fini: - drm_fb_helper_fini(helper); -err_drm_client_release: - drm_client_release(&helper->client); - return err; -} + drm_client_register(&helper->client); -static void tegra_fbdev_exit(struct drm_fb_helper *helper) -{ - struct drm_framebuffer *fb = helper->fb; - - drm_fb_helper_unregister_info(helper); - - if (fb) { - struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); - - /* Undo the special mapping we made in fbdev probe. */ - if (bo && bo->pages) { - vunmap(bo->vaddr); - bo->vaddr = NULL; - } - - drm_framebuffer_remove(fb); - } - - drm_fb_helper_fini(helper); - drm_client_release(&helper->client); - tegra_fbdev_free(helper); -} + return; -int tegra_drm_fb_prepare(struct drm_device *drm) -{ - drm->fb_helper = tegra_fbdev_create(drm); - if (IS_ERR(drm->fb_helper)) - return PTR_ERR(drm->fb_helper); - - return 0; -} - -void tegra_drm_fb_free(struct drm_device *drm) -{ - tegra_fbdev_free(drm->fb_helper); -} - -int tegra_drm_fb_init(struct drm_device *drm) -{ - int err; - - err = tegra_fbdev_init(drm->fb_helper, drm->mode_config.num_crtc, - drm->mode_config.num_connector); - if (err < 0) - return err; - - return 0; -} - -void tegra_drm_fb_exit(struct drm_device *drm) -{ - tegra_fbdev_exit(drm->fb_helper); +err_drm_client_init: + drm_fb_helper_unprepare(helper); + kfree(helper); } |