1/* 2 * Copyright 2011 Joakim Sindholt <opensource@zhasha.com> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * on the rights to use, copy, modify, merge, publish, distribute, sub 8 * license, and/or sell copies of the Software, and to permit persons to whom 9 * the Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 * USE OR OTHER DEALINGS IN THE SOFTWARE. */ 22 23/* XXX: header order is slightly screwy here */ 24#include "loader.h" 25 26#include "adapter9.h" 27 28#include "pipe-loader/pipe_loader.h" 29 30#include "pipe/p_screen.h" 31#include "pipe/p_state.h" 32 33#include "target-helpers/drm_helper.h" 34#include "target-helpers/sw_helper.h" 35#include "state_tracker/drm_driver.h" 36 37#include "d3dadapter/d3dadapter9.h" 38#include "d3dadapter/drm.h" 39 40#include "util/xmlconfig.h" 41#include "util/xmlpool.h" 42 43#include "drm-uapi/drm.h" 44#include <sys/ioctl.h> 45#include <fcntl.h> 46#include <stdio.h> 47 48#define DBG_CHANNEL DBG_ADAPTER 49 50const char __driConfigOptionsNine[] = 51DRI_CONF_BEGIN 52 DRI_CONF_SECTION_PERFORMANCE 53 DRI_CONF_VBLANK_MODE(DRI_CONF_VBLANK_DEF_INTERVAL_1) 54 DRI_CONF_SECTION_END 55 DRI_CONF_SECTION_NINE 56 DRI_CONF_NINE_OVERRIDEVENDOR(-1) 57 DRI_CONF_NINE_THROTTLE(-2) 58 DRI_CONF_NINE_THREADSUBMIT("false") 59 DRI_CONF_NINE_ALLOWDISCARDDELAYEDRELEASE("true") 60 DRI_CONF_NINE_TEARFREEDISCARD("false") 61 DRI_CONF_NINE_CSMT(-1) 62 DRI_CONF_NINE_DYNAMICTEXTUREWORKAROUND("false") 63 DRI_CONF_NINE_SHADERINLINECONSTANTS("false") 64 DRI_CONF_SECTION_END 65DRI_CONF_END; 66 67struct fallback_card_config { 68 const char *name; 69 unsigned vendor_id; 70 unsigned device_id; 71} fallback_cards[] = { 72 {"NV124", 0x10de, 0x13C2}, /* NVIDIA GeForce GTX 970 */ 73 {"HAWAII", 0x1002, 0x67b1}, /* AMD Radeon R9 290 */ 74 {"Haswell Mobile", 0x8086, 0x13C2}, /* Intel Haswell Mobile */ 75 {"SVGA3D", 0x15ad, 0x0405}, /* VMware SVGA 3D */ 76}; 77 78/* prototypes */ 79void 80d3d_match_vendor_id( D3DADAPTER_IDENTIFIER9* drvid, 81 unsigned fallback_ven, 82 unsigned fallback_dev, 83 const char* fallback_name ); 84 85void d3d_fill_driver_version(D3DADAPTER_IDENTIFIER9* drvid); 86 87void d3d_fill_cardname(D3DADAPTER_IDENTIFIER9* drvid); 88 89struct d3dadapter9drm_context 90{ 91 struct d3dadapter9_context base; 92 struct pipe_loader_device *dev, *swdev; 93 int fd; 94}; 95 96static void 97drm_destroy( struct d3dadapter9_context *ctx ) 98{ 99 struct d3dadapter9drm_context *drm = (struct d3dadapter9drm_context *)ctx; 100 101 if (ctx->ref) 102 ctx->ref->destroy(ctx->ref); 103 /* because ref is a wrapper around hal, freeing ref frees hal too. */ 104 else if (ctx->hal) 105 ctx->hal->destroy(ctx->hal); 106 107 if (drm->swdev) 108 pipe_loader_release(&drm->swdev, 1); 109 if (drm->dev) 110 pipe_loader_release(&drm->dev, 1); 111 112 close(drm->fd); 113 FREE(ctx); 114} 115 116static inline void 117get_bus_info( int fd, 118 DWORD *vendorid, 119 DWORD *deviceid, 120 DWORD *subsysid, 121 DWORD *revision ) 122{ 123 int vid, did; 124 125 if (loader_get_pci_id_for_fd(fd, &vid, &did)) { 126 DBG("PCI info: vendor=0x%04x, device=0x%04x\n", 127 vid, did); 128 *vendorid = vid; 129 *deviceid = did; 130 *subsysid = 0; 131 *revision = 0; 132 } else { 133 DBG("Unable to detect card. Faking %s\n", fallback_cards[0].name); 134 *vendorid = fallback_cards[0].vendor_id; 135 *deviceid = fallback_cards[0].device_id; 136 *subsysid = 0; 137 *revision = 0; 138 } 139} 140 141static inline void 142read_descriptor( struct d3dadapter9_context *ctx, 143 int fd, int override_vendorid ) 144{ 145 unsigned i; 146 BOOL found; 147 D3DADAPTER_IDENTIFIER9 *drvid = &ctx->identifier; 148 149 memset(drvid, 0, sizeof(*drvid)); 150 get_bus_info(fd, &drvid->VendorId, &drvid->DeviceId, 151 &drvid->SubSysId, &drvid->Revision); 152 snprintf(drvid->DeviceName, sizeof(drvid->DeviceName), 153 "Gallium 0.4 with %s", ctx->hal->get_vendor(ctx->hal)); 154 snprintf(drvid->Description, sizeof(drvid->Description), 155 "%s", ctx->hal->get_name(ctx->hal)); 156 157 if (override_vendorid > 0) { 158 found = FALSE; 159 /* fill in device_id and card name for fake vendor */ 160 for (i = 0; i < sizeof(fallback_cards)/sizeof(fallback_cards[0]); i++) { 161 if (fallback_cards[i].vendor_id == override_vendorid) { 162 DBG("Faking card '%s' vendor 0x%04x, device 0x%04x\n", 163 fallback_cards[i].name, 164 fallback_cards[i].vendor_id, 165 fallback_cards[i].device_id); 166 drvid->VendorId = fallback_cards[i].vendor_id; 167 drvid->DeviceId = fallback_cards[i].device_id; 168 snprintf(drvid->Description, sizeof(drvid->Description), 169 "%s", fallback_cards[i].name); 170 found = TRUE; 171 break; 172 } 173 } 174 if (!found) { 175 DBG("Unknown fake vendor 0x%04x! Using detected vendor !\n", override_vendorid); 176 } 177 } 178 /* choose fall-back vendor if necessary to allow 179 * the following functions to return sane results */ 180 d3d_match_vendor_id(drvid, fallback_cards[0].vendor_id, fallback_cards[0].device_id, fallback_cards[0].name); 181 /* fill in driver name and version info */ 182 d3d_fill_driver_version(drvid); 183 /* override Description field with Windows like names */ 184 d3d_fill_cardname(drvid); 185 186 /* this driver isn't WHQL certified */ 187 drvid->WHQLLevel = 0; 188 189 /* this value is fixed */ 190 drvid->DeviceIdentifier.Data1 = 0xaeb2cdd4; 191 drvid->DeviceIdentifier.Data2 = 0x6e41; 192 drvid->DeviceIdentifier.Data3 = 0x43ea; 193 drvid->DeviceIdentifier.Data4[0] = 0x94; 194 drvid->DeviceIdentifier.Data4[1] = 0x1c; 195 drvid->DeviceIdentifier.Data4[2] = 0x83; 196 drvid->DeviceIdentifier.Data4[3] = 0x61; 197 drvid->DeviceIdentifier.Data4[4] = 0xcc; 198 drvid->DeviceIdentifier.Data4[5] = 0x76; 199 drvid->DeviceIdentifier.Data4[6] = 0x07; 200 drvid->DeviceIdentifier.Data4[7] = 0x81; 201} 202 203static HRESULT WINAPI 204drm_create_adapter( int fd, 205 ID3DAdapter9 **ppAdapter ) 206{ 207 struct d3dadapter9drm_context *ctx = CALLOC_STRUCT(d3dadapter9drm_context); 208 HRESULT hr; 209 bool different_device; 210 driOptionCache defaultInitOptions; 211 driOptionCache userInitOptions; 212 int throttling_value_user = -2; 213 int override_vendorid = -1; 214 215 if (!ctx) { return E_OUTOFMEMORY; } 216 217 ctx->base.destroy = drm_destroy; 218 219 /* Although the fd is provided from external source, mesa/nine 220 * takes ownership of it. */ 221 fd = loader_get_user_preferred_fd(fd, &different_device); 222 ctx->fd = fd; 223 ctx->base.linear_framebuffer = different_device; 224 225 if (!pipe_loader_drm_probe_fd(&ctx->dev, fd)) { 226 ERR("Failed to probe drm fd %d.\n", fd); 227 FREE(ctx); 228 close(fd); 229 return D3DERR_DRIVERINTERNALERROR; 230 } 231 232 ctx->base.hal = pipe_loader_create_screen(ctx->dev); 233 if (!ctx->base.hal) { 234 ERR("Unable to load requested driver.\n"); 235 drm_destroy(&ctx->base); 236 return D3DERR_DRIVERINTERNALERROR; 237 } 238 239 if (!ctx->base.hal->get_param(ctx->base.hal, PIPE_CAP_DMABUF)) { 240 ERR("The driver is not capable of dma-buf sharing." 241 "Abandon to load nine state tracker\n"); 242 drm_destroy(&ctx->base); 243 return D3DERR_DRIVERINTERNALERROR; 244 } 245 246 /* Previously was set to PIPE_CAP_MAX_FRAMES_IN_FLIGHT, 247 * but the change of value of this cap to 1 seems to cause 248 * regressions. */ 249 ctx->base.throttling_value = 2; 250 ctx->base.throttling = ctx->base.throttling_value > 0; 251 252 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsNine); 253 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, "nine", NULL); 254 if (driCheckOption(&userInitOptions, "throttle_value", DRI_INT)) { 255 throttling_value_user = driQueryOptioni(&userInitOptions, "throttle_value"); 256 if (throttling_value_user == -1) 257 ctx->base.throttling = FALSE; 258 else if (throttling_value_user >= 0) { 259 ctx->base.throttling = TRUE; 260 ctx->base.throttling_value = throttling_value_user; 261 } 262 } 263 264 if (driCheckOption(&userInitOptions, "vblank_mode", DRI_ENUM)) 265 ctx->base.vblank_mode = driQueryOptioni(&userInitOptions, "vblank_mode"); 266 else 267 ctx->base.vblank_mode = 1; 268 269 if (driCheckOption(&userInitOptions, "thread_submit", DRI_BOOL)) 270 ctx->base.thread_submit = driQueryOptionb(&userInitOptions, "thread_submit"); 271 else 272 ctx->base.thread_submit = different_device; 273 274 if (driCheckOption(&userInitOptions, "override_vendorid", DRI_INT)) { 275 override_vendorid = driQueryOptioni(&userInitOptions, "override_vendorid"); 276 } 277 278 if (driCheckOption(&userInitOptions, "discard_delayed_release", DRI_BOOL)) 279 ctx->base.discard_delayed_release = driQueryOptionb(&userInitOptions, "discard_delayed_release"); 280 else 281 ctx->base.discard_delayed_release = TRUE; 282 283 if (driCheckOption(&userInitOptions, "tearfree_discard", DRI_BOOL)) 284 ctx->base.tearfree_discard = driQueryOptionb(&userInitOptions, "tearfree_discard"); 285 else 286 ctx->base.tearfree_discard = FALSE; 287 288 if (ctx->base.tearfree_discard && !ctx->base.discard_delayed_release) { 289 ERR("tearfree_discard requires discard_delayed_release\n"); 290 ctx->base.tearfree_discard = FALSE; 291 } 292 293 if (driCheckOption(&userInitOptions, "csmt_force", DRI_INT)) 294 ctx->base.csmt_force = driQueryOptioni(&userInitOptions, "csmt_force"); 295 else 296 ctx->base.csmt_force = -1; 297 298 if (driCheckOption(&userInitOptions, "dynamic_texture_workaround", DRI_BOOL)) 299 ctx->base.dynamic_texture_workaround = driQueryOptionb(&userInitOptions, "dynamic_texture_workaround"); 300 else 301 ctx->base.dynamic_texture_workaround = FALSE; 302 303 if (driCheckOption(&userInitOptions, "shader_inline_constants", DRI_BOOL)) 304 ctx->base.shader_inline_constants = driQueryOptionb(&userInitOptions, "shader_inline_constants"); 305 else 306 ctx->base.shader_inline_constants = FALSE; 307 308 driDestroyOptionCache(&userInitOptions); 309 driDestroyOptionInfo(&defaultInitOptions); 310 311 /* wrap it to create a software screen that can share resources */ 312 if (pipe_loader_sw_probe_wrapped(&ctx->swdev, ctx->base.hal)) 313 ctx->base.ref = pipe_loader_create_screen(ctx->swdev); 314 315 if (!ctx->base.ref) { 316 ERR("Couldn't wrap drm screen to swrast screen. Software devices " 317 "will be unavailable.\n"); 318 } 319 320 /* read out PCI info */ 321 read_descriptor(&ctx->base, fd, override_vendorid); 322 323 /* create and return new ID3DAdapter9 */ 324 hr = NineAdapter9_new(&ctx->base, (struct NineAdapter9 **)ppAdapter); 325 if (FAILED(hr)) { 326 drm_destroy(&ctx->base); 327 return hr; 328 } 329 330 return D3D_OK; 331} 332 333const struct D3DAdapter9DRM drm9_desc = { 334 .major_version = D3DADAPTER9DRM_MAJOR, 335 .minor_version = D3DADAPTER9DRM_MINOR, 336 .create_adapter = drm_create_adapter 337}; 338