aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_fb_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c155
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
2868err_drm_fb_helper_fini: 2921err_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/* 3017static 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 */
2968static 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
3042static 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 */
3061static void drm_fbdev_fb_destroy(struct fb_info *info)
3062{
3063 drm_fbdev_release(info->par);
3064}
3065
3000static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) 3066static 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
3115err_fb_info_destroy:
3116 drm_fb_helper_fini(fb_helper);
3117err_free_buffer:
3118 drm_client_framebuffer_delete(buffer);
3119
3120 return ret;
3121} 3176}
3122EXPORT_SYMBOL(drm_fb_helper_generic_probe); 3177EXPORT_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
3146static int drm_fbdev_client_restore(struct drm_client_dev *client) 3194static 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
3240err_cleanup:
3241 drm_fbdev_cleanup(fb_helper);
3242err:
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
3184static const struct drm_client_funcs drm_fbdev_client_funcs = { 3251static 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);