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