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