rpi: Framebuffer and platform drivers

The platform driver is used to access the features provided by the
Videocore mboxes, i.e., power configuration and framebuffer setup. The
framebuffer driver uses the platform interface to setup a screen mode of
1024x768.
This commit is contained in:
Norman Feske 2013-09-15 17:48:10 +02:00
parent 1d5c11c7af
commit 148206d757
12 changed files with 921 additions and 1 deletions

View File

@ -5,7 +5,7 @@
#
# denote wich specs are also fullfilled by this spec
SPECS += arm_v6 pl011 usb
SPECS += arm_v6 pl011 usb framebuffer
# add repository relative include paths
REP_INC_DIR += include/platform/rpi

View File

@ -0,0 +1,59 @@
/*
* \brief Framebuffer info structure
* \author Norman Feske
* \date 2013-09-15
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _PLATFORM__FRAMEBUFFER_INFO_H_
#define _PLATFORM__FRAMEBUFFER_INFO_H_
#include <base/stdint.h>
namespace Platform {
using namespace Genode;
struct Framebuffer_info;
}
/**
* Structure used by the protocol between the Videocore GPU and the ARM CPU for
* setting up the framebuffer via the mbox.
*/
struct Platform::Framebuffer_info
{
uint32_t phys_width;
uint32_t phys_height;
uint32_t virt_width;
uint32_t virt_height;
uint32_t pitch = 0;
uint32_t depth;
uint32_t x_offset = 0;
uint32_t y_offset = 0;
uint32_t addr = 0;
uint32_t size = 0;
/**
* Default constructor needed to make the object transferable via RPC
*/
Framebuffer_info()
:
phys_width(0), phys_height(0), virt_width(0), virt_height(),
depth(0)
{ }
Framebuffer_info(uint32_t width, uint32_t height, uint32_t depth)
:
phys_width(width), phys_height(height),
virt_width(width), virt_height(height),
depth(depth)
{ }
};
#endif /* _PLATFORM__FRAMEBUFFER_INFO_H_ */

View File

@ -0,0 +1,226 @@
/*
* \brief Marshalling of mbox messages for property channel
* \author Norman Feske
* \date 2013-09-15
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _PLATFORM__PROPERTY_MESSAGE_H_
#define _PLATFORM__PROPERTY_MESSAGE_H_
/* Genode includes */
#include <util/misc_math.h>
#include <base/printf.h>
/* board-specific includes */
#include <drivers/board_base.h>
namespace Platform {
using namespace Genode;
struct Property_message;
}
/**
* Mailbox message buffer for the property channel
*
* This data structure is overlayed with memory shared with the VC. It
* contains a header, followed by a sequence of so-called command tags, wrapped
* up by a zero as an end marker.
*/
struct Platform::Property_message
{
uint32_t buf_size;
enum Code { REQUEST = 0,
RESPONSE_SUCCESS = 0x80000000 };
Code code = REQUEST;
/*
* Start of the buffer that contains a sequence of tags
*/
char buffer[];
/*
* There must be no member variables after this point
*/
/*
* Each tag consists of a header, a part with request arguments, and a
* part with responses.
*/
template <typename TAG>
struct Tag
{
uint32_t const opcode;
/**
* Size of tag buffer
*/
uint32_t const buf_size;
/**
* Size of request part of the tag
*
* The value is never changed locally. Therefore, it is declared as
* const. However, it will be updated by the VC. So we declare it
* volatile, too.
*/
uint32_t volatile const len;
char payload[];
/**
* Utility for returning a response size of a tag type
*
* Depending on the presence of a 'TAG::Response' type, we need to
* return the actual size of the response (if the type is present) or
* 0. Both overloads are called with a compliant parameter 0. But only
* if 'T::Response' exists, the first overload is selected.
*
* SFINAE to the rescue!
*/
template <typename T>
static size_t response_size(typename T::Response *)
{
return sizeof(typename T::Response);
}
template <typename>
static size_t response_size(...)
{
return 0;
}
template <typename T>
static size_t request_size(typename T::Request *)
{
return sizeof(typename T::Request);
}
template <typename>
static size_t request_size(...)
{
return 0;
}
template <typename T>
struct Placeable : T
{
template <typename... ARGS>
Placeable(ARGS... args) : T(args...) { }
inline void *operator new (size_t, void *ptr) { return ptr; }
};
template <typename T, typename... ARGS>
void construct_request(typename T::Request *, ARGS... args)
{
new ((typename T::Request *)payload)
Placeable<typename T::Request>(args...);
}
template <typename>
void construct_request(...) { }
template <typename T>
void construct_response(typename T::Response *)
{
new (payload) Placeable<typename T::Response>;
}
template <typename>
void construct_response(...) { }
static constexpr size_t payload_size()
{
return max(request_size<TAG>(0), response_size<TAG>(0));
}
template <typename... REQUEST_ARGS>
Tag(REQUEST_ARGS... request_args)
:
opcode(TAG::opcode()),
buf_size(payload_size()),
len(request_size<TAG>(0))
{
/*
* The order is important. If we called 'construct_response' after
* 'construct_request', we would overwrite the request parameters
* with the default response.
*/
construct_response<TAG>(0);
construct_request<TAG>(0, request_args...);
}
inline void *operator new (size_t, void *ptr) { return ptr; }
};
void reset()
{
buf_size = 0;
code = REQUEST;
}
/**
* \return reference to tag in the message buffer
*/
template <typename POLICY, typename... REQUEST_ARGS>
typename POLICY::Response const &append(REQUEST_ARGS... request_args)
{
auto *tag = new (buffer + buf_size) Tag<POLICY>(request_args...);
buf_size += sizeof(Tag<POLICY>) + Tag<POLICY>::payload_size();
return *(typename POLICY::Response *)tag->payload;
}
template <typename POLICY, typename... REQUEST_ARGS>
void append_no_response(REQUEST_ARGS... request_args)
{
new (buffer + buf_size) Tag<POLICY>(request_args...);
buf_size += sizeof(Tag<POLICY>) + Tag<POLICY>::payload_size();
}
void finalize()
{
/* append end tag */
*(uint32_t *)(buffer + buf_size) = 0;
buf_size += sizeof(uint32_t);
}
static unsigned channel() { return 8; }
static Board_base::Videocore_cache_policy cache_policy()
{
return Board_base::NON_COHERENT; /* for channel 8 only */
}
void dump(char const *label)
{
unsigned const *buf = (unsigned *)this;
printf("%s message:\n", label);
for (unsigned i = 0;; i++) {
for (unsigned j = 0; j < 8; j++) {
unsigned const msg_word_idx = i*8 + j;
printf(" %08x", buf[msg_word_idx]);
if (msg_word_idx*sizeof(unsigned) < buf_size)
continue;
printf("\n");
return;
}
}
}
inline void *operator new (size_t, void *ptr) { return ptr; }
};
#endif /* _PLATFORM__PROPERTY_MESSAGE_H_ */

View File

@ -0,0 +1,38 @@
/*
* \brief Raspberry Pi specific platform session client side
* \author Norman Feske
* \date 2013-09-16
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__PLATFORM_SESSION__CLIENT_H_
#define _INCLUDE__PLATFORM_SESSION__CLIENT_H_
#include <base/capability.h>
#include <base/rpc.h>
#include <platform_session/platform_session.h>
namespace Platform { struct Client; }
struct Platform::Client : Genode::Rpc_client<Platform::Session>
{
explicit Client(Capability<Session> session)
: Genode::Rpc_client<Session>(session) { }
void setup_framebuffer(Framebuffer_info &info) {
call<Rpc_setup_framebuffer>(info); }
bool power_state(Power power) {
return call<Rpc_get_power_state>(power); }
void power_state(Power power, bool enable) {
call<Rpc_set_power_state>(power, enable); }
};
#endif /* _INCLUDE__PLATFORM_SESSION__CLIENT_H_ */

View File

@ -0,0 +1,76 @@
/*
* \brief Raspberry Pi specific platform session
* \author Norman Feske
* \date 2013-09-16
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__PLATFORM_SESSION__PLATFORM_SESSION_H_
#define _INCLUDE__PLATFORM_SESSION__PLATFORM_SESSION_H_
#include <base/capability.h>
#include <base/rpc.h>
#include <dataspace/capability.h>
#include <platform/framebuffer_info.h>
namespace Platform {
using namespace Genode;
struct Session;
}
struct Platform::Session : Genode::Session
{
static const char *service_name() { return "Platform"; }
/**
* Setup framebuffer
*
* The 'info' argument serves as both input and output parameter. As input,
* it describes the desired properties of the framebuffer. In return, the
* function delivers the values that were actually taken into effect.
*/
virtual void setup_framebuffer(Framebuffer_info &info) = 0;
enum Power {
POWER_SDHCI = 0,
POWER_UART0 = 1,
POWER_UART1 = 2,
POWER_USB_HCD = 3,
POWER_I2C0 = 4,
POWER_I2C1 = 5,
POWER_I2C2 = 6,
POWER_SPI = 7,
POWER_CCP2TX = 8,
};
/**
* Request power state
*/
virtual bool power_state(Power) = 0;
/**
* Set power state
*/
virtual void power_state(Power, bool enable) = 0;
/*********************
** RPC declaration **
*********************/
GENODE_RPC(Rpc_setup_framebuffer, void, setup_framebuffer, Framebuffer_info &);
GENODE_RPC(Rpc_get_power_state, bool, power_state, Power);
GENODE_RPC(Rpc_set_power_state, void, power_state, Power, bool);
GENODE_RPC_INTERFACE(Rpc_setup_framebuffer, Rpc_set_power_state,
Rpc_get_power_state);
};
#endif /* _INCLUDE__PLATFORM_SESSION__PLATFORM_SESSION_H_ */

View File

@ -0,0 +1,98 @@
/*
* \brief Framebuffer driver for Raspberry Pi
* \author Norman Feske
* \date 2013-09-14
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <os/attached_io_mem_dataspace.h>
#include <os/static_root.h>
#include <cap_session/connection.h>
#include <base/sleep.h>
#include <framebuffer_session/framebuffer_session.h>
#include <base/rpc_server.h>
#include <platform_session/connection.h>
namespace Framebuffer {
using namespace Genode;
class Session_component;
};
class Framebuffer::Session_component : public Genode::Rpc_object<Framebuffer::Session>
{
private:
size_t const _width;
size_t const _height;
Attached_io_mem_dataspace _fb_mem;
public:
Session_component(addr_t phys_addr, size_t size, size_t width, size_t height)
:
_width(width), _height(height), _fb_mem(phys_addr, size)
{ }
/************************************
** Framebuffer::Session interface **
************************************/
Dataspace_capability dataspace() { return _fb_mem.cap(); }
void release() { }
Mode mode() const
{
return Mode(_width, _height, Mode::RGB565);
}
void mode_sigh(Genode::Signal_context_capability) { }
void refresh(int, int, int, int) { }
};
int main(int, char **)
{
using namespace Framebuffer;
using namespace Genode;
printf("--- fb_drv started ---\n");
static Platform::Connection platform;
Platform::Framebuffer_info fb_info(1024, 768, 16);
platform.setup_framebuffer(fb_info);
/*
* Initialize server entry point
*/
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "fb_ep");
/*
* Let the entry point serve the framebuffer session and root interfaces
*/
static Session_component fb_session(fb_info.addr,
fb_info.size,
fb_info.phys_width,
fb_info.phys_height);
static Static_root<Framebuffer::Session> fb_root(ep.manage(&fb_session));
/*
* Announce service
*/
env()->parent()->announce(ep.manage(&fb_root));
sleep_forever();
return 0;
}

View File

@ -0,0 +1,8 @@
TARGET = fb_drv
REQUIRES = platform_rpi
SRC_CC = main.cc
LIBS = base
INC_DIR += $(PRG_DIR)
# enable C++11 support
CC_CXX_OPT += -std=gnu++11

View File

@ -0,0 +1,64 @@
/*
* \brief Marshalling of mbox messages for framebuffer channel
* \author Norman Feske
* \date 2013-09-15
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _FRAMEBUFFER_MESSAGE_H_
#define _FRAMEBUFFER_MESSAGE_H_
/* Genode includes */
#include <util/misc_math.h>
#include <base/printf.h>
/* board-specific includes */
#include <drivers/board_base.h>
#include <platform/framebuffer_info.h>
namespace Platform { struct Framebuffer_message; }
/**
* Mailbox message buffer for the framebuffer channel
*/
struct Platform::Framebuffer_message : Framebuffer_info
{
Framebuffer_message(Framebuffer_info const &info) : Framebuffer_info(info) { }
void finalize() { }
static unsigned channel() { return 1; }
static Genode::Board_base::Videocore_cache_policy cache_policy()
{
return Genode::Board_base::COHERENT;
}
void dump(char const *label)
{
using Genode::printf;
printf("%s message:\n", label);
printf(" phys_width: %u\n", phys_width);
printf(" phys_height: %u\n", phys_height);
printf(" virt_width: %u\n", virt_width);
printf(" virt_height: %u\n", virt_height);
printf(" pitch: %u\n", pitch);
printf(" depth: %d\n", depth);
printf(" x_offset: %d\n", x_offset);
printf(" y_offset: %d\n", y_offset);
printf(" addr: 0x%08x\n", addr);
printf(" size: 0x%08x\n", size);
}
inline void *operator new (Genode::size_t, void *ptr) { return ptr; }
};
#endif /* _FRAMEBUFFER_MESSAGE_H_ */

View File

@ -0,0 +1,110 @@
/*
* \brief Driver for Raspberry Pi specific platform devices
* \author Norman Feske
* \date 2013-09-16
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/printf.h>
#include <base/sleep.h>
#include <base/rpc_server.h>
#include <cap_session/connection.h>
#include <root/component.h>
/* platform includes */
#include <platform_session/platform_session.h>
#include <platform/property_message.h>
/* local includes */
#include <mbox.h>
#include <property_command.h>
#include <framebuffer_message.h>
namespace Platform {
class Session_component;
class Root;
}
class Platform::Session_component : public Genode::Rpc_object<Platform::Session>
{
private:
Mbox &_mbox;
public:
/**
* Constructor
*/
Session_component(Mbox &mbox) : _mbox(mbox) { }
/**********************************
** Platform session interface **
**********************************/
void setup_framebuffer(Framebuffer_info &info)
{
auto const &msg = _mbox.message<Framebuffer_message>(info);
_mbox.call<Framebuffer_message>();
info = msg;
}
bool power_state(Power id)
{
auto &msg = _mbox.message<Property_message>();
auto const &res = msg.append<Property_command::Get_power_state>(id);
_mbox.call<Property_message>();
return res.state;
}
void power_state(Power id, bool enable)
{
auto &msg = _mbox.message<Property_message>();
msg.append_no_response<Property_command::Set_power_state>(id, enable, true);
_mbox.call<Property_message>();
}
};
class Platform::Root : public Genode::Root_component<Platform::Session_component>
{
private:
Mbox _mbox;
protected:
Session_component *_create_session(const char *args) {
return new (md_alloc()) Session_component(_mbox); }
public:
Root(Rpc_entrypoint *session_ep, Allocator *md_alloc)
: Root_component<Session_component>(session_ep, md_alloc) { }
};
int main(int, char **)
{
using namespace Platform;
PINF("--- Raspberry Pi platform driver ---\n");
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, 4096, "rpi_plat_ep");
static Platform::Root plat_root(&ep, env()->heap());
env()->parent()->announce(ep.manage(&plat_root));
sleep_forever();
return 0;
}

View File

@ -0,0 +1,118 @@
/*
* \brief Mbox for communicating between Videocore and ARM
* \author Norman Feske
* \date 2013-09-14
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _MBOX_H_
#define _MBOX_H_
/* Genode includes */
#include <util/mmio.h>
#include <base/env.h>
#include <os/attached_mmio.h>
#include <os/attached_ram_dataspace.h>
#include <base/printf.h>
#include <dataspace/client.h>
#include <timer_session/connection.h>
class Mbox : Genode::Attached_mmio
{
private:
enum { verbose = false };
typedef Genode::addr_t addr_t;
typedef Genode::uint32_t uint32_t;
typedef Genode::Dataspace_client Dataspace_client;
enum { BASE = 0x2000b800,
SIZE = 0x100 };
struct Read : Register<0x80, 32> { };
struct Status : Register<0x98, 32>
{
struct Rd_empty : Bitfield<30, 1> { };
struct Wr_full : Bitfield<31, 1> { };
};
struct Write : Register<0xa0, 32>
{
struct Channel : Bitfield<0, 4> { };
struct Value : Bitfield<4, 26> { };
struct Cache_policy : Bitfield<30, 2> { };
};
enum { MSG_BUFFER_SIZE = 0x1000 };
Genode::Attached_ram_dataspace _msg_buffer = { Genode::env()->ram_session(),
MSG_BUFFER_SIZE };
addr_t const _msg_phys = { Dataspace_client(_msg_buffer.cap()).phys_addr() };
struct Delayer : Mmio::Delayer
{
Timer::Connection timer;
void usleep(unsigned us) { timer.usleep(us); }
} _delayer;;
template <typename MESSAGE>
MESSAGE &_message()
{
return *_msg_buffer.local_addr<MESSAGE>();
}
public:
Mbox() : Genode::Attached_mmio(BASE, SIZE) { }
/**
* Return reference to typed message buffer
*/
template <typename MESSAGE, typename... ARGS>
MESSAGE &message(ARGS... args)
{
return *(new (_msg_buffer.local_addr<void>()) MESSAGE(args...));
}
template <typename MESSAGE>
void call()
{
_message<MESSAGE>().finalize();
if (verbose)
_message<MESSAGE>().dump("Input");
/* flush pending data in the read buffer */
while (!read<Status::Rd_empty>())
read<Read>();
if (!wait_for<Status::Wr_full>(0, _delayer, 500, 1)) {
PERR("Mbox: timeout waiting for ready-to-write");
return;
}
Write::access_t value = 0;
Write::Channel:: set(value, MESSAGE::channel());
Write::Value:: set(value, _msg_phys >> Write::Value::SHIFT);
Write::Cache_policy::set(value, MESSAGE::cache_policy());
write<Write>(value);
if (!wait_for<Status::Rd_empty>(0, _delayer, 500, 1)) {
PERR("Mbox: timeout waiting for response");
return;
}
if (verbose)
_message<MESSAGE>().dump("Output");
}
};
#endif /* _MBOX_H_ */

View File

@ -0,0 +1,115 @@
/*
* \brief Command definitions for the property mbox channel
* \author Norman Feske
* \date 2013-09-15
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _PROPERTY_COMMAND_H_
#define _PROPERTY_COMMAND_H_
/* Genode includes */
#include <base/stdint.h>
namespace Property_command {
using namespace Genode;
struct Get_power_state
{
static uint32_t opcode() { return 0x00020001; };
struct Request
{
uint32_t const device_id;
Request(uint32_t device_id) : device_id(device_id) { }
};
struct Response
{
uint32_t const device_id;
uint32_t const state;
};
};
struct Set_power_state
{
static uint32_t opcode() { return 0x00028001; };
struct Request
{
uint32_t const device_id;
uint32_t const state;
Request(uint32_t device_id, bool enable, bool wait)
:
device_id(device_id),
state(enable | (wait << 1))
{ }
};
struct Response
{
uint32_t const device_id;
uint32_t const state;
};
};
struct Allocate_buffer
{
static uint32_t opcode() { return 0x00040001; };
struct Request
{
uint32_t const alignment = 0x100;
};
struct Response
{
uint32_t const address = 0;
uint32_t const size = 0;
};
};
struct Release_buffer
{
static uint32_t opcode() { return 0x00048001; };
};
struct Get_physical_w_h
{
static uint32_t opcode() { return 0x00040003; };
struct Response
{
uint32_t const width = 0;
uint32_t const height = 0;
};
};
struct Set_physical_w_h
{
static uint32_t opcode() { return 0x00048003; };
struct Request
{
uint32_t const width;
uint32_t const height;
Request(uint32_t width, uint32_t height)
: width(width), height(height) { }
};
};
}
#endif /* _PROPERTY_COMMAND_H_ */

View File

@ -0,0 +1,8 @@
TARGET = platform_drv
REQUIRES = platform_rpi
SRC_CC = main.cc
INC_DIR += ${PRG_DIR}
LIBS = base
# enable C++11 support
CC_CXX_OPT += -std=gnu++11