/* * \brief Frame-buffer driver for the OMAP4430 display-subsystem (HDMI) * \author Norman Feske * \author Martin Stein * \date 2012-06-01 */ /* * Copyright (C) 2012-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ /* Genode includes */ #include #include #include #include /* local includes */ #include #include #include namespace Framebuffer { using namespace Genode; class Driver; } class Framebuffer::Driver { public: enum Format { FORMAT_RGB565 }; enum Output { OUTPUT_LCD, OUTPUT_HDMI }; private: Genode::Env &_env; bool _init_lcd(addr_t phys_base); bool _init_hdmi(addr_t phys_base); struct Timer_delayer : Timer::Connection, Mmio::Delayer { Timer_delayer(Genode::Env &env) : Timer::Connection(env) { } /** * Implementation of 'Delayer' interface */ void usleep(uint64_t us) { Timer::Connection::usleep(us); } } _delayer { _env }; /* display sub system registers */ Attached_io_mem_dataspace _dss_mmio; Dss _dss; /* display controller registers */ Attached_io_mem_dataspace _dispc_mmio; Dispc _dispc; /* HDMI controller registers */ Attached_io_mem_dataspace _hdmi_mmio; Hdmi _hdmi; size_t _fb_width; size_t _fb_height; Format _fb_format; public: Driver(Genode::Env &env); static size_t bytes_per_pixel(Format format) { switch (format) { case FORMAT_RGB565: return 2; } return 0; } size_t buffer_size(size_t width, size_t height, Format format) { return bytes_per_pixel(format)*width*height; } bool init(size_t width, size_t height, Format format, Output output, addr_t phys_base); }; Framebuffer::Driver::Driver(Genode::Env &env) : _env(env), _dss_mmio(_env, Panda::DSS_MMIO_BASE, Panda::DSS_MMIO_SIZE), _dss((addr_t)_dss_mmio.local_addr()), _dispc_mmio(_env, Panda::DISPC_MMIO_BASE, Panda::DISPC_MMIO_SIZE), _dispc((addr_t)_dispc_mmio.local_addr()), _hdmi_mmio(_env, Panda::HDMI_MMIO_BASE, Panda::HDMI_MMIO_SIZE), _hdmi((addr_t)_hdmi_mmio.local_addr()), _fb_width(0), _fb_height(0), _fb_format(FORMAT_RGB565) { } bool Framebuffer::Driver::_init_lcd(Framebuffer::addr_t phys_base) { /* disable LCD to allow editing configuration */ _dispc.write(0); /* set load mode */ _dispc.write(Dispc::Config1::Load_mode::DATA_EVERY_FRAME); _dispc.write(_fb_width - 1); _dispc.write(_fb_height - 1); Dispc::Gfx_attributes::access_t pixel_format = 0; switch (_fb_format) { case FORMAT_RGB565: pixel_format = Dispc::Gfx_attributes::Format::RGB16; break; } _dispc.write(pixel_format); _dispc.write(phys_base); _dispc.write(phys_base); _dispc.write(_fb_width - 1); _dispc.write(_fb_height - 1); _dispc.write(0x6d2240); _dispc.write(1); _dispc.write(1); _dispc.write(1); return true; } bool Framebuffer::Driver::_init_hdmi(Framebuffer::addr_t phys_base) { /* enable display core clock and set divider to 1 */ _dispc.write(1); _dispc.write(1); /* set load mode */ _dispc.write(Dispc::Config1::Load_mode::DATA_EVERY_FRAME); _hdmi.write(0); if (!_hdmi.issue_pwr_pll_command(Hdmi::Pwr_ctrl::ALL_OFF, _delayer)) { error("powering off HDMI timed out"); return false; } if (!_hdmi.issue_pwr_pll_command(Hdmi::Pwr_ctrl::BOTH_ON_ALL_CLKS, _delayer)) { error("powering on HDMI timed out"); return false; } if (!_hdmi.reset_pll(_delayer)) { error("resetting HDMI PLL timed out"); return false; } _hdmi.write(Hdmi::Pll_control::Mode::MANUAL); _hdmi.write(270); _hdmi.write(15); _hdmi.write(0); _hdmi.write(1); _hdmi.write(0); _hdmi.write(3); _hdmi.write(2); _hdmi.write(1); _hdmi.write(0x35555); if (!_hdmi.pll_go(_delayer)) { error("HDMI PLL GO timed out"); return false; } if (!_hdmi.issue_pwr_phy_command(Hdmi::Pwr_ctrl::LDOON, _delayer)) { error("HDMI Phy power on timed out"); return false; } _hdmi.write(1); _hdmi.write(0xf0000000); if (!_hdmi.issue_pwr_phy_command(Hdmi::Pwr_ctrl::TXON, _delayer)) { error("HDMI Txphy power on timed out"); return false; } _hdmi.write(160); _hdmi.write(24); _hdmi.write(136); _hdmi.write(29); _hdmi.write(3); _hdmi.write(6); _hdmi.write(Hdmi::Video_cfg::Packing_mode::PACK_24B); _hdmi.write(_fb_width); _hdmi.write(_fb_height); _hdmi.write(0); _hdmi.write(0); _hdmi.write(0); _hdmi.write(1); _dss.write(Dss::Ctrl::Venc_hdmi_switch::HDMI); _dispc.write(_fb_width - 1); _dispc.write(_fb_height - 1); _hdmi.write(1); Dispc::Gfx_attributes::access_t pixel_format = 0; switch (_fb_format) { case FORMAT_RGB565: pixel_format = Dispc::Gfx_attributes::Format::RGB16; break; } _dispc.write(pixel_format); _dispc.write(phys_base); _dispc.write(phys_base); _dispc.write(_fb_width - 1); _dispc.write(_fb_height - 1); _dispc.write(0x6d2240); _dispc.write(1); _dispc.write(Dispc::Gfx_attributes::Channelout::TV); _dispc.write(Dispc::Gfx_attributes::Channelout2::PRIMARY_LCD); _dispc.write(1); _dispc.write(1); try { _dispc.wait_for(_delayer, Dispc::Control1::Go_tv::Equal(Dispc::Control1::Go_tv::HW_UPDATE_DONE)); } catch (Dispc::Polling_timeout) { error("Go_tv timed out"); return false; } return true; } bool Framebuffer::Driver::init(size_t width, size_t height, Framebuffer::Driver::Format format, Output output, Framebuffer::addr_t phys_base) { _fb_width = width; _fb_height = height; _fb_format = format; bool ret = false; switch (output) { case OUTPUT_LCD: ret = _init_lcd(phys_base); break; case OUTPUT_HDMI: ret = _init_hdmi(phys_base); break; default: error("unknown output ", (int)output, " specified"); } return ret; }