at v6.16 167 lines 4.2 kB view raw
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 16static 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 29static int drm_fbdev_client_restore(struct drm_client_dev *client) 30{ 31 drm_fb_helper_lastclose(client->dev); 32 33 return 0; 34} 35 36static 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 58err_drm_fb_helper_fini: 59 drm_fb_helper_fini(fb_helper); 60err_drm_err: 61 drm_err(dev, "fbdev: Failed to setup emulation (ret=%d)\n", ret); 62 return ret; 63} 64 65static 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 77static 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 89static 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 */ 117int 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 163err_drm_client_init: 164 drm_fb_helper_unprepare(fb_helper); 165 kfree(fb_helper); 166 return ret; 167}