From: Vitaly V. B. <vi...@bu...> - 2012-12-08 12:00:15
|
Hello! AMD ADL is a part of the proprietary AMD Catalyst driver that is capable to provide access to displays' DDC interface. And it looks like there is no other way to implement DDC/CI on a Catalyst (direct PCI access for newer adapters is broken, no /dev/i2c either). Patch provides adapter and display enumeration (probing) and access to the corresponding i2c bus functionality (EDID, DDC/CI). Device naming scheme is like this: "adl:<adapter>:<display>". Please note that for unknown reason ADL thinks that there's multiple active adapters and yet they're the same. diff --git a/src/lib/amd_adl.c b/src/lib/amd_adl.c new file mode 100644 index 0000000..40ca909 --- /dev/null +++ b/src/lib/amd_adl.c @@ -0,0 +1,306 @@ +/* + ddc/ci interface functions header + Copyright(c) 2012 Vitaly V. Bursov (vi...@bu...) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "config.h" + +#ifdef HAVE_AMDADL +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> +#include <dlfcn.h> + +#define MAX_DISPLAYS (64) + +#ifndef LINUX +#define LINUX /* not Windows */ +#endif +#include <ADL/adl_sdk.h> + +#include "amd_adl.h" +#include "ddcci.h" + +#if 1 +# define D(x) +#else +# define D(x) x +#endif + +struct adl_state { + int initialized; + + void *lib; + + int (*ADL_Main_Control_Create)(ADL_MAIN_MALLOC_CALLBACK, int ); + int (*ADL_Main_Control_Destroy)(); + + int (*ADL_Adapter_NumberOfAdapters_Get)(int *lpNumAdapters); + int (*ADL_Adapter_AdapterInfo_Get)(LPAdapterInfo lpInfo, int iInputSize); + int (*ADL_Display_NumberOfDisplays_Get)(int iAdapterIndex, int *lpNumDisplays); + int (*ADL_Display_DisplayInfo_Get)(int iAdapterIndex, int *lpNumDisplays, ADLDisplayInfo **lppInfo, int iForceDetect); + int (*ADL_Display_DDCBlockAccess_Get)(int iAdapterIndex, int iDisplayIndex, int iOption, int iCommandIndex, int iSendMsgLen, char *lpucSendMsgBuf, int *lpulRecvMsgLen, char *lpucRecvMsgBuf); + + struct _displays { + int adapter_index; + int display_index; + } displays[MAX_DISPLAYS]; + int displays_count; +}; + +static struct adl_state *adl; + +static void* __stdcall adl_malloc (int size) +{ + void* buffer = malloc (size); + if (buffer) + memset(buffer, 0, size); + return buffer; +} + +static void __stdcall adl_free ( void **buffer ) +{ + if (*buffer != NULL) { + free (*buffer); + *buffer = NULL; + } +} + +int amd_adl_get_displays_count() +{ + if (!adl->initialized) + return -1; + + return adl->displays_count; +} + +int amd_adl_get_display(int idx, int *adapter, int *display) +{ + if (!adl->initialized) + return -1; + + if (idx < 0 || idx >= adl->displays_count) + return -1; + + if (adapter) + *adapter = adl->displays[idx].adapter_index; + if (display) + *display = adl->displays[idx].display_index; + + return 0; +} + +int amd_adl_check_display(int adapter, int display) +{ + int i; + + if (!adl->initialized) + return -1; + + for (i=0;i<adl->displays_count;i++){ + if (adl->displays[i].adapter_index == adapter && + adl->displays[i].display_index == display) + return 0; + } + return -1; +} + +int amd_adl_i2c_read(int adapter, int display, unsigned int addr, unsigned char *buf, unsigned int len) +{ + int res; + char wbuf = addr << 1 | 1; + + res = adl->ADL_Display_DDCBlockAccess_Get(adapter, display, 0, 0, 1, &wbuf, (int*)&len, (char*)buf); + + D(fprintf(stderr, " >>>>>>>> adl i2c r on %d:%d a %x l %d err %d\n", adapter, display, addr, len, res)); + + if (res != ADL_OK){ + return -1; + } + + return len; +} + +int amd_adl_i2c_write(int adapter, int display, unsigned int addr, unsigned char *buf, unsigned int len) +{ + int res, rlen; + char *wbuf = alloca(len+1); + + wbuf[0] = addr << 1; + memcpy(&wbuf[1], buf, len); + + rlen = 0; + res = adl->ADL_Display_DDCBlockAccess_Get(adapter, display, 0, 0, len+1, wbuf, &rlen, NULL); + + D(fprintf(stderr, " >>>>>>>> adl i2c w on %d:%d a %x l %d err %d\n", adapter, display, addr, len, res)); + + if (res != ADL_OK){ + return -1; + } + + return len; +} + + +int amd_adl_init() +{ + int i; + int res; + int adapters_count; + AdapterInfo *adapter_info; + + adl = adl_malloc(sizeof(struct adl_state)); + + if (!adl){ + fprintf(stderr, "ADL error: malloc failed\n"); + return 0; + } + + adl->lib = dlopen("libatiadlxx.so", RTLD_LAZY|RTLD_GLOBAL); + if (!adl->lib){ + if (get_verbosity()) + perror("ADL error: dlopen() failed\n"); + return 0; + } +#define LOADFUNC(_n_) \ + do { \ + adl->_n_ = dlsym(adl->lib, #_n_); \ + if (!adl->_n_) { \ + fprintf(stderr, "ADL error: loading symbol %s\n", #_n_); \ + return 0; \ + } \ + } while (0) + + LOADFUNC(ADL_Main_Control_Create); + LOADFUNC(ADL_Main_Control_Destroy); + + LOADFUNC(ADL_Adapter_NumberOfAdapters_Get); + LOADFUNC(ADL_Adapter_AdapterInfo_Get); + LOADFUNC(ADL_Display_NumberOfDisplays_Get); + LOADFUNC(ADL_Display_DisplayInfo_Get); + LOADFUNC(ADL_Display_DDCBlockAccess_Get); + +#undef LOADFUNC + + res = adl->ADL_Main_Control_Create(adl_malloc, 1); // retrieve adapter information only for adapters that are physically present and enabled + + if (res != ADL_OK){ + if (get_verbosity()) + fprintf(stderr, "Failed to initialize ADL: %d\n", res); + return 0; + } + + res = adl->ADL_Adapter_NumberOfAdapters_Get(&adapters_count); + if (res != ADL_OK){ + if (get_verbosity()) + fprintf(stderr, "Failed to get number of ADL adapters: %d\n", res); + return 0; + } + + if (adapters_count < 1){ + if (get_verbosity()) + fprintf(stderr, "No ADL adapters found.\n"); + return 0; + } + + adapter_info = adl_malloc(sizeof(AdapterInfo) * adapters_count); + if (!adapter_info){ + fprintf(stderr, "ADL error: malloc failed\n"); + return 0; + } + + res = adl->ADL_Adapter_AdapterInfo_Get(adapter_info, sizeof(AdapterInfo) * adapters_count); + if (res != ADL_OK){ + fprintf(stderr, "Failed to get ADL adapters info: %d\n", res); + return 0; + } + + for (i=0;i<adapters_count;i++){ + int aidx = adapter_info[i].iAdapterIndex; + int numdisplays; + int j; + ADLDisplayInfo *display_info; + + if (adl->ADL_Display_DisplayInfo_Get(aidx, &numdisplays, &display_info, 0) != ADL_OK) + continue; + + D(printf("\t ================================\n")); + D(printf("\t %d: %s - %s %d %x:%x.%x %s\n", adapter_info[i].iAdapterIndex, adapter_info[i].strAdapterName, adapter_info[i].strDisplayName, + adapter_info[i].iPresent, + adapter_info[i].iBusNumber, + adapter_info[i].iDeviceNumber, + adapter_info[i].iFunctionNumber, + adapter_info[i].strUDID)); + + for (j=0;j<numdisplays;j++){ + int didx; + + if ((display_info[j].iDisplayInfoValue & ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED) && + (display_info[j].iDisplayInfoValue & ADL_DISPLAY_DISPLAYINFO_DISPLAYMAPPED)){ + + didx = display_info[j].displayID.iDisplayLogicalIndex; + + D(printf("\t\t found display %s at %d:%d\n", + display_info[j].strDisplayName, aidx, didx)); + + adl->displays[adl->displays_count].adapter_index = aidx; + adl->displays[adl->displays_count].display_index = didx; + adl->displays_count++; + if (adl->displays_count >= MAX_DISPLAYS){ + break; + } + } + } + + adl_free((void**)&display_info); + + if (adl->displays_count >= MAX_DISPLAYS){ + break; + } + } + + adl_free((void**)&adapter_info); + + D(fprintf(stderr, "adl initialized, %d displays\n", adl->displays_count)); + + adl->initialized = 1; + return 1; +} + +void amd_adl_free() +{ + if (!adl) + return; + + adl->ADL_Main_Control_Destroy(); + + if (adl->lib){ + dlclose(adl->lib); + adl->lib = NULL; + } + + adl_free((void**)&adl); +} + +#endif /* HAVE_AMDADL */ + diff --git a/src/lib/amd_adl.h b/src/lib/amd_adl.h new file mode 100644 index 0000000..d6c7bc1 --- /dev/null +++ b/src/lib/amd_adl.h @@ -0,0 +1,33 @@ +/* + ddc/ci interface functions header + Copyright(c) 2012 Vitaly V. Bursov (vi...@bu...) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef AMD_ADL_H +#define AMD_ADL_H + +int amd_adl_init(); +void amd_adl_free(); + +int amd_adl_get_displays_count(); +int amd_adl_get_display(int idx, int *adapter, int *display); +int amd_adl_check_display(int adapter, int display); + +int amd_adl_i2c_read(int adapter, int display, unsigned int addr, unsigned char *buf, unsigned int len); +int amd_adl_i2c_write(int adapter, int display, unsigned int addr, unsigned char *buf, unsigned int len); + +#endif /* AMD_ADL_H */ diff --git a/src/lib/ddcci.c b/src/lib/ddcci.c index 4492df5..186d597 100644 --- a/src/lib/ddcci.c +++ b/src/lib/ddcci.c @@ -38,6 +38,7 @@ #include <sys/stat.h> #include "ddcci.h" +#include "amd_adl.h" #include "conf.h" @@ -239,12 +240,22 @@ int ddcci_init(char* usedatadir) printf(_("Failed to initialize ddccontrol database...\n")); return 0; } +#ifdef HAVE_AMDADL + if (!amd_adl_init()){ + if (verbosity) { + printf(_("Failed to initialize ADL...\n")); + } + } +#endif return ddcpci_init(); } void ddcci_release() { ddcpci_release(); ddcci_release_db(); +#ifdef HAVE_AMDADL + amd_adl_free(); +#endif } /* write len bytes (stored in buf) to i2c address addr */ @@ -318,6 +329,12 @@ static int i2c_write(struct monitor* mon, unsigned int addr, unsigned char *buf, return adata.status; } #endif +#ifdef HAVE_AMDADL + case type_adl: + { + return amd_adl_i2c_write(mon->adl_adapter, mon->adl_display, addr, buf, len); + } +#endif default: return -1; } @@ -396,6 +413,12 @@ static int i2c_read(struct monitor* mon, unsigned int addr, unsigned char *buf, return ret - ANSWER_SIZE; } #endif +#ifdef HAVE_AMDADL + case type_adl: + { + return amd_adl_i2c_read(mon->adl_adapter, mon->adl_display, addr, buf, len); + } +#endif default: return -1; } @@ -929,6 +952,23 @@ static int ddcci_open_with_addr(struct monitor* mon, const char* filename, int a mon->type = pci; } #endif +#ifdef HAVE_AMDADL + else if (strncmp(filename, "adl:", 4) == 0) { + mon->adl_adapter = -1; + mon->adl_display = -1; + if (sscanf(filename, "adl:%d:%d", &mon->adl_adapter, &mon->adl_display) != 2){ + fprintf(stderr, _("Invalid filename (%s).\n"), filename); + return -3; + } + + if (amd_adl_check_display(mon->adl_adapter, mon->adl_display)){ + fprintf(stderr, _("ADL display not found (%s).\n"), filename); + return -3; + } + + mon->type = type_adl; + } +#endif else { fprintf(stderr, _("Invalid filename (%s).\n"), filename); return -3; @@ -1167,6 +1207,28 @@ struct monitorlist* ddcci_probe() { closedir(dirp); +#ifdef HAVE_AMDADL + /* ADL probe */ + int adl_disp; + + for (adl_disp=0; adl_disp<amd_adl_get_displays_count(); adl_disp++){ + int adapter, display; + if (amd_adl_get_display(adl_disp, &adapter, &display)) + break; + + filename = malloc(64); + snprintf(filename, 64, "adl:%d:%d", adapter, display); + if (verbosity) { + printf(_("Found ADL display (%s)\n"), filename); + } + ddcci_probe_device(filename, ¤t, &last); + if (!verbosity) { + printf("."); + fflush(stdout); + } + } +#endif + if (!verbosity) printf("\n"); diff --git a/src/lib/ddcci.h b/src/lib/ddcci.h index c058051..cac907b 100644 --- a/src/lib/ddcci.h +++ b/src/lib/ddcci.h @@ -63,6 +63,9 @@ struct caps { struct monitor { int fd; unsigned int addr; +#ifdef HAVE_AMDADL + int adl_adapter, adl_display; +#endif char pnpid[8]; unsigned char digital; /* 0 - digital, 1 - analog */ struct timeval last; @@ -76,6 +79,9 @@ struct monitor { #ifdef HAVE_DDCPCI ,pci #endif +#ifdef HAVE_AMDADL + ,type_adl +#endif } type; int probing; /* are we probing? */ |