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