summaryrefslogtreecommitdiff
path: root/linux-core
AgeCommit message (Expand)Author
2007-07-26drm: fix size argument for copy to/from userDave Airlie
2007-07-24Fix typo on previous commit. Sigh...Ian Romanick
2007-07-24Pass correct offset to xgi_find_pcie_virt.Ian Romanick
2007-07-24Fix license formatting.Ian Romanick
2007-07-24Fix flags for serveral ioctls.Ian Romanick
2007-07-24Fix bug preventing X server from restarting.Ian Romanick
2007-07-23Minor log message clean up.Ian Romanick
2007-07-23Minor log message clean up.Ian Romanick
2007-07-23linux: Make DRM_IOCTL_GET_CLIENT return EINVAL when it can't find client #idx.Eric Anholt
2007-07-23Eliminate XGI_CHECK_PCI_CONFIG.Ian Romanick
2007-07-23drm_rmmap_ioctl(): remove dead codeAdrian Bunk
2007-07-21Bump version.Ian Romanick
2007-07-21Remove some extraneous debug messages.Ian Romanick
2007-07-21Rename and document fields of xgi_cmdring_info.Ian Romanick
2007-07-21Make s_cmdring a field in the xgi_info structure instead of a global.Ian Romanick
2007-07-21Clean up xgi_cmd_info and associated code.Ian Romanick
2007-07-21Fix misc ioctl issues, makes Nouveau run.Pekka Paalanen
2007-07-21fix missing brace placement for IOC_INDave Airlie
2007-07-21fix drm no-compile due to BSD :-)Dave Airlie
2007-07-20Replace DRM_IOCTL_ARGS with (dev, data, file_priv) and remove DRM_DEVICE.Eric Anholt
2007-07-20Replace filp in ioctl arguments with drm_file *file_priv.Eric Anholt
2007-07-20Remove DRM_ERR OS macro.Eric Anholt
2007-07-20Fix linux spinlock macros after the last commit.Eric Anholt
2007-07-20Clean up flush command generation in addFlush2D.Ian Romanick
2007-07-20Clean up generation of begin commands in xgi_submit_cmdlistIan Romanick
2007-07-20Change handling of begin types slightly.Ian Romanick
2007-07-20Changed mode config spinlock to mutexJakob Bornecrantz
2007-07-19Delete unused variable in xgi_driver_load.Ian Romanick
2007-07-19Fix error handing related to xgi_cmdlist_initialize.Ian Romanick
2007-07-19Debug message and comment clean up in xgi_submit_cmdlist.Ian Romanick
2007-07-19Rework xgi_(pcie|fb)_free_all to prevent deadlock.Ian Romanick
2007-07-19Move MMIO drm_addmap (and code that depends on it) to xgi_bootstrap.Ian Romanick
2007-07-19Initial pass at converting driver to DRM infrastructure.Ian Romanick
2007-07-19Fix unlocking of spinlock when we should notJakob Bornecrantz
2007-07-19Merge branch 'origin'Eric Anholt
2007-07-18Add dry-coded DRM drawable private information storage for FreeBSD.Eric Anholt
2007-07-18Fix via dmablit when blit queue is full.Thomas Hellstrom
2007-07-18fix some missing whitespace/tabDave Airlie
2007-07-18drm: idr stuff is upstream for 2.6.23Dave Airlie
2007-07-18drm: remove drm_u64_t, replace with uint64_t everwhereDave Airlie
2007-07-18drm_context: fix brainoDave Airlie
2007-07-16Make drm_sg_free callable in-kernel.Ian Romanick
2007-07-16Log message clean up in WriteRegDWord. Remove unused inline functions.Ian Romanick
2007-07-16Clean ups (primarilly log messages) in xgi_test_rwinkernel.Ian Romanick
2007-07-16Eliminate several useless ioctls and associated cruft.Ian Romanick
2007-07-16Massive log message clean up in xgi_submit_cmdlist.Ian Romanick
2007-07-17nouveau: G8x PCIEGARTBen Skeggs
2007-07-16drm/radeon/ttm: more VRAM fixesDave Airlie
2007-07-16drm: fixup old kernel compat codeDave Airlie
2007-07-16drm: fixup compat wrappersDave Airlie
> 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
#include <chrono>
#include <cstdio>
#include <vector>
#include <memory>
#include <algorithm>
#include <poll.h>

#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>

#include <kms++.h>
#include <kms++util.h>
#include "cube-egl.h"
#include "cube-gles2.h"

using namespace kms;
using namespace std;

static int s_flip_pending;
static bool s_need_exit;

static bool s_support_planes;

class GbmDevice
{
public:
	GbmDevice(Card& card)
	{
		m_dev = gbm_create_device(card.fd());
		FAIL_IF(!m_dev, "failed to create gbm device");
	}

	~GbmDevice()
	{
		gbm_device_destroy(m_dev);
	}

	GbmDevice(const GbmDevice& other) = delete;
	GbmDevice& operator=(const GbmDevice& other) = delete;

	struct gbm_device* handle() const { return m_dev; }

private:
	struct gbm_device* m_dev;
};

class GbmSurface
{
public:
	GbmSurface(GbmDevice& gdev, int width, int height)
	{
		m_surface = gbm_surface_create(gdev.handle(), width, height,
					       GBM_FORMAT_XRGB8888,
					       GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
		FAIL_IF(!m_surface, "failed to create gbm surface");
	}

	~GbmSurface()
	{
		gbm_surface_destroy(m_surface);
	}

	GbmSurface(const GbmSurface& other) = delete;
	GbmSurface& operator=(const GbmSurface& other) = delete;

	bool has_free()
	{
		return gbm_surface_has_free_buffers(m_surface);
	}

	gbm_bo* lock_front_buffer()
	{
		return gbm_surface_lock_front_buffer(m_surface);
	}

	void release_buffer(gbm_bo *bo)
	{
		gbm_surface_release_buffer(m_surface, bo);
	}

	struct gbm_surface* handle() const { return m_surface; }

private:
	struct gbm_surface* m_surface;
};

class GbmEglSurface
{
public:
	GbmEglSurface(Card& card, GbmDevice& gdev, const EglState& egl, int width, int height)
		: card(card), egl(egl), m_width(width), m_height(height),
		  bo_prev(0), bo_next(0)
	{
		gsurface = unique_ptr<GbmSurface>(new GbmSurface(gdev, width, height));
		esurface = eglCreateWindowSurface(egl.display(), egl.config(), gsurface->handle(), NULL);
		FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface");
	}

	~GbmEglSurface()
	{
		if (bo_next)
			gsurface->release_buffer(bo_next);
		eglDestroySurface(egl.display(), esurface);
	}

	void make_current()
	{
		FAIL_IF(!gsurface->has_free(), "No free buffers");

		eglMakeCurrent(egl.display(), esurface, esurface, egl.context());
	}

	void swap_buffers()
	{
		eglSwapBuffers(egl.display(), esurface);
	}

	static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
	{
		auto fb = reinterpret_cast<Framebuffer*>(data);
		delete fb;
	}

	static Framebuffer* drm_fb_get_from_bo(struct gbm_bo *bo, Card& card)
	{
		auto fb = reinterpret_cast<Framebuffer*>(gbm_bo_get_user_data(bo));
		if (fb)
			return fb;

		uint32_t width = gbm_bo_get_width(bo);
		uint32_t height = gbm_bo_get_height(bo);
		uint32_t stride = gbm_bo_get_stride(bo);
		uint32_t handle = gbm_bo_get_handle(bo).u32;

		fb = new ExtFramebuffer(card, width, height, 24, 32, stride, handle);

		gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);

		return fb;
	}

	Framebuffer* lock_next()
	{
		bo_prev = bo_next;
		bo_next = gsurface->lock_front_buffer();
		FAIL_IF(!bo_next, "could not lock gbm buffer");
		return drm_fb_get_from_bo(bo_next, card);
	}

	void free_prev()
	{
		if (bo_prev) {
			gsurface->release_buffer(bo_prev);
			bo_prev = 0;
		}
	}

	uint32_t width() const { return m_width; }
	uint32_t height() const { return m_height; }

private:
	Card& card;
	const EglState& egl;

	unique_ptr<GbmSurface> gsurface;
	EGLSurface esurface;

	int m_width;
	int m_height;

	struct gbm_bo* bo_prev;
	struct gbm_bo* bo_next;
};

class OutputHandler : private PageFlipHandlerBase
{
public:
	OutputHandler(Card& card, GbmDevice& gdev, const EglState& egl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* plane, float rotation_mult)
		: m_frame_num(0), m_connector(connector), m_crtc(crtc), m_plane(plane), m_mode(mode),
		  m_rotation_mult(rotation_mult)
	{
		m_surface1 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, mode.hdisplay, mode.vdisplay));
		m_scene1 = unique_ptr<GlScene>(new GlScene());
		m_scene1->set_viewport(m_surface1->width(), m_surface1->height());

		if (m_plane) {
			m_surface2 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, 400, 400));
			m_scene2 = unique_ptr<GlScene>(new GlScene());
			m_scene2->set_viewport(m_surface2->width(), m_surface2->height());
		}
	}

	OutputHandler(const OutputHandler& other) = delete;
	OutputHandler& operator=(const OutputHandler& other) = delete;

	void setup()
	{
		int ret;

		m_surface1->make_current();
		m_surface1->swap_buffers();
		Framebuffer* fb = m_surface1->lock_next();

		Framebuffer* planefb = 0;

		if (m_plane) {
			m_surface2->make_current();
			m_surface2->swap_buffers();
			planefb = m_surface2->lock_next();
		}


		ret = m_crtc->set_mode(m_connector, *fb, m_mode);
		FAIL_IF(ret, "failed to set mode");

		if (m_crtc->card().has_atomic()) {
			Plane* root_plane = 0;
			for (Plane* p : m_crtc->get_possible_planes()) {
				if (p->crtc_id() == m_crtc->id()) {
					root_plane = p;
					break;
				}
			}

			FAIL_IF(!root_plane, "No primary plane for crtc %d", m_crtc->id());

			m_root_plane = root_plane;
		}

		if (m_plane) {
			ret = m_crtc->set_plane(m_plane, *planefb,
						0, 0, planefb->width(), planefb->height(),
						0, 0, planefb->width(), planefb->height());
			FAIL_IF(ret, "failed to set plane");
		}
	}

	void start_flipping()
	{
		m_t1 = chrono::steady_clock::now();
		queue_next();
	}

private:
	void handle_page_flip(uint32_t frame, double time)
	{
		++m_frame_num;

		if (m_frame_num  % 100 == 0) {
			auto t2 = chrono::steady_clock::now();
			chrono::duration<float> fsec = t2 - m_t1;
			printf("fps: %f\n", 100.0 / fsec.count());
			m_t1 = t2;
		}

		s_flip_pending--;

		m_surface1->free_prev();
		if (m_plane)
			m_surface2->free_prev();

		if (s_need_exit)
			return;

		queue_next();
	}

	void queue_next()
	{
		m_surface1->make_current();
		m_scene1->draw(m_frame_num * m_rotation_mult);
		m_surface1->swap_buffers();
		Framebuffer* fb = m_surface1->lock_next();

		Framebuffer* planefb = 0;

		if (m_plane) {
			m_surface2->make_current();
			m_scene2->draw(m_frame_num * m_rotation_mult * 2);
			m_surface2->swap_buffers();
			planefb = m_surface2->lock_next();
		}

		if (m_crtc->card().has_atomic()) {
			int r;

			AtomicReq req(m_crtc->card());

			req.add(m_root_plane, "FB_ID", fb->id());
			if (m_plane)
				req.add(m_plane, "FB_ID", planefb->id());

			r = req.test();
			FAIL_IF(r, "atomic test failed");

			r = req.commit(this);
			FAIL_IF(r, "atomic commit failed");
		} else {
			int ret;

			ret = m_crtc->page_flip(*fb, this);
			FAIL_IF(ret, "failed to queue page flip");

			if (m_plane) {
				ret = m_crtc->set_plane(m_plane, *planefb,
							0, 0, planefb->width(), planefb->height(),
							0, 0, planefb->width(), planefb->height());
				FAIL_IF(ret, "failed to set plane");
			}
		}

		s_flip_pending++;
	}

	int m_frame_num;
	chrono::steady_clock::time_point m_t1;

	Connector* m_connector;
	Crtc* m_crtc;
	Plane* m_plane;
	Videomode m_mode;
	Plane* m_root_plane;

	unique_ptr<GbmEglSurface> m_surface1;
	unique_ptr<GbmEglSurface> m_surface2;

	unique_ptr<GlScene> m_scene1;
	unique_ptr<GlScene> m_scene2;

	float m_rotation_mult;
};

void main_gbm()
{
	Card card;

	GbmDevice gdev(card);
	EglState egl(gdev.handle());

	vector<unique_ptr<OutputHandler>> outputs;
	vector<Plane*> used_planes;

	float rot_mult = 1;

	for (auto pipe : card.get_connected_pipelines()) {
		auto connector = pipe.connector;
		auto crtc = pipe.crtc;
		auto mode = connector->get_default_mode();

		Plane* plane = 0;

		if (s_support_planes) {
			for (Plane* p : crtc->get_possible_planes()) {
				if (find(used_planes.begin(), used_planes.end(), p) != used_planes.end())
					continue;

				if (p->plane_type() != PlaneType::Overlay)
					continue;

				plane = p;
				break;
			}
		}

		if (plane)
			used_planes.push_back(plane);

		auto out = new OutputHandler(card, gdev, egl, connector, crtc, mode, plane, rot_mult);
		outputs.emplace_back(out);

		rot_mult *= 1.33;
	}

	for (auto& out : outputs)
		out->setup();

	for (auto& out : outputs)
		out->start_flipping();

	struct pollfd fds[2] = { };
	fds[0].fd = 0;
	fds[0].events =  POLLIN;
	fds[1].fd = card.fd();
	fds[1].events =  POLLIN;

	while (!s_need_exit || s_flip_pending) {
		int r = poll(fds, ARRAY_SIZE(fds), -1);
		FAIL_IF(r < 0, "poll error %d", r);

		if (fds[0].revents)
			s_need_exit = true;

		if (fds[1].revents)
			card.call_page_flip_handlers();
	}
}