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