OMAP4 HDMI driver

This commit is contained in:
Norman Feske 2012-06-15 17:28:31 +02:00
parent 4c3df9caf1
commit a60dac3b3d
7 changed files with 563 additions and 2 deletions

View File

@ -60,7 +60,7 @@ append_if [have_spec omap4] config {
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="omap44xx_fb_drv">
<start name="omap4_fb_drv">
<resource name="RAM" quantum="4M"/>
<provides><service name="Framebuffer"/></provides>
</start>}
@ -88,7 +88,7 @@ lappend_if [have_spec sdl] boot_modules fb_sdl
lappend_if [have_spec pci] boot_modules pci_drv
lappend_if [have_spec vesa] boot_modules vesa_drv
lappend_if [have_spec pl11x] boot_modules pl11x_drv
lappend_if [have_spec omap4] boot_modules omap44xx_fb_drv
lappend_if [have_spec omap4] boot_modules omap4_fb_drv
build_boot_image $boot_modules

View File

@ -0,0 +1,111 @@
/*
* \brief Display controller
* \author Martin Stein
* \author Norman Feske
* \date 2012-06-11
*/
#ifndef _DISPC_H_
#define _DISPC_H_
/* local includes */
#include <mmio.h>
struct Dispc : Mmio
{
/**
* Configures the display controller module for outputs LCD 1 and TV
*/
struct Control1 : Register<0x40, 32>
{
struct Tv_enable : Bitfield<1, 1> { };
struct Go_tv : Bitfield<6, 1>
{
enum { HW_UPDATE_DONE = 0x0, /* set by HW after updating */
REQUEST_HW_UPDATE = 0x1 }; /* must be set by user */
};
};
/**
* Configures the display controller module for outputs LCD 1 and TV
*/
struct Config1 : Register<0x44, 32>
{
/**
* Loading of palette/gamma table
*/
struct Load_mode : Bitfield<1, 2>
{
enum { DATA_EVERY_FRAME = 0x2, };
};
};
struct Size_tv : Register<0x78, 32>
{
struct Width : Bitfield<0, 11> { };
struct Height : Bitfield<16, 11> { };
};
/**
* Configures base address of the graphics buffer
*/
struct Gfx_ba1 : Register<0x80, 32> { };
/**
* Configures the size of the graphics window
*/
struct Gfx_size : Register<0x8c, 32>
{
struct Sizex : Bitfield<0,11> { };
struct Sizey : Bitfield<16,11> { };
};
/**
* Configures the graphics attributes
*/
struct Gfx_attributes : Register<0xa0, 32>
{
struct Enable : Bitfield<0, 1> { };
struct Format : Bitfield<1, 5>
{
enum { RGB16 = 0x6,
ARGB32 = 0xc,
RGBA32 = 0xd };
};
/**
* Select GFX channel output
*/
struct Channelout : Bitfield<8, 1>
{
enum { TV = 0x1 };
};
struct Channelout2 : Bitfield<30, 2>
{
enum { PRIMARY_LCD = 0 };
};
};
struct Global_buffer : Register<0x800, 32> { };
struct Divisor : Register<0x804, 32>
{
struct Enable : Bitfield<0, 1> { };
struct Lcd : Bitfield<16, 8> { };
};
/**
* Constructor
*
* \param mmio_base base address of DISPC MMIO
*/
Dispc(Genode::addr_t const mmio_base)
:
Mmio(mmio_base)
{ }
};
#endif /* _DISPC_H_ */

View File

@ -0,0 +1,29 @@
/*
* \brief General display subsystem registers
* \author Norman Feske
* \date 2012-06-11
*/
#ifndef _DSS_H_
#define _DSS_H_
/* local includes */
#include <mmio.h>
struct Dss : Genode::Mmio
{
Dss(Genode::addr_t const mmio_base) : Genode::Mmio(mmio_base) { }
struct Sysstatus : Register<0x14, 32> { };
struct Ctrl : Register<0x40, 32>
{
struct Venc_hdmi_switch : Bitfield<15, 1>
{
enum { HDMI = 1 };
};
};
struct Status : Register<0x5c, 32> { };
};
#endif /* _DSS_H_ */

View File

@ -0,0 +1,156 @@
/*
* \brief HDMI subsystem registers
* \author Norman Feske
* \date 2012-06-11
*/
#ifndef _HDMI_H_
#define _HDMI_H_
/* local includes */
#include <mmio.h>
struct Hdmi : Mmio
{
struct Pwr_ctrl : Register<0x40, 32>
{
enum Pll_cmd_type { ALL_OFF = 0,
BOTH_ON_ALL_CLKS = 2, };
struct Pll_cmd : Bitfield<2, 2> { };
struct Pll_status : Bitfield<0, 2> { };
enum Phy_cmd_type { LDOON = 1,
TXON = 2 };
struct Phy_cmd : Bitfield<6, 2> { };
struct Phy_status : Bitfield<4, 2> { };
};
struct Video_cfg : Register<0x50, 32>
{
struct Start : Bitfield<31, 1> { };
struct Packing_mode : Bitfield<8, 3>
{
enum { PACK_24B = 1 };
};
struct Vsp : Bitfield<7, 1> { };
struct Hsp : Bitfield<6, 1> { };
struct Interlacing : Bitfield<3, 1> { };
struct Tm : Bitfield<0, 2> { };
};
struct Video_size : Register<0x60, 32>
{
struct X : Bitfield<0, 16> { };
struct Y : Bitfield<16, 16> { };
};
struct Video_timing_h : Register<0x68, 32>
{
struct Bp : Bitfield<20, 12> { };
struct Fp : Bitfield<8, 12> { };
struct Sw : Bitfield<0, 8> { };
};
struct Video_timing_v : Register<0x6c, 32>
{
struct Bp : Bitfield<20, 12> { };
struct Fp : Bitfield<8, 12> { };
struct Sw : Bitfield<0, 8> { };
};
/**
* \return true on success
*/
bool issue_pwr_pll_command(Pwr_ctrl::Pll_cmd_type cmd, Delayer &delayer)
{
write<Pwr_ctrl::Pll_cmd>(cmd);
return wait_for<Pwr_ctrl::Pll_status>(cmd, delayer);
}
bool issue_pwr_phy_command(Pwr_ctrl::Phy_cmd_type cmd, Delayer &delayer)
{
write<Pwr_ctrl::Phy_cmd>(cmd);
return wait_for<Pwr_ctrl::Phy_status>(cmd, delayer);
}
struct Pll_control : Register<0x200, 32>
{
struct Mode : Bitfield<0, 1>
{
enum { MANUAL = 0 };
};
struct Reset : Bitfield<3, 1> { };
};
struct Pll_status : Register<0x204, 32>
{
struct Reset_done : Bitfield<0, 1> { };
struct Pll_locked : Bitfield<1, 1> { };
};
bool wait_until_pll_locked(Delayer &delayer)
{
return wait_for<Pll_status::Pll_locked>(1, delayer);
};
struct Pll_go : Register<0x208, 32>
{
struct Go : Bitfield<0, 1> { };
};
bool pll_go(Delayer &delayer)
{
write<Pll_go::Go>(1);
/* wait for PLL_GO bit change and the PLL reaching locked state */
return wait_for<Pll_go::Go>(1, delayer)
&& wait_until_pll_locked(delayer);
}
struct Cfg1 : Register<0x20c, 32>
{
struct Regm : Bitfield<9, 12> { };
struct Regn : Bitfield<1, 8> { };
};
struct Cfg2 : Register<0x210, 32>
{
struct Highfreq_div_by_2 : Bitfield<12, 1> { };
struct Refen : Bitfield<13, 1> { };
struct Clkinen : Bitfield<14, 1> { };
struct Refsel : Bitfield<21, 2> { };
struct Freq_divider : Bitfield<1, 3> { };
};
struct Cfg4 : Register<0x220, 32>
{
struct Regm2 : Bitfield<18, 7> { };
struct Regmf : Bitfield<0, 18> { };
};
bool reset_pll(Delayer &delayer)
{
write<Pll_control::Reset>(0);
return wait_for<Pll_status::Reset_done>(1, delayer);
};
struct Txphy_tx_ctrl : Register<0x300, 32>
{
struct Freqout : Bitfield<30, 2> { };
};
struct Txphy_digital_ctrl : Register<0x304, 32> { };
Hdmi(Genode::addr_t const mmio_base) : Mmio(mmio_base) { }
};
#endif /* _HDMI_H_ */

View File

@ -0,0 +1,209 @@
/*
* \brief Frame-buffer driver for the OMAP4430 display-subsystem (HDMI)
* \author Norman Feske
* \author Martin Stein
* \date 2012-06-01
*/
/*
* Copyright (C) 2012 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 <framebuffer_session/framebuffer_session.h>
#include <os/attached_io_mem_dataspace.h>
#include <timer_session/connection.h>
#include <cap_session/connection.h>
#include <dataspace/client.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <root/component.h>
/* local includes */
#include <mmio.h>
#include <dss.h>
#include <dispc.h>
#include <hdmi.h>
int main(int, char **)
{
using namespace Genode;
printf("--- omap44xx framebuffer driver ---\n");
struct Timer_delayer : Timer::Connection, Delayer
{
/**
* Implementation of 'Delayer' interface
*/
void usleep(unsigned us)
{
/* polling */
if (us == 0)
return;
unsigned ms = us / 1000;
if (ms == 0)
ms = 1;
Timer::Connection::msleep(ms);
}
};
static Timer_delayer delayer;
/* memory map */
enum {
DSS_MMIO_BASE = 0x58000000,
DSS_MMIO_SIZE = 0x00001000,
DISPC_MMIO_BASE = 0x58001000,
DISPC_MMIO_SIZE = 0x1000,
HDMI_MMIO_BASE = 0x58006000,
HDMI_MMIO_SIZE = 0x1000,
};
/*
* Obtain MMIO resources
*/
/* display sub system registers */
Attached_io_mem_dataspace dss_mmio(DSS_MMIO_BASE, DSS_MMIO_SIZE);
Dss dss((addr_t)dss_mmio.local_addr<void>());
/* display controller registers */
Attached_io_mem_dataspace dispc_mmio(DISPC_MMIO_BASE, DISPC_MMIO_SIZE);
Dispc dispc((addr_t)dispc_mmio.local_addr<void>());
/* HDMI controller registers */
Attached_io_mem_dataspace hdmi_mmio(HDMI_MMIO_BASE, HDMI_MMIO_SIZE);
Hdmi hdmi((addr_t)hdmi_mmio.local_addr<void>());
/* enable display core clock and set divider to 1 */
dispc.write<Dispc::Divisor::Lcd>(1);
dispc.write<Dispc::Divisor::Enable>(1);
/* set load mode */
dispc.write<Dispc::Config1::Load_mode>(Dispc::Config1::Load_mode::DATA_EVERY_FRAME);
hdmi.write<Hdmi::Video_cfg::Start>(0);
if (!hdmi.issue_pwr_pll_command(Hdmi::Pwr_ctrl::ALL_OFF, delayer)) {
PERR("Powering off HDMI timed out\n");
return 1;
}
if (!hdmi.issue_pwr_pll_command(Hdmi::Pwr_ctrl::BOTH_ON_ALL_CLKS, delayer)) {
PERR("Powering on HDMI timed out\n");
return 2;
}
if (!hdmi.reset_pll(delayer)) {
PERR("Resetting HDMI PLL timed out\n");
return 3;
}
hdmi.write<Hdmi::Pll_control::Mode>(Hdmi::Pll_control::Mode::MANUAL);
hdmi.write<Hdmi::Cfg1::Regm>(270);
hdmi.write<Hdmi::Cfg1::Regn>(15);
hdmi.write<Hdmi::Cfg2::Highfreq_div_by_2>(0);
hdmi.write<Hdmi::Cfg2::Refen>(1);
hdmi.write<Hdmi::Cfg2::Clkinen>(0);
hdmi.write<Hdmi::Cfg2::Refsel>(3);
hdmi.write<Hdmi::Cfg2::Freq_divider>(2);
hdmi.write<Hdmi::Cfg4::Regm2>(1);
hdmi.write<Hdmi::Cfg4::Regmf>(0x35555);
if (!hdmi.pll_go(delayer)) {
PERR("HDMI PLL GO timed out");
return 4;
}
if (!hdmi.issue_pwr_phy_command(Hdmi::Pwr_ctrl::LDOON, delayer)) {
PERR("HDMI Phy power on timed out");
return 5;
}
hdmi.write<Hdmi::Txphy_tx_ctrl::Freqout>(1);
hdmi.write<Hdmi::Txphy_digital_ctrl>(0xf0000000);
if (!hdmi.issue_pwr_phy_command(Hdmi::Pwr_ctrl::TXON, delayer)) {
PERR("HDMI Txphy power on timed out");
return 6;
}
hdmi.write<Hdmi::Video_timing_h::Bp>(160);
hdmi.write<Hdmi::Video_timing_h::Fp>(24);
hdmi.write<Hdmi::Video_timing_h::Sw>(136);
hdmi.write<Hdmi::Video_timing_v::Bp>(29);
hdmi.write<Hdmi::Video_timing_v::Fp>(3);
hdmi.write<Hdmi::Video_timing_v::Sw>(6);
hdmi.write<Hdmi::Video_cfg::Packing_mode>(Hdmi::Video_cfg::Packing_mode::PACK_24B);
hdmi.write<Hdmi::Video_size::X>(1024);
hdmi.write<Hdmi::Video_size::Y>(768);
hdmi.write<Hdmi::Video_cfg::Vsp>(0);
hdmi.write<Hdmi::Video_cfg::Hsp>(0);
hdmi.write<Hdmi::Video_cfg::Interlacing>(0);
hdmi.write<Hdmi::Video_cfg::Tm>(1);
dss.write<Dss::Ctrl::Venc_hdmi_switch>(Dss::Ctrl::Venc_hdmi_switch::HDMI);
dispc.write<Dispc::Size_tv::Width>(1024 - 1);
dispc.write<Dispc::Size_tv::Height>(768 - 1);
hdmi.write<Hdmi::Video_cfg::Start>(1);
dispc.write<Dispc::Gfx_attributes::Format>(Dispc::Gfx_attributes::Format::RGB16);
typedef Genode::uint16_t pixel_t;
Genode::Dataspace_capability fb_ds =
Genode::env()->ram_session()->alloc(1024*768*sizeof(pixel_t), false);
Genode::addr_t fb_phys_base = Genode::Dataspace_client(fb_ds).phys_addr();
dispc.write<Dispc::Gfx_ba1>(fb_phys_base);
dispc.write<Dispc::Gfx_size::Sizex>(1024 - 1);
dispc.write<Dispc::Gfx_size::Sizey>(768 - 1);
dispc.write<Dispc::Global_buffer>(0x6d2240);
dispc.write<Dispc::Gfx_attributes::Enable>(1);
dispc.write<Dispc::Gfx_attributes::Channelout>(Dispc::Gfx_attributes::Channelout::TV);
dispc.write<Dispc::Gfx_attributes::Channelout2>(Dispc::Gfx_attributes::Channelout2::PRIMARY_LCD);
dispc.write<Dispc::Control1::Tv_enable>(1);
dispc.write<Dispc::Control1::Go_tv>(1);
if (!dispc.wait_for<Dispc::Control1::Go_tv>(Dispc::Control1::Go_tv::HW_UPDATE_DONE, delayer)) {
PERR("Go_tv timed out");
return 6;
}
pixel_t *fb_local_base = Genode::env()->rm_session()->attach(fb_ds);
for (unsigned y = 0; y < 768; y++) {
for (unsigned x = 0; x < 1024; x++) {
fb_local_base[y*1024 + x] = (x & 0x1f);
fb_local_base[y*1024 + x] |= ((y) & 0x1f) << 11;
fb_local_base[y*1024 + x] |= ((x + y) & 0x3f) << 5;
}
}
PINF("done");
sleep_forever();
return 0;
}

View File

@ -0,0 +1,42 @@
/*
* \brief Utilities for accessing MMIO registers
* \author Norman Feske
* \date 2012-06-15
*/
#ifndef _MMIO_H_
#define _MMIO_H_
#include <base/printf.h>
/* Genode includes */
#include <util/mmio.h>
struct Delayer
{
virtual void usleep(unsigned us) = 0;
};
/**
* Extend 'Genode::Mmio' framework with the ability to poll for bitfield states
*/
struct Mmio : Genode::Mmio
{
template <typename BITFIELD>
inline bool wait_for(typename BITFIELD::Compound_reg::access_t const value,
Delayer &delayer,
unsigned max_attempts = 500,
unsigned delay_us = 1000)
{
for (unsigned i = 0; i < max_attempts; i++, delayer.usleep(delay_us))
if (read<BITFIELD>() == value)
return true;
return false;
}
Mmio(Genode::addr_t mmio_base) : Genode::Mmio(mmio_base) { }
};
#endif /* _MMIO_H_ */

View File

@ -0,0 +1,14 @@
#
# \brief Framebuffer driver specific for OMAP44xx systems
# \author Martin Stein
# \date 2012-05-02
#
TARGET = omap4_fb_drv
REQUIRES = omap4
SRC_CC = main.cc
LIBS = cxx env server
INC_DIR += $(PRG_DIR)
vpath main.cc $(PRG_DIR)