diff --git a/Makefile.common b/Makefile.common index ec1d8b609b..f2ce03513a 100644 --- a/Makefile.common +++ b/Makefile.common @@ -2112,6 +2112,9 @@ endif ifeq ($(HAVE_ODROIDGO2), 1) DEFINES += -DHAVE_ODROIDGO2 - LIBS += -lrk_rga + LIBS += -lrga + INCLUDE_DIRS += -I$(DEPS_DIR)/libgo2/include + OBJ += $(DEPS_DIR)/libgo2/src/display.o \ + $(DEPS_DIR)/libgo2/src/queue.o endif ################################## diff --git a/deps/libgo2/LICENSE b/deps/libgo2/LICENSE new file mode 100644 index 0000000000..18aab7d57e --- /dev/null +++ b/deps/libgo2/LICENSE @@ -0,0 +1,16 @@ +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \ No newline at end of file diff --git a/deps/libgo2/Makefile b/deps/libgo2/Makefile new file mode 100644 index 0000000000..47536696ab --- /dev/null +++ b/deps/libgo2/Makefile @@ -0,0 +1,34 @@ +# GNU Make solution makefile autogenerated by Premake +# Type "make help" for usage help + +ifndef config + config=debug +endif +export config + +PROJECTS := go2 + +.PHONY: all clean help $(PROJECTS) + +all: $(PROJECTS) + +go2: + @echo "==== Building go2 ($(config)) ====" + @${MAKE} --no-print-directory -C build/gmake -f Makefile + +clean: + @${MAKE} --no-print-directory -C build/gmake -f Makefile clean + +help: + @echo "Usage: make [config=name] [target]" + @echo "" + @echo "CONFIGURATIONS:" + @echo " debug" + @echo " release" + @echo "" + @echo "TARGETS:" + @echo " all (default)" + @echo " clean" + @echo " go2" + @echo "" + @echo "For more information, see http://industriousone.com/premake/quick-start" diff --git a/deps/libgo2/README.md b/deps/libgo2/README.md new file mode 100644 index 0000000000..10f33dda4b --- /dev/null +++ b/deps/libgo2/README.md @@ -0,0 +1 @@ +libgo2 - Support library for the ODROID-GO Advance diff --git a/deps/libgo2/build/gmake/Makefile b/deps/libgo2/build/gmake/Makefile new file mode 100644 index 0000000000..564ade2720 --- /dev/null +++ b/deps/libgo2/build/gmake/Makefile @@ -0,0 +1,144 @@ +# GNU Make project makefile autogenerated by Premake +ifndef config + config=debug +endif + +ifndef verbose + SILENT = @ +endif + +ifndef CC + CC = gcc +endif + +ifndef CXX + CXX = g++ +endif + +ifndef AR + AR = ar +endif + +ifeq ($(config),debug) + OBJDIR = obj/Debug + TARGETDIR = ../.. + TARGET = $(TARGETDIR)/libgo2.so + DEFINES += -DDEBUG + INCLUDES += -I/usr/include/libdrm + CPPFLAGS += -MMD -MP $(DEFINES) $(INCLUDES) + CFLAGS += $(CPPFLAGS) $(ARCH) -g -fPIC -Wall + CXXFLAGS += $(CFLAGS) + LDFLAGS += -shared -lopenal -lEGL -levdev -lgbm -lpthread -ldrm -lm -lrga -lpng -lasound + LIBS += + RESFLAGS += $(DEFINES) $(INCLUDES) + LDDEPS += + LINKCMD = $(CC) -o $(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(ARCH) $(LIBS) + define PREBUILDCMDS + endef + define PRELINKCMDS + endef + define POSTBUILDCMDS + endef +endif + +ifeq ($(config),release) + OBJDIR = obj/Release + TARGETDIR = ../.. + TARGET = $(TARGETDIR)/libgo2.so + DEFINES += -DNDEBUG + INCLUDES += -I/usr/include/libdrm + CPPFLAGS += -MMD -MP $(DEFINES) $(INCLUDES) + CFLAGS += $(CPPFLAGS) $(ARCH) -O2 -fPIC -Wall + CXXFLAGS += $(CFLAGS) + LDFLAGS += -s -shared -lopenal -lEGL -levdev -lgbm -lpthread -ldrm -lm -lrga -lpng -lasound + LIBS += + RESFLAGS += $(DEFINES) $(INCLUDES) + LDDEPS += + LINKCMD = $(CC) -o $(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(ARCH) $(LIBS) + define PREBUILDCMDS + endef + define PRELINKCMDS + endef + define POSTBUILDCMDS + endef +endif + +OBJECTS := \ + $(OBJDIR)/audio.o \ + $(OBJDIR)/input.o \ + $(OBJDIR)/display.o \ + $(OBJDIR)/queue.o \ + +RESOURCES := \ + +SHELLTYPE := msdos +ifeq (,$(ComSpec)$(COMSPEC)) + SHELLTYPE := posix +endif +ifeq (/bin,$(findstring /bin,$(SHELL))) + SHELLTYPE := posix +endif + +.PHONY: clean prebuild prelink + +all: $(TARGETDIR) $(OBJDIR) prebuild prelink $(TARGET) + @: + +$(TARGET): $(GCH) $(OBJECTS) $(LDDEPS) $(RESOURCES) + @echo Linking go2 + $(SILENT) $(LINKCMD) + $(POSTBUILDCMDS) + +$(TARGETDIR): + @echo Creating $(TARGETDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(TARGETDIR) +else + $(SILENT) mkdir $(subst /,\\,$(TARGETDIR)) +endif + +$(OBJDIR): + @echo Creating $(OBJDIR) +ifeq (posix,$(SHELLTYPE)) + $(SILENT) mkdir -p $(OBJDIR) +else + $(SILENT) mkdir $(subst /,\\,$(OBJDIR)) +endif + +clean: + @echo Cleaning go2 +ifeq (posix,$(SHELLTYPE)) + $(SILENT) rm -f $(TARGET) + $(SILENT) rm -rf $(OBJDIR) +else + $(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET)) + $(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR)) +endif + +prebuild: + $(PREBUILDCMDS) + +prelink: + $(PRELINKCMDS) + +ifneq (,$(PCH)) +$(GCH): $(PCH) + @echo $(notdir $<) + -$(SILENT) cp $< $(OBJDIR) + $(SILENT) $(CC) $(CFLAGS) -o "$@" -c "$<" +endif + +$(OBJDIR)/audio.o: ../../src/audio.c + @echo $(notdir $<) + $(SILENT) $(CC) $(CFLAGS) -o "$@" -c "$<" +$(OBJDIR)/input.o: ../../src/input.c + @echo $(notdir $<) + $(SILENT) $(CC) $(CFLAGS) -o "$@" -c "$<" +$(OBJDIR)/display.o: ../../src/display.c + @echo $(notdir $<) + $(SILENT) $(CC) $(CFLAGS) -o "$@" -c "$<" +$(OBJDIR)/queue.o: ../../src/queue.c + @echo $(notdir $<) + $(SILENT) $(CC) $(CFLAGS) -o "$@" -c "$<" + +-include $(OBJECTS:%.o=%.d) diff --git a/deps/libgo2/include/go2/display.h b/deps/libgo2/include/go2/display.h new file mode 100644 index 0000000000..cbe30a9c2a --- /dev/null +++ b/deps/libgo2/include/go2/display.h @@ -0,0 +1,105 @@ +#pragma once + +/* +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + + +typedef struct go2_display go2_display_t; +typedef struct go2_surface go2_surface_t; +typedef struct go2_frame_buffer go2_frame_buffer_t; +typedef struct go2_presenter go2_presenter_t; + +typedef enum go2_rotation +{ + GO2_ROTATION_DEGREES_0 = 0, + GO2_ROTATION_DEGREES_90, + GO2_ROTATION_DEGREES_180, + GO2_ROTATION_DEGREES_270 +} go2_rotation_t; + +typedef struct go2_context_attributes +{ + int major; + int minor; + int red_bits; + int green_bits; + int blue_bits; + int alpha_bits; + int depth_bits; + int stencil_bits; +} go2_context_attributes_t; + +typedef struct go2_context go2_context_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +go2_display_t* go2_display_create(); +void go2_display_destroy(go2_display_t* display); +int go2_display_width_get(go2_display_t* display); +int go2_display_height_get(go2_display_t* display); +void go2_display_present(go2_display_t* display, go2_frame_buffer_t* frame_buffer); +uint32_t go2_display_backlight_get(go2_display_t* display); +void go2_display_backlight_set(go2_display_t* display, uint32_t value); + + +int go2_drm_format_get_bpp(uint32_t format); + + +go2_surface_t* go2_surface_create(go2_display_t* display, int width, int height, uint32_t format); +void go2_surface_destroy(go2_surface_t* surface); +int go2_surface_width_get(go2_surface_t* surface); +int go2_surface_height_get(go2_surface_t* surface); +uint32_t go2_surface_format_get(go2_surface_t* surface); +int go2_surface_stride_get(go2_surface_t* surface); +go2_display_t* go2_surface_display_get(go2_surface_t* surface); +int go2_surface_prime_fd(go2_surface_t* surface); +void* go2_surface_map(go2_surface_t* surface); +void go2_surface_unmap(go2_surface_t* surface); +void go2_surface_blit(go2_surface_t* srcSurface, int srcX, int srcY, int srcWidth, int srcHeight, + go2_surface_t* dstSurface, int dstX, int dstY, int dstWidth, int dstHeight, + go2_rotation_t rotation); +int go2_surface_save_as_png(go2_surface_t* surface, const char* filename); + + +go2_frame_buffer_t* go2_frame_buffer_create(go2_surface_t* surface); +void go2_frame_buffer_destroy(go2_frame_buffer_t* frame_buffer); +go2_surface_t* go2_frame_buffer_surface_get(go2_frame_buffer_t* frame_buffer); + + +go2_presenter_t* go2_presenter_create(go2_display_t* display, uint32_t format, uint32_t background_color); +void go2_presenter_destroy(go2_presenter_t* presenter); +void go2_presenter_post(go2_presenter_t* presenter, go2_surface_t* surface, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, go2_rotation_t rotation); + + +go2_context_t* go2_context_create(go2_display_t* display, int width, int height, const go2_context_attributes_t* attributes); +void go2_context_destroy(go2_context_t* context); +void* go2_context_egldisplay_get(go2_context_t* context); +void go2_context_make_current(go2_context_t* context); +void go2_context_swap_buffers(go2_context_t* context); +go2_surface_t* go2_context_surface_lock(go2_context_t* context); +void go2_context_surface_unlock(go2_context_t* context, go2_surface_t* surface); + +#ifdef __cplusplus +} +#endif diff --git a/deps/libgo2/include/go2/queue.h b/deps/libgo2/include/go2/queue.h new file mode 100644 index 0000000000..3c6918efcc --- /dev/null +++ b/deps/libgo2/include/go2/queue.h @@ -0,0 +1,37 @@ +#pragma once + +/* +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +typedef struct go2_queue go2_queue_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +go2_queue_t* go2_queue_create(int capacity); +int go2_queue_count_get(go2_queue_t* queue); +void go2_queue_push(go2_queue_t* queue, void* value); +void* go2_queue_pop(go2_queue_t* queue); +void go2_queue_destroy(go2_queue_t* queue); + +#ifdef __cplusplus +} +#endif diff --git a/deps/libgo2/premake4.lua b/deps/libgo2/premake4.lua new file mode 100644 index 0000000000..e416b4cfc3 --- /dev/null +++ b/deps/libgo2/premake4.lua @@ -0,0 +1,22 @@ +#!lua +local output = "./build/" .. _ACTION + +solution "go2_solution" + configurations { "Debug", "Release" } + +project "go2" + location (output) + kind "SharedLib" + language "C" + files { "src/**.h", "src/**.c" } + buildoptions { "-Wall" } + linkoptions { "-lopenal -lEGL -levdev -lgbm -lpthread -ldrm -lm -lrga -lpng -lasound" } + includedirs { "/usr/include/libdrm" } + + configuration "Debug" + flags { "Symbols" } + defines { "DEBUG" } + + configuration "Release" + flags { "Optimize" } + defines { "NDEBUG" } diff --git a/deps/libgo2/src/audio.c b/deps/libgo2/src/audio.c new file mode 100644 index 0000000000..26877e93bc --- /dev/null +++ b/deps/libgo2/src/audio.c @@ -0,0 +1,287 @@ +/* +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "audio.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define SOUND_SAMPLES_SIZE (2048) +#define SOUND_CHANNEL_COUNT 2 + + +typedef struct go2_audio +{ + ALsizei frequency; + ALCdevice *device; + ALCcontext *context; + ALuint source; + bool isAudioInitialized; +} go2_audio_t; + + +go2_audio_t* go2_audio_create(int frequency) +{ + go2_audio_t* result = malloc(sizeof(*result)); + if (!result) + { + printf("malloc failed.\n"); + goto out; + } + + memset(result, 0, sizeof(*result)); + + + result->frequency = frequency; + + result->device = alcOpenDevice(NULL); + if (!result->device) + { + printf("alcOpenDevice failed.\n"); + goto err_00; + } + + result->context = alcCreateContext(result->device, NULL); + if (!alcMakeContextCurrent(result->context)) + { + printf("alcMakeContextCurrent failed.\n"); + goto err_01; + } + + alGenSources((ALuint)1, &result->source); + + alSourcef(result->source, AL_PITCH, 1); + alSourcef(result->source, AL_GAIN, 1); + alSource3f(result->source, AL_POSITION, 0, 0, 0); + alSource3f(result->source, AL_VELOCITY, 0, 0, 0); + alSourcei(result->source, AL_LOOPING, AL_FALSE); + + //memset(audioBuffer, 0, AUDIOBUFFER_LENGTH * sizeof(short)); + + const int BUFFER_COUNT = 4; + for (int i = 0; i < BUFFER_COUNT; ++i) + { + ALuint buffer; + alGenBuffers((ALuint)1, &buffer); + alBufferData(buffer, AL_FORMAT_STEREO16, NULL, 0, frequency); + alSourceQueueBuffers(result->source, 1, &buffer); + } + + alSourcePlay(result->source); + + result->isAudioInitialized = true; + + // testing + //uint32_t vol = go2_audio_volume_get(result); + //printf("audio: vol=%d\n", vol); + //go2_audio_path_get(result); + + + return result; + + +err_01: + alcCloseDevice(result->device); + +err_00: + free(result); + +out: + return NULL; +} + +void go2_audio_destroy(go2_audio_t* audio) +{ + alDeleteSources(1, &audio->source); + alcDestroyContext(audio->context); + alcCloseDevice(audio->device); + + free(audio); +} + +void go2_audio_submit(go2_audio_t* audio, const short* data, int frames) +{ + if (!audio || !audio->isAudioInitialized) return; + + + if (!alcMakeContextCurrent(audio->context)) + { + printf("alcMakeContextCurrent failed.\n"); + return; + } + + ALint processed = 0; + while(!processed) + { + alGetSourceiv(audio->source, AL_BUFFERS_PROCESSED, &processed); + + if (!processed) + { + sleep(0); + //printf("Audio overflow.\n"); + //return; + } + } + + ALuint openALBufferID; + alSourceUnqueueBuffers(audio->source, 1, &openALBufferID); + + ALuint format = AL_FORMAT_STEREO16; + + int dataByteLength = frames * sizeof(short) * SOUND_CHANNEL_COUNT; + alBufferData(openALBufferID, format, data, dataByteLength, audio->frequency); + + alSourceQueueBuffers(audio->source, 1, &openALBufferID); + + ALint result; + alGetSourcei(audio->source, AL_SOURCE_STATE, &result); + + if (result != AL_PLAYING) + { + alSourcePlay(audio->source); + } +} + +uint32_t go2_audio_volume_get(go2_audio_t* audio) +{ + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + const char *card = "default"; + const char *selem_name = "Playback"; + + snd_mixer_open(&handle, 0); + snd_mixer_attach(handle, card); + snd_mixer_selem_register(handle, NULL, NULL); + snd_mixer_load(handle); + + snd_mixer_selem_id_alloca(&sid); + snd_mixer_selem_id_set_index(sid, 0); + snd_mixer_selem_id_set_name(sid, selem_name); + + snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); + + long min; + long max; + snd_mixer_selem_get_playback_volume_range(elem, &min, &max); + + + //snd_mixer_selem_set_playback_volume_all(elem, value / 100.0f * max); + long volume; + snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &volume); + + snd_mixer_close(handle); + + uint32_t result = volume / (float)max * 100.0f; + //printf("volume: min=%ld, max=%ld, volume=%ld, result=%d\n", min, max, volume, result); + + return result; +} + +void go2_audio_volume_set(go2_audio_t* audio, uint32_t value) +{ + // https://gist.github.com/wolfg1969/3575700 + + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + const char *card = "default"; + const char *selem_name = "Playback"; + + snd_mixer_open(&handle, 0); + snd_mixer_attach(handle, card); + snd_mixer_selem_register(handle, NULL, NULL); + snd_mixer_load(handle); + + snd_mixer_selem_id_alloca(&sid); + snd_mixer_selem_id_set_index(sid, 0); + snd_mixer_selem_id_set_name(sid, selem_name); + + snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); + + long min; + long max; + snd_mixer_selem_get_playback_volume_range(elem, &min, &max); + //printf("volume: min=%ld, max=%ld\n", min, max); + + snd_mixer_selem_set_playback_volume_all(elem, value / 100.0f * max); + + snd_mixer_close(handle); +} + +go2_audio_path_t go2_audio_path_get(go2_audio_t* audio) +{ + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + const char *card = "default"; + const char *selem_name = "Playback Path"; + + snd_mixer_open(&handle, 0); + snd_mixer_attach(handle, card); + snd_mixer_selem_register(handle, NULL, NULL); + snd_mixer_load(handle); + + snd_mixer_selem_id_alloca(&sid); + snd_mixer_selem_id_set_index(sid, 0); + snd_mixer_selem_id_set_name(sid, selem_name); + + snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); + + unsigned int value; + snd_mixer_selem_get_enum_item(elem, SND_MIXER_SCHN_MONO, &value); + + // char name[128]; + // snd_mixer_selem_get_enum_item_name(elem, value, 128, name); + // printf("audio path: value=%d [%s]\n", value, name); + + snd_mixer_close(handle); + + return (go2_audio_path_t)value; +} + +void go2_audio_path_set(go2_audio_t* audio, go2_audio_path_t value) +{ + snd_mixer_t *handle; + snd_mixer_selem_id_t *sid; + const char *card = "default"; + const char *selem_name = "Playback Path"; + + snd_mixer_open(&handle, 0); + snd_mixer_attach(handle, card); + snd_mixer_selem_register(handle, NULL, NULL); + snd_mixer_load(handle); + + snd_mixer_selem_id_alloca(&sid); + snd_mixer_selem_id_set_index(sid, 0); + snd_mixer_selem_id_set_name(sid, selem_name); + + snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); + + snd_mixer_selem_set_enum_item(elem, SND_MIXER_SCHN_MONO, (unsigned int)value); + + snd_mixer_close(handle); +} diff --git a/deps/libgo2/src/audio.h b/deps/libgo2/src/audio.h new file mode 100644 index 0000000000..bf6dd3da51 --- /dev/null +++ b/deps/libgo2/src/audio.h @@ -0,0 +1,52 @@ +#pragma once + +/* +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + + +typedef struct go2_audio go2_audio_t; + +typedef enum +{ + Audio_Path_Off = 0, + Audio_Path_Speaker, + Audio_Path_Headphones, + Audio_Path_SpeakerAndHeadphones, + + Audio_Path_MAX = 0x7fffffff +} go2_audio_path_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +go2_audio_t* go2_audio_create(int frequency); +void go2_audio_destroy(go2_audio_t* audio); +void go2_audio_submit(go2_audio_t* audio, const short* data, int frames); +uint32_t go2_audio_volume_get(go2_audio_t* audio); +void go2_audio_volume_set(go2_audio_t* audio, uint32_t value); +go2_audio_path_t go2_audio_path_get(go2_audio_t* audio); +void go2_audio_path_set(go2_audio_t* audio, go2_audio_path_t value); + +#ifdef __cplusplus +} +#endif diff --git a/deps/libgo2/src/display.c b/deps/libgo2/src/display.c new file mode 100644 index 0000000000..52b2827cae --- /dev/null +++ b/deps/libgo2/src/display.c @@ -0,0 +1,1367 @@ +/* +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "display.h" + +#include "queue.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define EGL_EGLEXT_PROTOTYPES +//#define GL_GLEXT_PROTOTYPES +#include +#include +// #include +// #include + +#include + + +typedef struct go2_display +{ + int fd; + uint32_t connector_id; + drmModeModeInfo mode; + uint32_t width; + uint32_t height; + uint32_t crtc_id; +} go2_display_t; + +typedef struct go2_surface +{ + go2_display_t* display; + uint32_t gem_handle; + uint64_t size; + int width; + int height; + int stride; + uint32_t format; + int prime_fd; + bool is_mapped; + uint8_t* map; +} go2_surface_t; + +typedef struct go2_frame_buffer +{ + go2_surface_t* surface; + uint32_t fb_id; +} go2_frame_buffer_t; + +typedef struct go2_presenter +{ + go2_display_t* display; + uint32_t format; + uint32_t background_color; + go2_queue_t* freeFrameBuffers; + go2_queue_t* usedFrameBuffers; + pthread_mutex_t queueMutex; + pthread_t renderThread; + sem_t freeSem; + sem_t usedSem; + volatile bool terminating; +} go2_presenter_t; + + +go2_display_t* go2_display_create() +{ + int i; + + + go2_display_t* result = malloc(sizeof(*result)); + if (!result) + { + printf("malloc failed.\n"); + goto out; + } + + memset(result, 0, sizeof(*result)); + + + // Open device + result->fd = open("/dev/dri/card0", O_RDWR); + if (result->fd < 0) + { + printf("open /dev/dri/card0 failed.\n"); + goto err_00; + } + + + drmModeRes* resources = drmModeGetResources(result->fd); + if (!resources) + { + printf("drmModeGetResources failed: %s\n", strerror(errno)); + goto err_01; + } + + + // Find connector + drmModeConnector* connector; + for (i = 0; i < resources->count_connectors; i++) + { + connector = drmModeGetConnector(result->fd, resources->connectors[i]); + if (connector->connection == DRM_MODE_CONNECTED) { + break; + } + + drmModeFreeConnector(connector); + connector = NULL; + } + + if (!connector) + { + printf("DRM_MODE_CONNECTED not found.\n"); + goto err_02; + } + + result->connector_id = connector->connector_id; + + + // Find prefered mode + drmModeModeInfo* mode; + for (i = 0; i < connector->count_modes; i++) + { + drmModeModeInfo *current_mode = &connector->modes[i]; + if (current_mode->type & DRM_MODE_TYPE_PREFERRED) + { + mode = current_mode; + break; + } + + mode = NULL; + } + + if (!mode) + { + printf("DRM_MODE_TYPE_PREFERRED not found.\n"); + goto err_03; + } + + result->mode = *mode; + result->width = mode->hdisplay; + result->height = mode->vdisplay; + + + // Find encoder + drmModeEncoder* encoder; + for (i = 0; i < resources->count_encoders; i++) + { + encoder = drmModeGetEncoder(result->fd, resources->encoders[i]); + if (encoder->encoder_id == connector->encoder_id) + { + break; + } + + drmModeFreeEncoder(encoder); + encoder = NULL; + } + + if (!encoder) + { + + printf("could not find encoder!\n"); + goto err_03; + } + + result->crtc_id = encoder->crtc_id; + + drmModeFreeEncoder(encoder); + drmModeFreeConnector(connector); + drmModeFreeResources(resources); + + return result; + + +err_03: + drmModeFreeConnector(connector); + +err_02: + drmModeFreeResources(resources); + +err_01: + close(result->fd); + +err_00: + free(result); + +out: + return NULL; +} + + +void go2_display_destroy(go2_display_t* display) +{ + close(display->fd); + free(display); +} + +int go2_display_width_get(go2_display_t* display) +{ + return display->width; +} + +int go2_display_height_get(go2_display_t* display) +{ + return display->height; +} + +void go2_display_present(go2_display_t* display, go2_frame_buffer_t* frame_buffer) +{ + int ret = drmModeSetCrtc(display->fd, display->crtc_id, frame_buffer->fb_id, 0, 0, &display->connector_id, 1, &display->mode); + if (ret) + { + printf("drmModeSetCrtc failed.\n"); + } +} + +const char* BACKLIGHT_BRIGHTNESS_NAME = "/sys/class/backlight/backlight/brightness"; +const char* BACKLIGHT_BRIGHTNESS_MAX_NAME = "/sys/class/backlight/backlight/max_brightness"; +#define BACKLIGHT_BUFFER_SIZE (127) + +uint32_t go2_display_backlight_get(go2_display_t* display) +{ + int fd; + int max = 255; + int value = 0; + char buffer[BACKLIGHT_BUFFER_SIZE + 1]; + + fd = open(BACKLIGHT_BRIGHTNESS_MAX_NAME, O_RDONLY); + if (fd > 0) + { + memset(buffer, 0, BACKLIGHT_BUFFER_SIZE + 1); + + ssize_t count = read(fd, buffer, BACKLIGHT_BUFFER_SIZE); + if (count > 0) + { + max = atoi(buffer); + } + + close(fd); + + if (max == 0) return 0; + } + + fd = open(BACKLIGHT_BRIGHTNESS_NAME, O_RDONLY); + if (fd > 0) + { + memset(buffer, 0, BACKLIGHT_BUFFER_SIZE + 1); + + ssize_t count = read(fd, buffer, BACKLIGHT_BUFFER_SIZE); + if (count > 0) + { + value = atoi(buffer); + } + + close(fd); + } + + float percent = value / (float)max * 100.0f; + return (uint32_t)percent; +} + +void go2_display_backlight_set(go2_display_t* display, uint32_t value) +{ + int fd; + int max = 255; + char buffer[BACKLIGHT_BUFFER_SIZE + 1]; + + + if (value > 100) value = 100; + + fd = open(BACKLIGHT_BRIGHTNESS_MAX_NAME, O_RDONLY); + if (fd > 0) + { + memset(buffer, 0, BACKLIGHT_BUFFER_SIZE + 1); + + ssize_t count = read(fd, buffer, BACKLIGHT_BUFFER_SIZE); + if (count > 0) + { + max = atoi(buffer); + } + + close(fd); + + if (max == 0) return; + } + + fd = open(BACKLIGHT_BRIGHTNESS_NAME, O_WRONLY); + if (fd > 0) + { + float percent = value / 100.0f * (float)max; + sprintf(buffer, "%d\n", (uint32_t)percent); + + //printf("backlight=%d, max=%d\n", (uint32_t)percent, max); + + ssize_t count = write(fd, buffer, strlen(buffer)); + if (count < 0) + { + printf("go2_display_backlight_set write failed.\n"); + } + + close(fd); + } + else + { + printf("go2_display_backlight_set open failed.\n"); + } + +} + + +int go2_drm_format_get_bpp(uint32_t format) +{ + int result; + + switch(format) + { + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRX4444: + + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_BGRA4444: + + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRX5551: + + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_BGRA5551: + + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + result = 16; + break; + + + case DRM_FORMAT_RGB888: + case DRM_FORMAT_BGR888: + result = 24; + break; + + + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_RGBX1010102: + case DRM_FORMAT_BGRX1010102: + + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + result = 32; + break; + + + default: + printf("unhandled DRM FORMAT.\n"); + result = 0; + } + + return result; +} + +go2_surface_t* go2_surface_create(go2_display_t* display, int width, int height, uint32_t format) +{ + go2_surface_t* result = malloc(sizeof(*result)); + if (!result) + { + printf("malloc failed.\n"); + goto out; + } + + memset(result, 0, sizeof(*result)); + + + struct drm_mode_create_dumb args = {0}; + args.width = width; + args.height = height; + args.bpp = go2_drm_format_get_bpp(format); + args.flags = 0; + + int io = drmIoctl(display->fd, DRM_IOCTL_MODE_CREATE_DUMB, &args); + if (io < 0) + { + printf("DRM_IOCTL_MODE_CREATE_DUMB failed.\n"); + goto out; + } + + + result->display = display; + result->gem_handle = args.handle; + result->size = args.size; + result->width = width; + result->height = height; + result->stride = args.pitch; + result->format = format; + + return result; + +out: + free(result); + return NULL; +} + +void go2_surface_destroy(go2_surface_t* surface) +{ + struct drm_mode_destroy_dumb args = { 0 }; + args.handle = surface->gem_handle; + + int io = drmIoctl(surface->display->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &args); + if (io < 0) + { + printf("DRM_IOCTL_MODE_DESTROY_DUMB failed.\n"); + } + + free(surface); +} + +int go2_surface_width_get(go2_surface_t* surface) +{ + return surface->width; +} + +int go2_surface_height_get(go2_surface_t* surface) +{ + return surface->height; +} + +uint32_t go2_surface_format_get(go2_surface_t* surface) +{ + return surface->format; +} + +int go2_surface_stride_get(go2_surface_t* surface) +{ + return surface->stride; +} + +go2_display_t* go2_surface_display_get(go2_surface_t* surface) +{ + return surface->display; +} + +int go2_surface_prime_fd(go2_surface_t* surface) +{ + if (surface->prime_fd <= 0) + { + int io = drmPrimeHandleToFD(surface->display->fd, surface->gem_handle, DRM_RDWR | DRM_CLOEXEC, &surface->prime_fd); + if (io < 0) + { + printf("drmPrimeHandleToFD failed.\n"); + goto out; + } + } + + return surface->prime_fd; + +out: + surface->prime_fd = 0; + return 0; +} + +void* go2_surface_map(go2_surface_t* surface) +{ + if (surface->is_mapped) + return surface->map; + + + int prime_fd = go2_surface_prime_fd(surface); + surface->map = mmap(NULL, surface->size, PROT_READ | PROT_WRITE, MAP_SHARED, prime_fd, 0); + if (surface->map == MAP_FAILED) + { + printf("mmap failed.\n"); + return NULL; + } + + surface->is_mapped = true; + return surface->map; +} + +void go2_surface_unmap(go2_surface_t* surface) +{ + if (surface->is_mapped) + { + munmap(surface->map, surface->size); + + surface->is_mapped = false; + surface->map = NULL; + } +} + + +static uint32_t go2_rkformat_get(uint32_t drm_fourcc) +{ + switch (drm_fourcc) + { + case DRM_FORMAT_RGBA8888: + return RK_FORMAT_RGBA_8888; + + case DRM_FORMAT_RGBX8888: + return RK_FORMAT_RGBX_8888; + + case DRM_FORMAT_RGB888: + return RK_FORMAT_RGB_888; + + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + return RK_FORMAT_BGRA_8888; + + case DRM_FORMAT_RGB565: + return RK_FORMAT_RGB_565; + + case DRM_FORMAT_RGBA5551: + return RK_FORMAT_RGBA_5551; + + case DRM_FORMAT_RGBA4444: + return RK_FORMAT_RGBA_4444; + + case DRM_FORMAT_BGR888: + return RK_FORMAT_BGR_888; + + default: + printf("RKFORMAT not supported. "); + printf("drm_fourcc=%c%c%c%c\n", drm_fourcc & 0xff, drm_fourcc >> 8 & 0xff, drm_fourcc >> 16 & 0xff, drm_fourcc >> 24); + return 0; + } +} + +void go2_surface_blit(go2_surface_t* srcSurface, int srcX, int srcY, int srcWidth, int srcHeight, + go2_surface_t* dstSurface, int dstX, int dstY, int dstWidth, int dstHeight, + go2_rotation_t rotation) +{ + rga_info_t dst = { 0 }; + dst.fd = go2_surface_prime_fd(dstSurface); + dst.mmuFlag = 1; + dst.rect.xoffset = dstX; + dst.rect.yoffset = dstY; + dst.rect.width = dstWidth; + dst.rect.height = dstHeight; + dst.rect.wstride = dstSurface->stride / (go2_drm_format_get_bpp(dstSurface->format) / 8); + dst.rect.hstride = dstSurface->height; + dst.rect.format = go2_rkformat_get(dstSurface->format); + + rga_info_t src = { 0 }; + src.fd = go2_surface_prime_fd(srcSurface); + src.mmuFlag = 1; + + switch (rotation) + { + case GO2_ROTATION_DEGREES_0: + src.rotation = 0; + break; + + case GO2_ROTATION_DEGREES_90: + src.rotation = HAL_TRANSFORM_ROT_90; + break; + + case GO2_ROTATION_DEGREES_180: + src.rotation = HAL_TRANSFORM_ROT_180; + break; + + case GO2_ROTATION_DEGREES_270: + src.rotation = HAL_TRANSFORM_ROT_270; + break; + + default: + printf("rotation not supported.\n"); + return; + } + + src.rect.xoffset = srcX; + src.rect.yoffset = srcY; + src.rect.width = srcWidth; + src.rect.height = srcHeight; + src.rect.wstride = srcSurface->stride / (go2_drm_format_get_bpp(srcSurface->format) / 8); + src.rect.hstride = srcSurface->height; + src.rect.format = go2_rkformat_get(srcSurface->format); + +#if 0 + enum + { + CATROM = 0x0, + MITCHELL = 0x1, + HERMITE = 0x2, + B_SPLINE = 0x3, + }; /*bicubic coefficient*/ +#endif + src.scale_mode = 2; + + + int ret = c_RkRgaBlit(&src, &dst, NULL); + if (ret) + { + printf("c_RkRgaBlit failed.\n"); + } +} + +int go2_surface_save_as_png(go2_surface_t* surface, const char* filename) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_bytep* row_pointers = NULL; + + + png_byte color_type = 0; + png_byte bit_depth = 0; + switch (surface->format) + { + case DRM_FORMAT_RGBA8888: + color_type = PNG_COLOR_TYPE_RGBA; + bit_depth = 8; + break; + + case DRM_FORMAT_RGB888: + color_type = PNG_COLOR_TYPE_RGB; + bit_depth = 8; + break; + + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_BGR888: + + default: + printf("The image format is not supported.\n"); + return -2; + } + + + // based on http://zarb.org/~gc/html/libpng.html + + /* create file */ + FILE *fp = fopen(filename, "wb"); + if (!fp) + { + printf("fopen failed. filename='%s'\n", filename); + return -1; + } + + + /* initialize stuff */ + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!png_ptr) + { + printf("png_create_write_struct failed.\n"); + fclose(fp); + return -1; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + printf("png_create_info_struct failed.\n"); + fclose(fp); + return -1; + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + printf("init_io failed.\n"); + goto out; + } + + png_init_io(png_ptr, fp); + + + /* write header */ + if (setjmp(png_jmpbuf(png_ptr))) + { + printf("write header failed.\n"); + goto out; + } + + png_set_IHDR(png_ptr, info_ptr, surface->width, surface->height, + bit_depth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + + /* write bytes */ + png_bytep src = (png_bytep)go2_surface_map(surface); + row_pointers = malloc(sizeof(png_bytep) * surface->height); + for (int y = 0; y < surface->height; ++y) + { + row_pointers[y] = src + (surface->stride * y); + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + printf("writing bytes failed.\n"); + goto out; + } + + png_write_image(png_ptr, row_pointers); + + + /* end write */ + if (setjmp(png_jmpbuf(png_ptr))) + { + printf("end of write failed.\n"); + goto out; + } + + png_write_end(png_ptr, NULL); + + /* cleanup heap allocation */ + free(row_pointers); + + fclose(fp); + return 0; + +out: + if (info_ptr) + png_destroy_info_struct(png_ptr, &info_ptr); + + if (png_ptr) + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + + if (row_pointers) + free(row_pointers); + + if (fp) + fclose(fp); + + return -1; +} + + +go2_frame_buffer_t* go2_frame_buffer_create(go2_surface_t* surface) +{ + go2_frame_buffer_t* result = malloc(sizeof(*result)); + if (!result) + { + printf("malloc failed.\n"); + return NULL; + } + + memset(result, 0, sizeof(*result)); + + + result->surface = surface; + + const uint32_t handles[4] = {surface->gem_handle, 0, 0, 0}; + const uint32_t pitches[4] = {surface->stride, 0, 0, 0}; + const uint32_t offsets[4] = {0, 0, 0, 0}; + + int ret = drmModeAddFB2(surface->display->fd, + surface->width, + surface->height, + surface->format, + handles, + pitches, + offsets, + &result->fb_id, + 0); + if (ret) + { + printf("drmModeAddFB2 failed.\n"); + free(result); + return NULL; + } + + return result; +} + +void go2_frame_buffer_destroy(go2_frame_buffer_t* frame_buffer) +{ + int ret = drmModeRmFB(frame_buffer->surface->display->fd, frame_buffer->fb_id); + if (ret) + { + printf("drmModeRmFB failed.\n"); + } + + free(frame_buffer); +} + +go2_surface_t* go2_frame_buffer_surface_get(go2_frame_buffer_t* frame_buffer) +{ + return frame_buffer->surface; +} + + + + + +#if 0 +typedef struct go2_presenter +{ + go2_display_t* display; + uint32_t format; + uint32_t background_color; + go2_queue_t* freeFrameBuffers; + go2_queue_t* usedFrameBuffers; + pthread_mutex_t queueMutex; + pthread_t renderThread; + sem_t freeSem; + sem_t usedSem; + volatile bool terminating; +} go2_presenter_t; +#endif + +#define BUFFER_COUNT (3) + +static void* go2_presenter_renderloop(void* arg) +{ + go2_presenter_t* presenter = (go2_presenter_t*)arg; + go2_frame_buffer_t* prevFrameBuffer = NULL; + + presenter->terminating = false; + while(!presenter->terminating) + { + sem_wait(&presenter->usedSem); + if(presenter->terminating) break; + + + pthread_mutex_lock(&presenter->queueMutex); + + if (go2_queue_count_get(presenter->usedFrameBuffers) < 1) + { + printf("no framebuffer available.\n"); + abort(); + } + + go2_frame_buffer_t* dstFrameBuffer = (go2_frame_buffer_t*)go2_queue_pop(presenter->usedFrameBuffers); + + pthread_mutex_unlock(&presenter->queueMutex); + + + go2_display_present(presenter->display, dstFrameBuffer); + + if (prevFrameBuffer) + { + pthread_mutex_lock(&presenter->queueMutex); + go2_queue_push(presenter->freeFrameBuffers, prevFrameBuffer); + pthread_mutex_unlock(&presenter->queueMutex); + + sem_post(&presenter->freeSem); + } + + prevFrameBuffer = dstFrameBuffer; + } + + + return NULL; +} + +go2_presenter_t* go2_presenter_create(go2_display_t* display, uint32_t format, uint32_t background_color) +{ + go2_presenter_t* result = malloc(sizeof(*result)); + if (!result) + { + printf("malloc failed.\n"); + return NULL; + } + + memset(result, 0, sizeof(*result)); + + + result->display = display; + result->format = format; + result->background_color = background_color; + result->freeFrameBuffers = go2_queue_create(BUFFER_COUNT); + result->usedFrameBuffers = go2_queue_create(BUFFER_COUNT); + + int width = go2_display_width_get(display); + int height = go2_display_height_get(display); + + for (int i = 0; i < BUFFER_COUNT; ++i) + { + go2_surface_t* surface = go2_surface_create(display, width, height, format); + go2_frame_buffer_t* frameBuffer = go2_frame_buffer_create(surface); + + go2_queue_push(result->freeFrameBuffers, frameBuffer); + } + + + sem_init(&result->usedSem, 0, 0); + sem_init(&result->freeSem, 0, BUFFER_COUNT); + + pthread_mutex_init(&result->queueMutex, NULL); + + pthread_create(&result->renderThread, NULL, go2_presenter_renderloop, result); + + return result; +} + +void go2_presenter_destroy(go2_presenter_t* presenter) +{ + presenter->terminating = true; + sem_post(&presenter->usedSem); + + pthread_join(presenter->renderThread, NULL); + pthread_mutex_destroy(&presenter->queueMutex); + + sem_destroy(&presenter->freeSem); + sem_destroy(&presenter->usedSem); + + + while(go2_queue_count_get(presenter->usedFrameBuffers) > 0) + { + go2_frame_buffer_t* frameBuffer = go2_queue_pop(presenter->usedFrameBuffers); + + go2_surface_t* surface = frameBuffer->surface; + + go2_frame_buffer_destroy(frameBuffer); + go2_surface_destroy(surface); + } + + while(go2_queue_count_get(presenter->freeFrameBuffers) > 0) + { + go2_frame_buffer_t* frameBuffer = go2_queue_pop(presenter->freeFrameBuffers); + + go2_surface_t* surface = frameBuffer->surface; + + go2_frame_buffer_destroy(frameBuffer); + go2_surface_destroy(surface); + } + + free(presenter); +} + +void go2_presenter_post(go2_presenter_t* presenter, go2_surface_t* surface, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, go2_rotation_t rotation) +{ + sem_wait(&presenter->freeSem); + + + pthread_mutex_lock(&presenter->queueMutex); + + if (go2_queue_count_get(presenter->freeFrameBuffers) < 1) + { + printf("no framebuffer available.\n"); + abort(); + } + + go2_frame_buffer_t* dstFrameBuffer = go2_queue_pop(presenter->freeFrameBuffers); + + pthread_mutex_unlock(&presenter->queueMutex); + + + go2_surface_t* dstSurface = go2_frame_buffer_surface_get(dstFrameBuffer); + + rga_info_t dst = { 0 }; + dst.fd = go2_surface_prime_fd(dstSurface); + dst.mmuFlag = 1; + dst.rect.xoffset = 0; + dst.rect.yoffset = 0; + dst.rect.width = go2_surface_width_get(dstSurface); + dst.rect.height = go2_surface_height_get(dstSurface); + dst.rect.wstride = go2_surface_stride_get(dstSurface) / (go2_drm_format_get_bpp(go2_surface_format_get(dstSurface)) / 8); + dst.rect.hstride = go2_surface_height_get(dstSurface); + dst.rect.format = go2_rkformat_get(go2_surface_format_get(dstSurface)); + dst.color = presenter->background_color; + + int ret = c_RkRgaColorFill(&dst); + if (ret) + { + printf("c_RkRgaColorFill failed.\n"); + } + + + go2_surface_blit(surface, srcX, srcY, srcWidth, srcHeight, dstSurface, dstX, dstY, dstWidth, dstHeight, rotation); + + + pthread_mutex_lock(&presenter->queueMutex); + go2_queue_push(presenter->usedFrameBuffers, dstFrameBuffer); + pthread_mutex_unlock(&presenter->queueMutex); + + sem_post(&presenter->usedSem); +} + + + +#define BUFFER_MAX (3) + +typedef struct buffer_surface_pair +{ + struct gbm_bo* gbmBuffer; + go2_surface_t* surface; +} buffer_surface_pair_t; + +typedef struct go2_context +{ + go2_display_t* display; + int width; + int height; + go2_context_attributes_t attributes; + struct gbm_device* gbmDevice; + EGLDisplay eglDisplay; + struct gbm_surface* gbmSurface; + EGLSurface eglSurface; + EGLContext eglContext; + uint32_t drmFourCC; + buffer_surface_pair_t bufferMap[BUFFER_MAX]; + int bufferCount; +} go2_context_t; + + + +static EGLConfig FindConfig(EGLDisplay eglDisplay, int redBits, int greenBits, int blueBits, int alphaBits, int depthBits, int stencilBits) +{ + EGLint configAttributes[] = + { + EGL_RED_SIZE, redBits, + EGL_GREEN_SIZE, greenBits, + EGL_BLUE_SIZE, blueBits, + EGL_ALPHA_SIZE, alphaBits, + + EGL_DEPTH_SIZE, depthBits, + EGL_STENCIL_SIZE, stencilBits, + + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + + EGL_NONE + }; + + + int num_configs; + EGLBoolean success = eglChooseConfig(eglDisplay, configAttributes, NULL, 0, &num_configs); + if (success != EGL_TRUE) + { + printf("eglChooseConfig failed.\n"); + abort(); + } + + + //EGLConfig* configs = new EGLConfig[num_configs]; + EGLConfig configs[num_configs]; + success = eglChooseConfig(eglDisplay, configAttributes, configs, num_configs, &num_configs); + if (success != EGL_TRUE) + { + printf("eglChooseConfig failed.\n"); + abort(); + } + + + EGLConfig match = 0; + for (int i = 0; i < num_configs; ++i) + { + EGLint configRedSize; + EGLint configGreenSize; + EGLint configBlueSize; + EGLint configAlphaSize; + EGLint configDepthSize; + EGLint configStencilSize; + + eglGetConfigAttrib(eglDisplay, configs[i], EGL_RED_SIZE, &configRedSize); + eglGetConfigAttrib(eglDisplay, configs[i], EGL_GREEN_SIZE, &configGreenSize); + eglGetConfigAttrib(eglDisplay, configs[i], EGL_BLUE_SIZE, &configBlueSize); + eglGetConfigAttrib(eglDisplay, configs[i], EGL_ALPHA_SIZE, &configAlphaSize); + eglGetConfigAttrib(eglDisplay, configs[i], EGL_DEPTH_SIZE, &configDepthSize); + eglGetConfigAttrib(eglDisplay, configs[i], EGL_STENCIL_SIZE, &configStencilSize); + + //printf("Egl::FindConfig: index=%d, red=%d, green=%d, blue=%d, alpha=%d\n", + // i, configRedSize, configGreenSize, configBlueSize, configAlphaSize); + + if (configRedSize == redBits && + configBlueSize == blueBits && + configGreenSize == greenBits && + configAlphaSize == alphaBits && + configDepthSize == depthBits && + configStencilSize == stencilBits) + { + match = configs[i]; + break; + } + } + + return match; +} + +go2_context_t* go2_context_create(go2_display_t* display, int width, int height, const go2_context_attributes_t* attributes) +{ + EGLBoolean success; + + + go2_context_t* result = malloc(sizeof(*result)); + if (!result) + { + printf("malloc failed.\n"); + return NULL; + } + + memset(result, 0, sizeof(*result)); + + + result->display = display; + result->width = width; + result->height = height; + result->attributes = *attributes; + + + result->gbmDevice = gbm_create_device(display->fd); + if (!result->gbmDevice) + { + printf("gbm_create_device failed.\n"); + goto err_00; + } + + + PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL; + get_platform_display = (PFNEGLGETPLATFORMDISPLAYEXTPROC) eglGetProcAddress("eglGetPlatformDisplayEXT"); + if(get_platform_display == NULL) + { + printf("eglGetProcAddress failed.\n"); + goto err_01; + } + + result->eglDisplay = get_platform_display(EGL_PLATFORM_GBM_KHR, result->gbmDevice, NULL); + if (result->eglDisplay == EGL_NO_DISPLAY) + { + printf("eglGetPlatformDisplayEXT failed.\n"); + goto err_01; + } + + + // Initialize EGL + EGLint major; + EGLint minor; + success = eglInitialize(result->eglDisplay, &major, &minor); + if (success != EGL_TRUE) + { + printf("eglInitialize failed.\n"); + goto err_01; + } + + printf("EGL: major=%d, minor=%d\n", major, minor); + printf("EGL: Vendor=%s\n", eglQueryString(result->eglDisplay, EGL_VENDOR)); + printf("EGL: Version=%s\n", eglQueryString(result->eglDisplay, EGL_VERSION)); + printf("EGL: ClientAPIs=%s\n", eglQueryString(result->eglDisplay, EGL_CLIENT_APIS)); + printf("EGL: Extensions=%s\n", eglQueryString(result->eglDisplay, EGL_EXTENSIONS)); + printf("EGL: ClientExtensions=%s\n", eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)); + printf("\n"); + + + EGLConfig eglConfig = FindConfig(result->eglDisplay, attributes->red_bits, attributes->green_bits, + attributes->blue_bits, attributes->alpha_bits, attributes->depth_bits, attributes->stencil_bits); + + + // Get the native visual id associated with the config + //int visual_id; + eglGetConfigAttrib(result->eglDisplay, eglConfig, EGL_NATIVE_VISUAL_ID, (EGLint*)&result->drmFourCC); + + result->gbmSurface = gbm_surface_create(result->gbmDevice, + width, + height, + result->drmFourCC, + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if (!result->gbmSurface) + { + printf("gbm_surface_create failed.\n"); + abort(); + } + + + result->eglSurface = eglCreateWindowSurface(result->eglDisplay, eglConfig, (EGLNativeWindowType)result->gbmSurface, NULL); + if (result->eglSurface == EGL_NO_SURFACE) + { + printf("eglCreateWindowSurface failed\n"); + abort(); + } + + + // Create a context + eglBindAPI(EGL_OPENGL_ES_API); + + EGLint contextAttributes[] = { + EGL_CONTEXT_CLIENT_VERSION, attributes->major, + EGL_NONE }; + + result->eglContext = eglCreateContext(result->eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttributes); + if (result->eglContext == EGL_NO_CONTEXT) + { + printf("eglCreateContext failed\n"); + abort(); + } + + success = eglMakeCurrent(result->eglDisplay, result->eglSurface, result->eglSurface, result->eglContext); + if (success != EGL_TRUE) + { + printf("eglMakeCurrent failed\n"); + abort(); + } + + + return result; + + +err_01: + gbm_device_destroy(result->gbmDevice); + +err_00: + free(result); + return NULL; +} + +void go2_context_destroy(go2_context_t* context) +{ + eglDestroyContext(context->eglDisplay, context->eglContext); + eglDestroySurface(context->eglDisplay, context->eglSurface); + gbm_surface_destroy(context->gbmSurface); + eglTerminate(context->eglDisplay); + gbm_device_destroy(context->gbmDevice); + + for(int i = 0; i < context->bufferCount; ++i) + { + free(context->bufferMap[i].surface); + } + + free(context); +} + +void* go2_context_egldisplay_get(go2_context_t* context) +{ + return context->eglDisplay; +} + +void go2_context_make_current(go2_context_t* context) +{ + EGLBoolean success = eglMakeCurrent(context->eglDisplay, context->eglSurface, context->eglSurface, context->eglContext); + if (success != EGL_TRUE) + { + printf("eglMakeCurrent failed\n"); + abort(); + } +} + +void go2_context_swap_buffers(go2_context_t* context) +{ + EGLBoolean ret = eglSwapBuffers(context->eglDisplay, context->eglSurface); + if (ret == EGL_FALSE) + { + printf("eglSwapBuffers failed\n"); + //abort(); + } +} + +go2_surface_t* go2_context_surface_lock(go2_context_t* context) +{ + struct gbm_bo *bo = gbm_surface_lock_front_buffer(context->gbmSurface); + if (!bo) + { + printf("gbm_surface_lock_front_buffer failed.\n"); + abort(); + } + + go2_surface_t* surface = NULL; + for (int i = 0; i < context->bufferCount; ++i) + { + buffer_surface_pair_t* pair = &context->bufferMap[i]; + if (pair->gbmBuffer == bo) + { + surface = pair->surface; + break; + } + } + + if (!surface) + { + if (context->bufferCount >= BUFFER_MAX) + { + printf("swap buffers count exceeded.\n"); + abort(); + } + + surface = malloc(sizeof(*surface)); + if (!surface) + { + printf("malloc failed.\n"); + abort(); + } + + memset(surface, 0, sizeof(*surface)); + + + surface->display = context->display; + surface->gem_handle = gbm_bo_get_handle(bo).u32; + surface->size = gbm_bo_get_stride(bo); + surface->width = gbm_bo_get_width(bo); + surface->height = gbm_bo_get_height(bo); + surface->stride = gbm_bo_get_stride(bo); + surface->format = context->drmFourCC; + + + buffer_surface_pair_t* pair = &context->bufferMap[context->bufferCount++]; + pair->gbmBuffer = bo; + pair->surface = surface; + + //printf("added buffer - bo=%p, count=%d\n", bo, context->bufferCount); + } + + return surface; +} + +void go2_context_surface_unlock(go2_context_t* context, go2_surface_t* surface) +{ + struct gbm_bo* bo = NULL; + for (int i = 0; i < context->bufferCount; ++i) + { + buffer_surface_pair_t* pair = &context->bufferMap[i]; + if (pair->surface == surface) + { + bo = pair->gbmBuffer; + break; + } + } + + if (!bo) + { + abort(); + } + + gbm_surface_release_buffer(context->gbmSurface, bo); +} diff --git a/deps/libgo2/src/display.h b/deps/libgo2/src/display.h new file mode 100644 index 0000000000..cbe30a9c2a --- /dev/null +++ b/deps/libgo2/src/display.h @@ -0,0 +1,105 @@ +#pragma once + +/* +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + + +typedef struct go2_display go2_display_t; +typedef struct go2_surface go2_surface_t; +typedef struct go2_frame_buffer go2_frame_buffer_t; +typedef struct go2_presenter go2_presenter_t; + +typedef enum go2_rotation +{ + GO2_ROTATION_DEGREES_0 = 0, + GO2_ROTATION_DEGREES_90, + GO2_ROTATION_DEGREES_180, + GO2_ROTATION_DEGREES_270 +} go2_rotation_t; + +typedef struct go2_context_attributes +{ + int major; + int minor; + int red_bits; + int green_bits; + int blue_bits; + int alpha_bits; + int depth_bits; + int stencil_bits; +} go2_context_attributes_t; + +typedef struct go2_context go2_context_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +go2_display_t* go2_display_create(); +void go2_display_destroy(go2_display_t* display); +int go2_display_width_get(go2_display_t* display); +int go2_display_height_get(go2_display_t* display); +void go2_display_present(go2_display_t* display, go2_frame_buffer_t* frame_buffer); +uint32_t go2_display_backlight_get(go2_display_t* display); +void go2_display_backlight_set(go2_display_t* display, uint32_t value); + + +int go2_drm_format_get_bpp(uint32_t format); + + +go2_surface_t* go2_surface_create(go2_display_t* display, int width, int height, uint32_t format); +void go2_surface_destroy(go2_surface_t* surface); +int go2_surface_width_get(go2_surface_t* surface); +int go2_surface_height_get(go2_surface_t* surface); +uint32_t go2_surface_format_get(go2_surface_t* surface); +int go2_surface_stride_get(go2_surface_t* surface); +go2_display_t* go2_surface_display_get(go2_surface_t* surface); +int go2_surface_prime_fd(go2_surface_t* surface); +void* go2_surface_map(go2_surface_t* surface); +void go2_surface_unmap(go2_surface_t* surface); +void go2_surface_blit(go2_surface_t* srcSurface, int srcX, int srcY, int srcWidth, int srcHeight, + go2_surface_t* dstSurface, int dstX, int dstY, int dstWidth, int dstHeight, + go2_rotation_t rotation); +int go2_surface_save_as_png(go2_surface_t* surface, const char* filename); + + +go2_frame_buffer_t* go2_frame_buffer_create(go2_surface_t* surface); +void go2_frame_buffer_destroy(go2_frame_buffer_t* frame_buffer); +go2_surface_t* go2_frame_buffer_surface_get(go2_frame_buffer_t* frame_buffer); + + +go2_presenter_t* go2_presenter_create(go2_display_t* display, uint32_t format, uint32_t background_color); +void go2_presenter_destroy(go2_presenter_t* presenter); +void go2_presenter_post(go2_presenter_t* presenter, go2_surface_t* surface, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, go2_rotation_t rotation); + + +go2_context_t* go2_context_create(go2_display_t* display, int width, int height, const go2_context_attributes_t* attributes); +void go2_context_destroy(go2_context_t* context); +void* go2_context_egldisplay_get(go2_context_t* context); +void go2_context_make_current(go2_context_t* context); +void go2_context_swap_buffers(go2_context_t* context); +go2_surface_t* go2_context_surface_lock(go2_context_t* context); +void go2_context_surface_unlock(go2_context_t* context, go2_surface_t* surface); + +#ifdef __cplusplus +} +#endif diff --git a/deps/libgo2/src/input.c b/deps/libgo2/src/input.c new file mode 100644 index 0000000000..948420759c --- /dev/null +++ b/deps/libgo2/src/input.c @@ -0,0 +1,371 @@ +/* +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "input.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define BATTERY_BUFFER_SIZE (128) + +static const char* EVDEV_NAME = "/dev/input/by-path/platform-odroidgo2-joypad-event-joystick"; +static const char* BATTERY_STATUS_NAME = "/sys/class/power_supply/battery/status"; +static const char* BATTERY_CAPACITY_NAME = "/sys/class/power_supply/battery/capacity"; + + +typedef struct go2_input +{ + int fd; + struct libevdev* dev; + go2_gamepad_state_t current_state; + go2_gamepad_state_t pending_state; + pthread_mutex_t gamepadMutex; + pthread_t thread_id; + go2_battery_state_t current_battery; + pthread_t battery_thread; + bool terminating; +} go2_input_t; + + +static void* battery_task(void* arg) +{ + go2_input_t* input = (go2_input_t*)arg; + int fd; + void* result = 0; + char buffer[BATTERY_BUFFER_SIZE + 1]; + go2_battery_state_t battery; + + + memset(&battery, 0, sizeof(battery)); + + + while(!input->terminating) + { + fd = open(BATTERY_STATUS_NAME, O_RDONLY); + if (fd > 0) + { + memset(buffer, 0, BATTERY_BUFFER_SIZE + 1); + ssize_t count = read(fd, buffer, BATTERY_BUFFER_SIZE); + if (count > 0) + { + //printf("BATT: buffer='%s'\n", buffer); + + if (buffer[0] == 'D') + { + battery.status = Battery_Status_Discharging; + } + else if (buffer[0] == 'C') + { + battery.status = Battery_Status_Charging; + } + else if (buffer[0] == 'F') + { + battery.status = Battery_Status_Full; + } + else + { + battery.status = Battery_Status_Unknown; + } + } + + close(fd); + } + + fd = open(BATTERY_CAPACITY_NAME, O_RDONLY); + if (fd > 0) + { + memset(buffer, 0, BATTERY_BUFFER_SIZE + 1); + ssize_t count = read(fd, buffer, BATTERY_BUFFER_SIZE); + if (count > 0) + { + battery.level = atoi(buffer); + } + else + { + battery.level = 0; + } + + close(fd); + } + + + pthread_mutex_lock(&input->gamepadMutex); + + input->current_battery = battery; + + pthread_mutex_unlock(&input->gamepadMutex); + + //printf("BATT: status=%d, level=%d\n", input->current_battery.status, input->current_battery.level); + + sleep(1); + } + + //printf("BATT: exit.\n"); + return result; +} + + + + +static void* input_task(void* arg) +{ + go2_input_t* input = (go2_input_t*)arg; + + if (!input->dev) return NULL; + + const int abs_x_max = 512; //libevdev_get_abs_maximum(input->dev, ABS_X); + const int abs_y_max = 512; //libevdev_get_abs_maximum(input->dev, ABS_Y); + + //printf("abs: x_max=%d, y_max=%d\n", abs_x_max, abs_y_max); + + + // Get current state + input->pending_state.dpad.up = libevdev_get_event_value(input->dev, EV_KEY, BTN_DPAD_UP) ? ButtonState_Pressed : ButtonState_Released; + input->pending_state.dpad.down = libevdev_get_event_value(input->dev, EV_KEY, BTN_DPAD_DOWN) ? ButtonState_Pressed : ButtonState_Released; + input->pending_state.dpad.left = libevdev_get_event_value(input->dev, EV_KEY, BTN_DPAD_LEFT) ? ButtonState_Pressed : ButtonState_Released; + input->pending_state.dpad.right = libevdev_get_event_value(input->dev, EV_KEY, BTN_DPAD_RIGHT) ? ButtonState_Pressed : ButtonState_Released; + + input->pending_state.buttons.a = libevdev_get_event_value(input->dev, EV_KEY, BTN_EAST) ? ButtonState_Pressed : ButtonState_Released; + input->pending_state.buttons.b = libevdev_get_event_value(input->dev, EV_KEY, BTN_SOUTH) ? ButtonState_Pressed : ButtonState_Released; + input->pending_state.buttons.x = libevdev_get_event_value(input->dev, EV_KEY, BTN_NORTH) ? ButtonState_Pressed : ButtonState_Released; + input->pending_state.buttons.y = libevdev_get_event_value(input->dev, EV_KEY, BTN_WEST) ? ButtonState_Pressed : ButtonState_Released; + + input->pending_state.buttons.top_left = libevdev_get_event_value(input->dev, EV_KEY, BTN_TL) ? ButtonState_Pressed : ButtonState_Released; + input->pending_state.buttons.top_right = libevdev_get_event_value(input->dev, EV_KEY, BTN_TR) ? ButtonState_Pressed : ButtonState_Released; + + input->current_state.buttons.f1 = libevdev_get_event_value(input->dev, EV_KEY, BTN_TRIGGER_HAPPY1) ? ButtonState_Pressed : ButtonState_Released; + input->current_state.buttons.f2 = libevdev_get_event_value(input->dev, EV_KEY, BTN_TRIGGER_HAPPY2) ? ButtonState_Pressed : ButtonState_Released; + input->current_state.buttons.f3 = libevdev_get_event_value(input->dev, EV_KEY, BTN_TRIGGER_HAPPY3) ? ButtonState_Pressed : ButtonState_Released; + input->current_state.buttons.f4 = libevdev_get_event_value(input->dev, EV_KEY, BTN_TRIGGER_HAPPY4) ? ButtonState_Pressed : ButtonState_Released; + input->current_state.buttons.f5 = libevdev_get_event_value(input->dev, EV_KEY, BTN_TRIGGER_HAPPY5) ? ButtonState_Pressed : ButtonState_Released; + input->current_state.buttons.f5 = libevdev_get_event_value(input->dev, EV_KEY, BTN_TRIGGER_HAPPY6) ? ButtonState_Pressed : ButtonState_Released; + + + // Events + while (!input->terminating) + { + /* EAGAIN is returned when the queue is empty */ + struct input_event ev; + int rc = libevdev_next_event(input->dev, LIBEVDEV_READ_FLAG_BLOCKING, &ev); + if (rc == 0) + { +#if 0 + printf("Gamepad Event: %s-%s(%d)=%d\n", + libevdev_event_type_get_name(ev.type), + libevdev_event_code_get_name(ev.type, ev.code), ev.code, + ev.value); +#endif + + if (ev.type == EV_KEY) + { + go2_button_state_t state = ev.value ? ButtonState_Pressed : ButtonState_Released; + + switch (ev.code) + { + case BTN_DPAD_UP: + input->pending_state.dpad.up = state; + break; + case BTN_DPAD_DOWN: + input->pending_state.dpad.down = state; + break; + case BTN_DPAD_LEFT: + input->pending_state.dpad.left = state; + break; + case BTN_DPAD_RIGHT: + input->pending_state.dpad.right = state; + break; + + case BTN_EAST: + input->pending_state.buttons.a = state; + break; + case BTN_SOUTH: + input->pending_state.buttons.b = state; + break; + case BTN_NORTH: + input->pending_state.buttons.x = state; + break; + case BTN_WEST: + input->pending_state.buttons.y = state; + break; + + case BTN_TL: + input->pending_state.buttons.top_left = state; + break; + case BTN_TR: + input->pending_state.buttons.top_right = state; + break; + + case BTN_TRIGGER_HAPPY1: + input->pending_state.buttons.f1 = state; + break; + case BTN_TRIGGER_HAPPY2: + input->pending_state.buttons.f2 = state; + break; + case BTN_TRIGGER_HAPPY3: + input->pending_state.buttons.f3 = state; + break; + case BTN_TRIGGER_HAPPY4: + input->pending_state.buttons.f4 = state; + break; + case BTN_TRIGGER_HAPPY5: + input->pending_state.buttons.f5 = state; + break; + case BTN_TRIGGER_HAPPY6: + input->pending_state.buttons.f6 = state; + break; + } + } + else if (ev.type == EV_ABS) + { + switch (ev.code) + { + case ABS_X: + input->pending_state.thumb.x = ev.value / (float)abs_x_max; + break; + case ABS_Y: + input->pending_state.thumb.y = ev.value / (float)abs_y_max; + break; + } + } + else if (ev.type == EV_SYN) + { + pthread_mutex_lock(&input->gamepadMutex); + + input->current_state = input->pending_state; + + pthread_mutex_unlock(&input->gamepadMutex); + } + } + } + + return NULL; +} + +go2_input_t* go2_input_create() +{ + int rc = 1; + + go2_input_t* result = malloc(sizeof(*result)); + if (!result) + { + printf("malloc failed.\n"); + goto out; + } + + memset(result, 0, sizeof(*result)); + + + + + + result->fd = open(EVDEV_NAME, O_RDONLY); + if (result->fd < 0) + { + printf("Joystick: No gamepad found.\n"); + } + else + { + rc = libevdev_new_from_fd(result->fd, &result->dev); + if (rc < 0) { + printf("Joystick: Failed to init libevdev (%s)\n", strerror(-rc)); + goto err_00; + } + + memset(&result->current_state, 0, sizeof(result->current_state)); + memset(&result->pending_state, 0, sizeof(result->pending_state)); + + + // printf("Input device name: \"%s\"\n", libevdev_get_name(result->dev)); + // printf("Input device ID: bus %#x vendor %#x product %#x\n", + // libevdev_get_id_bustype(result->dev), + // libevdev_get_id_vendor(result->dev), + // libevdev_get_id_product(result->dev)); + + if(pthread_create(&result->thread_id, NULL, input_task, (void*)result) < 0) + { + printf("could not create input_task thread\n"); + goto err_01; + } + + if(pthread_create(&result->battery_thread, NULL, battery_task, (void*)result) < 0) + { + printf("could not create battery_task thread\n"); + } + + } + + return result; + + +err_01: + libevdev_free(result->dev); + +err_00: + close(result->fd); + free(result); + +out: + return NULL; +} + +void go2_input_destroy(go2_input_t* input) +{ + input->terminating = true; + + pthread_cancel(input->thread_id); + + pthread_join(input->thread_id, NULL); + pthread_join(input->battery_thread, NULL); + + libevdev_free(input->dev); + close(input->fd); + free(input); +} + +void go2_input_gamepad_read(go2_input_t* input, go2_gamepad_state_t* outGamepadState) +{ + pthread_mutex_lock(&input->gamepadMutex); + + *outGamepadState = input->current_state; + + pthread_mutex_unlock(&input->gamepadMutex); +} + +void go2_input_battery_read(go2_input_t* input, go2_battery_state_t* outBatteryState) +{ + pthread_mutex_lock(&input->gamepadMutex); + + *outBatteryState = input->current_battery; + + pthread_mutex_unlock(&input->gamepadMutex); +} diff --git a/deps/libgo2/src/input.h b/deps/libgo2/src/input.h new file mode 100644 index 0000000000..da450229a4 --- /dev/null +++ b/deps/libgo2/src/input.h @@ -0,0 +1,103 @@ +#pragma once + +/* +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + + +typedef struct +{ + float x; + float y; +} go2_thumb_t; + +typedef enum +{ + ButtonState_Released = 0, + ButtonState_Pressed +} go2_button_state_t; + + +typedef struct +{ + go2_button_state_t a; + go2_button_state_t b; + go2_button_state_t x; + go2_button_state_t y; + + go2_button_state_t top_left; + go2_button_state_t top_right; + + go2_button_state_t f1; + go2_button_state_t f2; + go2_button_state_t f3; + go2_button_state_t f4; + go2_button_state_t f5; + go2_button_state_t f6; + +} go2_gamepad_buttons_t; + +typedef struct +{ + go2_button_state_t up; + go2_button_state_t down; + go2_button_state_t left; + go2_button_state_t right; +} go2_dpad_t; + +typedef struct +{ + go2_thumb_t thumb; + go2_dpad_t dpad; + go2_gamepad_buttons_t buttons; +} go2_gamepad_state_t; + +typedef struct go2_input go2_input_t; + + +typedef enum +{ + Battery_Status_Unknown = 0, + Battery_Status_Discharging, + Battery_Status_Charging, + Battery_Status_Full, + + Battery_Status_MAX = 0x7fffffff +} go2_battery_status_t; + +typedef struct +{ + uint32_t level; + go2_battery_status_t status; +} go2_battery_state_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +go2_input_t* go2_input_create(); +void go2_input_destroy(go2_input_t* input); +void go2_input_gamepad_read(go2_input_t* input, go2_gamepad_state_t* outGamepadState); +void go2_input_battery_read(go2_input_t* input, go2_battery_state_t* outBatteryState); + +#ifdef __cplusplus +} +#endif diff --git a/deps/libgo2/src/queue.c b/deps/libgo2/src/queue.c new file mode 100644 index 0000000000..05278a4cf4 --- /dev/null +++ b/deps/libgo2/src/queue.c @@ -0,0 +1,106 @@ +/* +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "queue.h" + +#include +#include +#include + + +typedef struct go2_queue +{ + int capacity; + int count; + void** data; +} go2_queue_t; + + + +go2_queue_t* go2_queue_create(int capacity) +{ + go2_queue_t* result = malloc(sizeof(*result)); + if (!result) + { + printf("malloc failed.\n"); + return NULL; + } + + memset(result, 0, sizeof(*result)); + + + result->capacity = capacity; + result->data = malloc(capacity * sizeof(void*)); + if (!result) + { + printf("data malloc failed.\n"); + free(result); + return NULL; + } + + return result; +} + +int go2_queue_count_get(go2_queue_t* queue) +{ + return queue->count; +} + +void go2_queue_push(go2_queue_t* queue, void* value) +{ + if (queue->count < queue->capacity) + { + queue->data[queue->count] = value; + queue->count++; + } + else + { + printf("queue is full.\n"); + } +} + +void* go2_queue_pop(go2_queue_t* queue) +{ + void* result; + + if (queue->count > 0) + { + result = queue->data[0]; + + for (int i = 0; i < queue->count - 1; ++i) + { + queue->data[i] = queue->data[i + 1]; + } + + queue->count--; + } + else + { + printf("queue is empty.\n"); + result = NULL; + } + + return result; +} + +void go2_queue_destroy(go2_queue_t* queue) +{ + free(queue->data); + free(queue); +} diff --git a/deps/libgo2/src/queue.h b/deps/libgo2/src/queue.h new file mode 100644 index 0000000000..3c6918efcc --- /dev/null +++ b/deps/libgo2/src/queue.h @@ -0,0 +1,37 @@ +#pragma once + +/* +libgo2 - Support library for the ODROID-GO Advance +Copyright (C) 2020 OtherCrashOverride + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +typedef struct go2_queue go2_queue_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +go2_queue_t* go2_queue_create(int capacity); +int go2_queue_count_get(go2_queue_t* queue); +void go2_queue_push(go2_queue_t* queue, void* value); +void* go2_queue_pop(go2_queue_t* queue); +void go2_queue_destroy(go2_queue_t* queue); + +#ifdef __cplusplus +} +#endif diff --git a/gfx/drivers_context/drm_ctx.c b/gfx/drivers_context/drm_ctx.c index 02753d95fe..2f872b4a2c 100644 --- a/gfx/drivers_context/drm_ctx.c +++ b/gfx/drivers_context/drm_ctx.c @@ -43,7 +43,7 @@ #include "../common/drm_common.h" #ifdef HAVE_ODROIDGO2 -#include +#include #include #endif @@ -73,19 +73,27 @@ static enum gfx_ctx_api drm_api = GFX_CTX_NONE; +#ifndef HAVE_ODROIDGO2 static struct gbm_bo *g_bo = NULL; static struct gbm_bo *g_next_bo = NULL; static struct gbm_surface *g_gbm_surface = NULL; static struct gbm_device *g_gbm_dev = NULL; static bool waiting_for_flip = false; +#endif typedef struct gfx_ctx_drm_data { #ifdef HAVE_EGL egl_ctx_data_t egl; #endif +#ifndef HAVE_ODROIDGO2 int fd; +#else + go2_display_t* display; + go2_presenter_t* presenter; + go2_context_t* context; +#endif int interval; unsigned fb_width; unsigned fb_height; @@ -93,6 +101,7 @@ typedef struct gfx_ctx_drm_data bool core_hw_context_enable; } gfx_ctx_drm_data_t; +#ifndef HAVE_ODROIDGO2 struct drm_fb { struct gbm_bo *bo; @@ -139,26 +148,6 @@ error: return NULL; } -static void gfx_ctx_drm_swap_interval(void *data, int interval) -{ - gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; - drm->interval = interval; - - if (interval > 1) - RARCH_WARN("[KMS]: Swap intervals > 1 currently not supported. Will use swap interval of 1.\n"); -} - -static void gfx_ctx_drm_check_window(void *data, bool *quit, - bool *resize, unsigned *width, unsigned *height, bool is_shutdown) -{ - (void)data; - (void)width; - (void)height; - - *resize = false; - *quit = (bool)frontend_driver_get_signal_handler_state(); -} - static void drm_flip_handler(int fd, unsigned frame, unsigned sec, unsigned usec, void *data) { @@ -233,7 +222,57 @@ static bool gfx_ctx_drm_queue_flip(void) /* Failed to queue page flip. */ return false; } +#endif +static void gfx_ctx_drm_swap_interval(void *data, int interval) +{ + gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; + drm->interval = interval; + + if (interval > 1) + RARCH_WARN("[KMS]: Swap intervals > 1 currently not supported. Will use swap interval of 1.\n"); +} + +static void gfx_ctx_drm_check_window(void *data, bool *quit, + bool *resize, unsigned *width, unsigned *height, bool is_shutdown) +{ + (void)data; + (void)width; + (void)height; + + *resize = false; + *quit = (bool)frontend_driver_get_signal_handler_state(); +} + +#ifdef HAVE_ODROIDGO2 +static void gfx_ctx_drm_swap_buffers(void *data, void *data2) +{ + gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; + video_frame_info_t *video_info = (video_frame_info_t*)data2; + + switch (drm_api) + { + case GFX_CTX_OPENGL_API: + case GFX_CTX_OPENGL_ES_API: + case GFX_CTX_OPENVG_API: +#ifdef HAVE_EGL + go2_context_swap_buffers(drm->context); + + go2_surface_t* surface = go2_context_surface_lock(drm->context); + go2_presenter_post(drm->presenter, + surface, + 0, 0, 480, 320, + 0, 0, 320, 480, + GO2_ROTATION_DEGREES_270); + go2_context_surface_unlock(drm->context, surface); +#endif + break; + default: + printf("unhandled gfx_ctx_drm_swap_buffers\n"); + break; + } +} +#else static void gfx_ctx_drm_swap_buffers(void *data, void *data2) { gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; @@ -270,18 +309,6 @@ static void gfx_ctx_drm_swap_buffers(void *data, void *data2) gfx_ctx_drm_wait_flip(true); } -static void gfx_ctx_drm_get_video_size(void *data, - unsigned *width, unsigned *height) -{ - gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; - - if (!drm) - return; - - *width = drm->fb_width; - *height = drm->fb_height; -} - static void free_drm_resources(gfx_ctx_drm_data_t *drm) { if (!drm) @@ -311,7 +338,21 @@ static void free_drm_resources(gfx_ctx_drm_data_t *drm) g_gbm_dev = NULL; g_drm_fd = -1; } +#endif +static void gfx_ctx_drm_get_video_size(void *data, + unsigned *width, unsigned *height) +{ + gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; + + if (!drm) + return; + + *width = drm->fb_width; + *height = drm->fb_height; +} + +#ifndef HAVE_ODROIDGO2 static void gfx_ctx_drm_destroy_resources(gfx_ctx_drm_data_t *drm) { if (!drm) @@ -346,19 +387,24 @@ static void gfx_ctx_drm_destroy_resources(gfx_ctx_drm_data_t *drm) g_bo = NULL; g_next_bo = NULL; } +#endif static void *gfx_ctx_drm_init(video_frame_info_t *video_info, void *video_driver) { +#ifndef HAVE_ODROIDGO2 int fd, i; unsigned monitor_index; unsigned gpu_index = 0; const char *gpu = NULL; struct string_list *gpu_descriptors = NULL; +#endif gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*) calloc(1, sizeof(gfx_ctx_drm_data_t)); if (!drm) return NULL; + +#ifndef HAVE_ODROIDGO2 drm->fd = -1; gpu_descriptors = dir_list_new("/dev/dri", NULL, false, true, false, false); @@ -435,8 +481,14 @@ error: free(drm); return NULL; +#else + drm->display = go2_display_create(); + drm->presenter = go2_presenter_create(drm->display, DRM_FORMAT_RGB565, 0xff080808); + return drm; +#endif } +#ifndef HAVE_ODROIDGO2 static EGLint *gfx_ctx_drm_egl_fill_attribs( gfx_ctx_drm_data_t *drm, EGLint *attr) { @@ -505,7 +557,9 @@ static EGLint *gfx_ctx_drm_egl_fill_attribs( *attr = EGL_NONE; return attr; } +#endif +#ifndef HAVE_ODROIDGO2 #ifdef HAVE_EGL static bool gbm_choose_xrgb8888_cb(void *display_data, EGLDisplay dpy, EGLConfig config) { @@ -528,6 +582,8 @@ static bool gbm_choose_xrgb8888_cb(void *display_data, EGLDisplay dpy, EGLConfig return id == GBM_FORMAT_XRGB8888; } +#endif +#endif #define DRM_EGL_ATTRIBS_BASE \ EGL_SURFACE_TYPE, EGL_WINDOW_BIT, \ @@ -537,6 +593,7 @@ static bool gbm_choose_xrgb8888_cb(void *display_data, EGLDisplay dpy, EGLConfig EGL_ALPHA_SIZE, 0, \ EGL_DEPTH_SIZE, 0 +#ifndef HAVE_ODROIDGO2 static bool gfx_ctx_drm_egl_set_video_mode(gfx_ctx_drm_data_t *drm) { const EGLint *attrib_ptr = NULL; @@ -644,9 +701,11 @@ static bool gfx_ctx_drm_set_video_mode(void *data, unsigned width, unsigned height, bool fullscreen) { +#ifndef HAVE_ODROIDGO2 float refresh_mod; int i, ret = 0; struct drm_fb *fb = NULL; +#endif gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data; if (!drm) @@ -654,6 +713,7 @@ static bool gfx_ctx_drm_set_video_mode(void *data, frontend_driver_install_signal_handler(); +#ifndef HAVE_ODROIDGO2 /* If we use black frame insertion, * we fake a 60 Hz monitor for 120 Hz one, * etc, so try to match that. */ @@ -710,7 +770,13 @@ static bool gfx_ctx_drm_set_video_mode(void *data, drm->fb_height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); +#else + /* Hardcoded odroidgo2 display resolution */ + drm->fb_width = 480; + drm->fb_height = 320; +#endif +#ifndef HAVE_ODROIDGO2 if (!g_gbm_surface) { RARCH_ERR("[KMS/EGL]: Couldn't create GBM surface.\n"); @@ -753,6 +819,25 @@ error: free(drm); return false; +#else + go2_context_attributes_t attr; + attr.major = 3; + attr.minor = 2; + attr.red_bits = 8; + attr.green_bits = 8; + attr.blue_bits = 8; + attr.alpha_bits = 8; + attr.depth_bits = 0; + attr.stencil_bits = 0; + + /* Hardcoded odroidgo2 display resolution */ + drm->context = go2_context_create(drm->display, 480, 320, &attr); + go2_context_make_current(drm->context); + + glClear(GL_COLOR_BUFFER_BIT); + + return true; +#endif } static void gfx_ctx_drm_destroy(void *data) @@ -762,8 +847,22 @@ static void gfx_ctx_drm_destroy(void *data) if (!drm) return; +#ifndef HAVE_ODROIDGO2 gfx_ctx_drm_destroy_resources(drm); free(drm); +#else + if (drm->context) + { + go2_context_destroy(drm->context); + drm->context = NULL; + } + + go2_presenter_destroy(drm->presenter); + drm->presenter = NULL; + + go2_display_destroy(drm->display); + drm->display = NULL; +#endif } static void gfx_ctx_drm_input_driver(void *data, diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 292f6c849a..a5a8d7d677 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -637,10 +637,3 @@ fi check_enabled 'ZLIB BUILTINZLIB' RPNG RPNG 'zlib is' false check_enabled V4L2 VIDEOPROCESSOR 'video processor' 'Video4linux2 is' true - -if [ "$HAVE_ODROIDGO2" != 'no' ]; then - check_pkgconf LIBRGA librk_rga - if [ "$HAVE_LIBRGA" != 'yes' ]; then - die 1 'librga is required for ODROID-GO Advance support' - fi -fi