1 // SPDX-License-Identifier: MIT 2 3 #include <drm/drm_client.h> 4 #include <drm/drm_crtc_helper.h> 5 #include <drm/drm_drv.h> 6 #include <drm/drm_fb_helper.h> 7 #include <drm/drm_fourcc.h> 8 #include <drm/drm_print.h> 9 10 #include "drm_client_internal.h" 11 12 /* 13 * struct drm_client_funcs 14 */ 15 16 static void drm_fbdev_client_free(struct drm_client_dev *client) 17 { 18 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 19 20 drm_fb_helper_unprepare(fb_helper); 21 kfree(fb_helper); 22 } 23 24 static void drm_fbdev_client_unregister(struct drm_client_dev *client) 25 { 26 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 27 28 if (fb_helper->info) { 29 /* 30 * Fully probed framebuffer device 31 */ 32 drm_fb_helper_unregister_info(fb_helper); 33 } else { 34 /* 35 * Partially initialized client, no framebuffer device yet 36 */ 37 drm_client_release(&fb_helper->client); 38 } 39 } 40 41 static int drm_fbdev_client_restore(struct drm_client_dev *client, bool force) 42 { 43 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 44 45 /* 46 * The client is registered before the initial fbdev probe. 47 * If probing failed, the client remains registered but there 48 * is no valid fbdev framebuffer to restore. 49 */ 50 if (!fb_helper->info || !fb_helper->fb) 51 return 0; 52 53 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force); 54 55 return 0; 56 } 57 58 static int drm_fbdev_client_hotplug(struct drm_client_dev *client) 59 { 60 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 61 struct drm_device *dev = client->dev; 62 int ret; 63 64 if (dev->fb_helper) 65 return drm_fb_helper_hotplug_event(dev->fb_helper); 66 67 ret = drm_fb_helper_init(dev, fb_helper); 68 if (ret) 69 goto err_drm_err; 70 71 if (!drm_drv_uses_atomic_modeset(dev)) 72 drm_helper_disable_unused_functions(dev); 73 74 ret = drm_fb_helper_initial_config(fb_helper); 75 if (ret) 76 goto err_drm_fb_helper_fini; 77 78 return 0; 79 80 err_drm_fb_helper_fini: 81 drm_fb_helper_fini(fb_helper); 82 err_drm_err: 83 drm_err(dev, "fbdev: Failed to setup emulation (ret=%d)\n", ret); 84 return ret; 85 } 86 87 static int drm_fbdev_client_suspend(struct drm_client_dev *client) 88 { 89 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 90 91 drm_fb_helper_set_suspend_unlocked(fb_helper, true); 92 93 return 0; 94 } 95 96 static int drm_fbdev_client_resume(struct drm_client_dev *client) 97 { 98 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 99 100 drm_fb_helper_set_suspend_unlocked(fb_helper, false); 101 102 return 0; 103 } 104 105 static const struct drm_client_funcs drm_fbdev_client_funcs = { 106 .owner = THIS_MODULE, 107 .free = drm_fbdev_client_free, 108 .unregister = drm_fbdev_client_unregister, 109 .restore = drm_fbdev_client_restore, 110 .hotplug = drm_fbdev_client_hotplug, 111 .suspend = drm_fbdev_client_suspend, 112 .resume = drm_fbdev_client_resume, 113 }; 114 115 /** 116 * drm_fbdev_client_setup() - Setup fbdev emulation 117 * @dev: DRM device 118 * @format: Preferred color format for the device. DRM_FORMAT_XRGB8888 119 * is used if this is zero. 120 * 121 * This function sets up fbdev emulation. Restore, hotplug events and 122 * teardown are all taken care of. Drivers that do suspend/resume need 123 * to call drm_client_dev_suspend() and drm_client_dev_resume() by 124 * themselves. Simple drivers might use drm_mode_config_helper_suspend(). 125 * 126 * This function is safe to call even when there are no connectors present. 127 * Setup will be retried on the next hotplug event. 128 * 129 * The fbdev client is destroyed by drm_dev_unregister(). 130 * 131 * Returns: 132 * 0 on success, or a negative errno code otherwise. 133 */ 134 int drm_fbdev_client_setup(struct drm_device *dev, const struct drm_format_info *format) 135 { 136 struct drm_fb_helper *fb_helper; 137 unsigned int color_mode; 138 int ret; 139 140 /* TODO: Use format info throughout DRM */ 141 if (format) { 142 unsigned int bpp = drm_format_info_bpp(format, 0); 143 144 switch (bpp) { 145 case 16: 146 color_mode = format->depth; // could also be 15 147 break; 148 default: 149 color_mode = bpp; 150 } 151 } else { 152 switch (dev->mode_config.preferred_depth) { 153 case 0: 154 case 24: 155 color_mode = 32; 156 break; 157 default: 158 color_mode = dev->mode_config.preferred_depth; 159 } 160 } 161 162 drm_WARN(dev, !dev->registered, "Device has not been registered.\n"); 163 drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n"); 164 165 fb_helper = kzalloc_obj(*fb_helper); 166 if (!fb_helper) 167 return -ENOMEM; 168 drm_fb_helper_prepare(dev, fb_helper, color_mode, NULL); 169 170 ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs); 171 if (ret) { 172 drm_err(dev, "Failed to register client: %d\n", ret); 173 goto err_drm_client_init; 174 } 175 176 drm_client_register(&fb_helper->client); 177 178 return 0; 179 180 err_drm_client_init: 181 drm_fb_helper_unprepare(fb_helper); 182 kfree(fb_helper); 183 return ret; 184 } 185