diff options
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 155 |
1 files changed, 113 insertions, 42 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d3af098b0922..ca706fb1d975 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c | |||
@@ -1797,6 +1797,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, | |||
1797 | int i; | 1797 | int i; |
1798 | struct drm_fb_helper_surface_size sizes; | 1798 | struct drm_fb_helper_surface_size sizes; |
1799 | int gamma_size = 0; | 1799 | int gamma_size = 0; |
1800 | int best_depth = 0; | ||
1800 | 1801 | ||
1801 | memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); | 1802 | memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); |
1802 | sizes.surface_depth = 24; | 1803 | sizes.surface_depth = 24; |
@@ -1804,7 +1805,10 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, | |||
1804 | sizes.fb_width = (u32)-1; | 1805 | sizes.fb_width = (u32)-1; |
1805 | sizes.fb_height = (u32)-1; | 1806 | sizes.fb_height = (u32)-1; |
1806 | 1807 | ||
1807 | /* if driver picks 8 or 16 by default use that for both depth/bpp */ | 1808 | /* |
1809 | * If driver picks 8 or 16 by default use that for both depth/bpp | ||
1810 | * to begin with | ||
1811 | */ | ||
1808 | if (preferred_bpp != sizes.surface_bpp) | 1812 | if (preferred_bpp != sizes.surface_bpp) |
1809 | sizes.surface_depth = sizes.surface_bpp = preferred_bpp; | 1813 | sizes.surface_depth = sizes.surface_bpp = preferred_bpp; |
1810 | 1814 | ||
@@ -1839,6 +1843,55 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, | |||
1839 | } | 1843 | } |
1840 | } | 1844 | } |
1841 | 1845 | ||
1846 | /* | ||
1847 | * If we run into a situation where, for example, the primary plane | ||
1848 | * supports RGBA5551 (16 bpp, depth 15) but not RGB565 (16 bpp, depth | ||
1849 | * 16) we need to scale down the depth of the sizes we request. | ||
1850 | */ | ||
1851 | for (i = 0; i < fb_helper->crtc_count; i++) { | ||
1852 | struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; | ||
1853 | struct drm_crtc *crtc = mode_set->crtc; | ||
1854 | struct drm_plane *plane = crtc->primary; | ||
1855 | int j; | ||
1856 | |||
1857 | DRM_DEBUG("test CRTC %d primary plane\n", i); | ||
1858 | |||
1859 | for (j = 0; j < plane->format_count; j++) { | ||
1860 | const struct drm_format_info *fmt; | ||
1861 | |||
1862 | fmt = drm_format_info(plane->format_types[j]); | ||
1863 | |||
1864 | /* | ||
1865 | * Do not consider YUV or other complicated formats | ||
1866 | * for framebuffers. This means only legacy formats | ||
1867 | * are supported (fmt->depth is a legacy field) but | ||
1868 | * the framebuffer emulation can only deal with such | ||
1869 | * formats, specifically RGB/BGA formats. | ||
1870 | */ | ||
1871 | if (fmt->depth == 0) | ||
1872 | continue; | ||
1873 | |||
1874 | /* We found a perfect fit, great */ | ||
1875 | if (fmt->depth == sizes.surface_depth) { | ||
1876 | best_depth = fmt->depth; | ||
1877 | break; | ||
1878 | } | ||
1879 | |||
1880 | /* Skip depths above what we're looking for */ | ||
1881 | if (fmt->depth > sizes.surface_depth) | ||
1882 | continue; | ||
1883 | |||
1884 | /* Best depth found so far */ | ||
1885 | if (fmt->depth > best_depth) | ||
1886 | best_depth = fmt->depth; | ||
1887 | } | ||
1888 | } | ||
1889 | if (sizes.surface_depth != best_depth) { | ||
1890 | DRM_INFO("requested bpp %d, scaled depth down to %d", | ||
1891 | sizes.surface_bpp, best_depth); | ||
1892 | sizes.surface_depth = best_depth; | ||
1893 | } | ||
1894 | |||
1842 | crtc_count = 0; | 1895 | crtc_count = 0; |
1843 | for (i = 0; i < fb_helper->crtc_count; i++) { | 1896 | for (i = 0; i < fb_helper->crtc_count; i++) { |
1844 | struct drm_display_mode *desired_mode; | 1897 | struct drm_display_mode *desired_mode; |
@@ -2866,7 +2919,7 @@ int drm_fb_helper_fbdev_setup(struct drm_device *dev, | |||
2866 | return 0; | 2919 | return 0; |
2867 | 2920 | ||
2868 | err_drm_fb_helper_fini: | 2921 | err_drm_fb_helper_fini: |
2869 | drm_fb_helper_fini(fb_helper); | 2922 | drm_fb_helper_fbdev_teardown(dev); |
2870 | 2923 | ||
2871 | return ret; | 2924 | return ret; |
2872 | } | 2925 | } |
@@ -2961,18 +3014,16 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user) | |||
2961 | return 0; | 3014 | return 0; |
2962 | } | 3015 | } |
2963 | 3016 | ||
2964 | /* | 3017 | static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper) |
2965 | * fb_ops.fb_destroy is called by the last put_fb_info() call at the end of | ||
2966 | * unregister_framebuffer() or fb_release(). | ||
2967 | */ | ||
2968 | static void drm_fbdev_fb_destroy(struct fb_info *info) | ||
2969 | { | 3018 | { |
2970 | struct drm_fb_helper *fb_helper = info->par; | ||
2971 | struct fb_info *fbi = fb_helper->fbdev; | 3019 | struct fb_info *fbi = fb_helper->fbdev; |
2972 | struct fb_ops *fbops = NULL; | 3020 | struct fb_ops *fbops = NULL; |
2973 | void *shadow = NULL; | 3021 | void *shadow = NULL; |
2974 | 3022 | ||
2975 | if (fbi->fbdefio) { | 3023 | if (!fb_helper->dev) |
3024 | return; | ||
3025 | |||
3026 | if (fbi && fbi->fbdefio) { | ||
2976 | fb_deferred_io_cleanup(fbi); | 3027 | fb_deferred_io_cleanup(fbi); |
2977 | shadow = fbi->screen_buffer; | 3028 | shadow = fbi->screen_buffer; |
2978 | fbops = fbi->fbops; | 3029 | fbops = fbi->fbops; |
@@ -2986,6 +3037,12 @@ static void drm_fbdev_fb_destroy(struct fb_info *info) | |||
2986 | } | 3037 | } |
2987 | 3038 | ||
2988 | drm_client_framebuffer_delete(fb_helper->buffer); | 3039 | drm_client_framebuffer_delete(fb_helper->buffer); |
3040 | } | ||
3041 | |||
3042 | static void drm_fbdev_release(struct drm_fb_helper *fb_helper) | ||
3043 | { | ||
3044 | drm_fbdev_cleanup(fb_helper); | ||
3045 | |||
2989 | /* | 3046 | /* |
2990 | * FIXME: | 3047 | * FIXME: |
2991 | * Remove conditional when all CMA drivers have been moved over to using | 3048 | * Remove conditional when all CMA drivers have been moved over to using |
@@ -2997,6 +3054,15 @@ static void drm_fbdev_fb_destroy(struct fb_info *info) | |||
2997 | } | 3054 | } |
2998 | } | 3055 | } |
2999 | 3056 | ||
3057 | /* | ||
3058 | * fb_ops.fb_destroy is called by the last put_fb_info() call at the end of | ||
3059 | * unregister_framebuffer() or fb_release(). | ||
3060 | */ | ||
3061 | static void drm_fbdev_fb_destroy(struct fb_info *info) | ||
3062 | { | ||
3063 | drm_fbdev_release(info->par); | ||
3064 | } | ||
3065 | |||
3000 | static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) | 3066 | static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) |
3001 | { | 3067 | { |
3002 | struct drm_fb_helper *fb_helper = info->par; | 3068 | struct drm_fb_helper *fb_helper = info->par; |
@@ -3047,7 +3113,6 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, | |||
3047 | struct drm_framebuffer *fb; | 3113 | struct drm_framebuffer *fb; |
3048 | struct fb_info *fbi; | 3114 | struct fb_info *fbi; |
3049 | u32 format; | 3115 | u32 format; |
3050 | int ret; | ||
3051 | 3116 | ||
3052 | DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", | 3117 | DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", |
3053 | sizes->surface_width, sizes->surface_height, | 3118 | sizes->surface_width, sizes->surface_height, |
@@ -3064,10 +3129,8 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, | |||
3064 | fb = buffer->fb; | 3129 | fb = buffer->fb; |
3065 | 3130 | ||
3066 | fbi = drm_fb_helper_alloc_fbi(fb_helper); | 3131 | fbi = drm_fb_helper_alloc_fbi(fb_helper); |
3067 | if (IS_ERR(fbi)) { | 3132 | if (IS_ERR(fbi)) |
3068 | ret = PTR_ERR(fbi); | 3133 | return PTR_ERR(fbi); |
3069 | goto err_free_buffer; | ||
3070 | } | ||
3071 | 3134 | ||
3072 | fbi->par = fb_helper; | 3135 | fbi->par = fb_helper; |
3073 | fbi->fbops = &drm_fbdev_fb_ops; | 3136 | fbi->fbops = &drm_fbdev_fb_ops; |
@@ -3098,8 +3161,7 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, | |||
3098 | if (!fbops || !shadow) { | 3161 | if (!fbops || !shadow) { |
3099 | kfree(fbops); | 3162 | kfree(fbops); |
3100 | vfree(shadow); | 3163 | vfree(shadow); |
3101 | ret = -ENOMEM; | 3164 | return -ENOMEM; |
3102 | goto err_fb_info_destroy; | ||
3103 | } | 3165 | } |
3104 | 3166 | ||
3105 | *fbops = *fbi->fbops; | 3167 | *fbops = *fbi->fbops; |
@@ -3111,13 +3173,6 @@ int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, | |||
3111 | } | 3173 | } |
3112 | 3174 | ||
3113 | return 0; | 3175 | return 0; |
3114 | |||
3115 | err_fb_info_destroy: | ||
3116 | drm_fb_helper_fini(fb_helper); | ||
3117 | err_free_buffer: | ||
3118 | drm_client_framebuffer_delete(buffer); | ||
3119 | |||
3120 | return ret; | ||
3121 | } | 3176 | } |
3122 | EXPORT_SYMBOL(drm_fb_helper_generic_probe); | 3177 | EXPORT_SYMBOL(drm_fb_helper_generic_probe); |
3123 | 3178 | ||
@@ -3129,18 +3184,11 @@ static void drm_fbdev_client_unregister(struct drm_client_dev *client) | |||
3129 | { | 3184 | { |
3130 | struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); | 3185 | struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); |
3131 | 3186 | ||
3132 | if (fb_helper->fbdev) { | 3187 | if (fb_helper->fbdev) |
3133 | drm_fb_helper_unregister_fbi(fb_helper); | ||
3134 | /* drm_fbdev_fb_destroy() takes care of cleanup */ | 3188 | /* drm_fbdev_fb_destroy() takes care of cleanup */ |
3135 | return; | 3189 | drm_fb_helper_unregister_fbi(fb_helper); |
3136 | } | 3190 | else |
3137 | 3191 | drm_fbdev_release(fb_helper); | |
3138 | /* Did drm_fb_helper_fbdev_setup() run? */ | ||
3139 | if (fb_helper->dev) | ||
3140 | drm_fb_helper_fini(fb_helper); | ||
3141 | |||
3142 | drm_client_release(client); | ||
3143 | kfree(fb_helper); | ||
3144 | } | 3192 | } |
3145 | 3193 | ||
3146 | static int drm_fbdev_client_restore(struct drm_client_dev *client) | 3194 | static int drm_fbdev_client_restore(struct drm_client_dev *client) |
@@ -3158,7 +3206,7 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) | |||
3158 | struct drm_device *dev = client->dev; | 3206 | struct drm_device *dev = client->dev; |
3159 | int ret; | 3207 | int ret; |
3160 | 3208 | ||
3161 | /* If drm_fb_helper_fbdev_setup() failed, we only try once */ | 3209 | /* Setup is not retried if it has failed */ |
3162 | if (!fb_helper->dev && fb_helper->funcs) | 3210 | if (!fb_helper->dev && fb_helper->funcs) |
3163 | return 0; | 3211 | return 0; |
3164 | 3212 | ||
@@ -3170,15 +3218,34 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) | |||
3170 | return 0; | 3218 | return 0; |
3171 | } | 3219 | } |
3172 | 3220 | ||
3173 | ret = drm_fb_helper_fbdev_setup(dev, fb_helper, &drm_fb_helper_generic_funcs, | 3221 | drm_fb_helper_prepare(dev, fb_helper, &drm_fb_helper_generic_funcs); |
3174 | fb_helper->preferred_bpp, 0); | 3222 | |
3175 | if (ret) { | 3223 | ret = drm_fb_helper_init(dev, fb_helper, dev->mode_config.num_connector); |
3176 | fb_helper->dev = NULL; | 3224 | if (ret) |
3177 | fb_helper->fbdev = NULL; | 3225 | goto err; |
3178 | return ret; | 3226 | |
3179 | } | 3227 | ret = drm_fb_helper_single_add_all_connectors(fb_helper); |
3228 | if (ret) | ||
3229 | goto err_cleanup; | ||
3230 | |||
3231 | if (!drm_drv_uses_atomic_modeset(dev)) | ||
3232 | drm_helper_disable_unused_functions(dev); | ||
3233 | |||
3234 | ret = drm_fb_helper_initial_config(fb_helper, fb_helper->preferred_bpp); | ||
3235 | if (ret) | ||
3236 | goto err_cleanup; | ||
3180 | 3237 | ||
3181 | return 0; | 3238 | return 0; |
3239 | |||
3240 | err_cleanup: | ||
3241 | drm_fbdev_cleanup(fb_helper); | ||
3242 | err: | ||
3243 | fb_helper->dev = NULL; | ||
3244 | fb_helper->fbdev = NULL; | ||
3245 | |||
3246 | DRM_DEV_ERROR(dev->dev, "fbdev: Failed to setup generic emulation (ret=%d)\n", ret); | ||
3247 | |||
3248 | return ret; | ||
3182 | } | 3249 | } |
3183 | 3250 | ||
3184 | static const struct drm_client_funcs drm_fbdev_client_funcs = { | 3251 | static const struct drm_client_funcs drm_fbdev_client_funcs = { |
@@ -3237,6 +3304,10 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) | |||
3237 | 3304 | ||
3238 | drm_client_add(&fb_helper->client); | 3305 | drm_client_add(&fb_helper->client); |
3239 | 3306 | ||
3307 | if (!preferred_bpp) | ||
3308 | preferred_bpp = dev->mode_config.preferred_depth; | ||
3309 | if (!preferred_bpp) | ||
3310 | preferred_bpp = 32; | ||
3240 | fb_helper->preferred_bpp = preferred_bpp; | 3311 | fb_helper->preferred_bpp = preferred_bpp; |
3241 | 3312 | ||
3242 | ret = drm_fbdev_client_hotplug(&fb_helper->client); | 3313 | ret = drm_fbdev_client_hotplug(&fb_helper->client); |