Add i.MX53 PWM driver and tablet input driver

This commit is contained in:
Stefan Kalkowski
2020-04-22 18:02:10 +02:00
committed by Norman Feske
parent 9c7df24e20
commit 1e2324c7fa
10 changed files with 730 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
/*
* \brief Input-driver
* \author Stefan Kalkowski
* \date 2013-03-15
*/
/*
* Copyright (C) 2013-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.
*/
#ifndef _DRIVERS__INPUT__SPEC__IMX53__DRIVER_H_
#define _DRIVERS__INPUT__SPEC__IMX53__DRIVER_H_
/* Genode includes */
#include <base/env.h>
#include <base/signal.h>
#include <gpio_session/connection.h>
/* local includes */
#include <egalax_ts.h>
#include <mpr121.h>
namespace Input {
class Tablet_driver;
}
class Input::Tablet_driver
{
private:
enum Gpio_irqs {
GPIO_TOUCH = 84,
GPIO_BUTTON = 132,
};
Event_queue &_ev_queue;
Gpio::Connection _gpio_ts;
Gpio::Connection _gpio_bt;
Genode::Irq_session_client _irq_ts;
Genode::Irq_session_client _irq_bt;
Genode::Io_signal_handler<Tablet_driver> _ts_dispatcher;
Genode::Io_signal_handler<Tablet_driver> _bt_dispatcher;
Touchscreen _touchscreen;
Buttons _buttons;
void _handle_ts()
{
_touchscreen.event(_ev_queue);
_irq_ts.ack_irq();
}
void _handle_bt()
{
_buttons.event(_ev_queue);
_irq_bt.ack_irq();
}
Tablet_driver(Genode::Env &env, Event_queue &ev_queue)
:
_ev_queue(ev_queue),
_gpio_ts(env, GPIO_TOUCH),
_gpio_bt(env, GPIO_BUTTON),
_irq_ts(_gpio_ts.irq_session(Gpio::Session::LOW_LEVEL)),
_irq_bt(_gpio_bt.irq_session(Gpio::Session::FALLING_EDGE)),
_ts_dispatcher(env.ep(), *this, &Tablet_driver::_handle_ts),
_bt_dispatcher(env.ep(), *this, &Tablet_driver::_handle_bt),
_touchscreen(env),
_buttons(env)
{
/* GPIO touchscreen handling */
_gpio_ts.direction(Gpio::Session::OUT);
_gpio_ts.write(true);
_gpio_ts.direction(Gpio::Session::IN);
_irq_ts.sigh(_ts_dispatcher);
_irq_ts.ack_irq();
/* GPIO button handling */
_gpio_bt.direction(Gpio::Session::OUT);
_gpio_bt.write(true);
_gpio_bt.direction(Gpio::Session::IN);
_irq_bt.sigh(_bt_dispatcher);
_irq_bt.ack_irq();
}
public:
static Tablet_driver* factory(Genode::Env &env, Event_queue &ev_queue);
};
#endif /* _DRIVERS__INPUT__SPEC__IMX53__DRIVER_H_ */

View File

@@ -0,0 +1,96 @@
/*
* \brief EETI eGalaxy touchscreen driver
* \author Stefan Kalkowski
* \date 2013-03-15
*/
/*
* Copyright (C) 2013-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.
*/
#ifndef _DRIVERS__INPUT__SPEC__IMX53__EGALAX_TS_H_
#define _DRIVERS__INPUT__SPEC__IMX53__EGALAX_TS_H_
/* Genode includes */
#include <drivers/defs/imx53.h>
#include <base/attached_io_mem_dataspace.h>
#include <input/event_queue.h>
#include <input/event.h>
#include <input/keycodes.h>
/* local includes */
#include <i2c.h>
namespace Input {
class Touchscreen;
}
class Input::Touchscreen {
private:
enum I2c_addresses { I2C_ADDR = 0x4 };
enum Finger_state { PRESSED, RELEASED };
Irq_handler _irq_handler;
Genode::Attached_io_mem_dataspace _i2c_ds;
I2c::I2c _i2c;
Genode::uint8_t _buf[10];
Finger_state _state;
public:
Touchscreen(Genode::Env &env)
:
_irq_handler(env, Imx53::I2C_3_IRQ),
_i2c_ds(env, Imx53::I2C_3_BASE, Imx53::I2C_3_SIZE),
_i2c((Genode::addr_t)_i2c_ds.local_addr<void>(),
_irq_handler),
_state(RELEASED)
{
/* ask for touchscreen firmware version */
Genode::uint8_t cmd[10] = { 0x03, 0x03, 0xa, 0x01, 0x41 };
_i2c.send(I2C_ADDR, cmd, sizeof(cmd));
}
void event(Event_queue &ev_queue)
{
_i2c.recv(I2C_ADDR, _buf, sizeof(_buf));
/* ignore all events except of multitouch*/
if (_buf[0] != 4)
return;
int x = (_buf[3] << 8) | _buf[2];
int y = (_buf[5] << 8) | _buf[4];
Genode::uint8_t state = _buf[1];
bool valid = state & (1 << 7);
int id = (state >> 2) & 0xf;
int down = state & 1;
if (!valid || id > 5)
return; /* invalid point */
x = 102400 / (3276700 / x);
y = 76800 / (3276700 / y);
/* motion event */
ev_queue.add(Input::Absolute_motion{x, y});
/* button event */
if ((down && (_state == RELEASED)) || (!down && (_state == PRESSED))) {
if (down) ev_queue.add(Input::Press {Input::BTN_LEFT});
else ev_queue.add(Input::Release{Input::BTN_LEFT});
_state = down ? PRESSED : RELEASED;
}
}
};
#endif /* _DRIVERS__INPUT__SPEC__IMX53__EGALAX_TS_H_ */

View File

@@ -0,0 +1,181 @@
/*
* \brief Driver for the i.MX53 i2c controller
* \author Stefan Kalkowski
* \date 2013-03-15
*/
/*
* Copyright (C) 2013-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.
*/
#ifndef _DRIVERS__INPUT__SPEC__IMX53__I2C_H_
#define _DRIVERS__INPUT__SPEC__IMX53__I2C_H_
/* Genode includes */
#include <util/mmio.h>
#include <timer_session/connection.h>
/* local includes */
#include "irq_handler.h"
namespace I2c
{
class I2c;
}
class I2c::I2c : Genode::Mmio
{
private:
struct Address : public Register<0x0, 8>
{
struct Addr : Bitfield<1,7> {};
};
struct Freq_divider : public Register<0x4, 8> {};
struct Control : public Register<0x8, 8>
{
struct Repeat_start : Bitfield<2,1> {};
struct Tx_ack_enable : Bitfield<3,1> {};
struct Tx_rx_select : Bitfield<4,1> {};
struct Master_slave_select : Bitfield<5,1> {};
struct Irq_enable : Bitfield<6,1> {};
struct Enable : Bitfield<7,1> {};
};
struct Status : public Register<0xc, 8>
{
struct Rcv_ack : Bitfield<0,1> {};
struct Irq : Bitfield<1,1> {};
struct Slave_rw : Bitfield<2,1> {};
struct Arbitration_lost : Bitfield<4,1> {};
struct Busy : Bitfield<5,1> {};
struct Addressed_as_slave : Bitfield<6,1> {};
struct Data_transfer : Bitfield<7,1> {};
};
struct Data : public Register<0x10, 8> { };
class No_ack : Genode::Exception {};
Irq_handler & _irq_handler;
void _busy() { while (!read<Status::Busy>()); }
void _start()
{
/* clock enable */
write<Freq_divider>(0x2c);
write<Status>(0);
write<Control>(Control::Enable::bits(1));
while (!read<Control::Enable>()) { ; }
write<Control::Master_slave_select>(1);
_busy();
write<Control>(Control::Tx_rx_select::bits(1) |
Control::Tx_ack_enable::bits(1) |
Control::Irq_enable::bits(1) |
Control::Master_slave_select::bits(1) |
Control::Enable::bits(1));
}
void _stop()
{
write<Control>(0);
/* clock disable */
}
void _write(Genode::uint8_t value)
{
write<Data>(value);
do { _irq_handler.wait(); }
while (!read<Status::Irq>());
write<Status::Irq>(0);
if (read<Status::Rcv_ack>()) throw No_ack();
_irq_handler.ack();
}
public:
I2c(Genode::addr_t const base, Irq_handler &irq_handler)
: Mmio(base),
_irq_handler(irq_handler)
{
write<Control>(0);
write<Status>(0);
}
void send(Genode::uint8_t addr, const Genode::uint8_t *buf,
Genode::size_t num)
{
while (true) {
try {
_start();
_write(addr << 1);
for (Genode::size_t i = 0; i < num; i++)
_write(buf[i]);
_stop();
return;
} catch(No_ack) { }
_stop();
}
}
void recv(Genode::uint8_t addr, Genode::uint8_t *buf,
Genode::size_t num)
{
while (true) {
try {
_start();
_write(addr << 1 | 1);
write<Control::Tx_rx_select>(0);
if (num > 1)
write<Control::Tx_ack_enable>(0);
read<Data>(); /* dummy read */
for (Genode::size_t i = 0; i < num; i++) {
do { _irq_handler.wait(); }
while (!read<Status::Irq>());
write<Status::Irq>(0);
if (i == num-1) {
write<Control::Tx_rx_select>(0);
write<Control::Master_slave_select>(0);
while (read<Status::Busy>()) ;
} else if (i == num-2) {
write<Control::Tx_ack_enable>(1);
}
buf[i] = read<Data>();
_irq_handler.ack();
}
_stop();
return;
} catch(No_ack) { }
_stop();
}
}
};
#endif /* _DRIVERS__INPUT__SPEC__IMX53__I2C_H_ */

View File

@@ -0,0 +1,53 @@
/*
* \brief Input-interrupt handler
* \author Josef Soentgen
* \date 2015-04-08
*/
/*
* Copyright (C) 2015-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.
*/
#ifndef _IRQ_HANDLER_H_
#define _IRQ_HANDLER_H_
/* Genode includes */
#include <irq_session/connection.h>
class Irq_handler
{
private:
Genode::Env &_env;
Genode::Irq_connection _irq;
Genode::Io_signal_handler<Irq_handler> _handler;
unsigned _sem_cnt = 1;
void _handle() { _sem_cnt = 0; }
public:
Irq_handler(Genode::Env &env, int irq_number)
:
_env(env), _irq(env, irq_number),
_handler(env.ep(), *this, &Irq_handler::_handle)
{
_irq.sigh(_handler);
_irq.ack_irq();
}
void wait()
{
_sem_cnt++;
while (_sem_cnt > 0)
_env.ep().wait_and_dispatch_one_io_signal();
}
void ack() { _irq.ack_irq(); }
};
#endif /* _IRQ_HANDLER_H_ */

View File

@@ -0,0 +1,65 @@
/**
* \brief Input driver front-end
* \author Norman Feske
* \author Christian Helmuth
* \author Stefan Kalkowski
* \date 2006-08-30
*/
/*
* Copyright (C) 2006-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 <base/component.h>
#include <base/env.h>
#include <base/rpc_server.h>
#include <platform_session/connection.h>
#include <input/component.h>
#include <input/root.h>
/* local includes */
#include <driver.h>
using namespace Genode;
Input::Tablet_driver* Input::Tablet_driver::factory(Genode::Env &env,
Event_queue &ev_queue)
{
static Input::Tablet_driver driver(env, ev_queue);
return &driver;
}
struct Main
{
Genode::Env &env;
Input::Session_component session { env, env.ram() };
Input::Root_component root { env.ep().rpc_ep(), session };
Main(Genode::Env &env) : env(env)
{
Platform::Connection plat_drv { env };
switch (plat_drv.revision()) {
case Platform::Session::SMD:
plat_drv.enable(Platform::Session::I2C_2);
plat_drv.enable(Platform::Session::I2C_3);
plat_drv.enable(Platform::Session::BUTTONS);
Input::Tablet_driver::factory(env, session.event_queue());
break;
default:
warning("No input driver available for this board");
}
/* tell parent about the service */
env.parent().announce(env.ep().manage(root));
}
};
void Component::construct(Genode::Env &env) { static Main main(env); }

View File

@@ -0,0 +1,107 @@
/*
* \brief Freescale MPR121 capacitative button driver
* \author Stefan Kalkowski
* \date 2013-03-15
*/
/*
* Copyright (C) 2013-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.
*/
#ifndef _DRIVERS__INPUT__SPEC__IMX53__MPR121_H_
#define _DRIVERS__INPUT__SPEC__IMX53__MPR121_H_
/* Genode includes */
#include <drivers/defs/imx53.h>
#include <base/attached_io_mem_dataspace.h>
#include <input/event_queue.h>
#include <input/event.h>
#include <input/keycodes.h>
/* local includes */
#include <i2c.h>
namespace Input {
class Buttons;
}
class Input::Buttons {
private:
enum {
I2C_ADDR = 0x5a,
};
enum Events {
RELEASE = 0,
BACK = 1,
HOME = 2,
MENU = 4,
POWER = 8,
};
Irq_handler _irq_handler;
Genode::Attached_io_mem_dataspace _i2c_ds;
I2c::I2c _i2c;
Genode::uint8_t _state;
public:
Buttons(Genode::Env &env)
:
_irq_handler(env, Imx53::I2C_2_IRQ),
_i2c_ds(env, Imx53::I2C_2_BASE, Imx53::I2C_2_SIZE),
_i2c((Genode::addr_t)_i2c_ds.local_addr<void>(),
_irq_handler),
_state(0)
{
static Genode::uint8_t init_cmd[][2] = {
{0x41, 0x8 }, {0x42, 0x5 }, {0x43, 0x8 },
{0x44, 0x5 }, {0x45, 0x8 }, {0x46, 0x5 },
{0x47, 0x8 }, {0x48, 0x5 }, {0x49, 0x8 },
{0x4a, 0x5 }, {0x4b, 0x8 }, {0x4c, 0x5 },
{0x4d, 0x8 }, {0x4e, 0x5 }, {0x4f, 0x8 },
{0x50, 0x5 }, {0x51, 0x8 }, {0x52, 0x5 },
{0x53, 0x8 }, {0x54, 0x5 }, {0x55, 0x8 },
{0x56, 0x5 }, {0x57, 0x8 }, {0x58, 0x5 },
{0x59, 0x8 }, {0x5a, 0x5 }, {0x2b, 0x1 },
{0x2c, 0x1 }, {0x2d, 0x0 }, {0x2e, 0x0 },
{0x2f, 0x1 }, {0x30, 0x1 }, {0x31, 0xff},
{0x32, 0x2 }, {0x5d, 0x4 }, {0x5c, 0xb },
{0x7b, 0xb }, {0x7d, 0xc9}, {0x7e, 0x82},
{0x7f, 0xb4}, {0x5e, 0x84}};
/* initialize mpr121 touch button device */
for (unsigned i = 0; i < sizeof(init_cmd)/2; i++)
_i2c.send(I2C_ADDR, init_cmd[i], 2);
}
void event(Event_queue &ev_queue)
{
int buttons[] = { BACK, HOME, MENU, POWER };
int codes[] = { Input::KEY_BACK, Input::KEY_HOME,
Input::KEY_MENU, Input::KEY_POWER};
Genode::uint8_t buf = 0;
_i2c.send(I2C_ADDR, &buf, 1);
_i2c.recv(I2C_ADDR, &buf, 1);
for (unsigned i = 0; i < (sizeof(buttons)/sizeof(int)); i++) {
if ((_state & buttons[i]) == (buf & buttons[i]))
continue;
Input::Keycode const key = Input::Keycode(codes[i]);
if (buf & buttons[i]) ev_queue.add(Input::Press {key});
else ev_queue.add(Input::Release{key});
};
_state = buf;
}
};
#endif /* _DRIVERS__INPUT__SPEC__IMX53__MPR121_H_ */

View File

@@ -0,0 +1,6 @@
TARGET = imx53_tablet_input_drv
REQUIRES = arm_v7
SRC_CC = main.cc
LIBS = base
INC_DIR += $(PRG_DIR)
INC_DIR += $(call select_from_repositories,include/spec/imx53)

View File

@@ -0,0 +1,86 @@
/*
* \brief Pulse width modulation
* \author Stefan Kalkowski
* \date 2020-04-22
*/
/*
* Copyright (C) 2020 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 <base/attached_rom_dataspace.h>
#include <base/attached_io_mem_dataspace.h>
#include <base/component.h>
#include <base/env.h>
#include <util/mmio.h>
struct Pwm : Genode::Mmio
{
enum Clk_src { OFF, IPG, IPG_HIGHFREQ, IPG_32K };
struct Control : Register<0x0, 32>
{
struct Enable : Bitfield<0, 1> {};
struct Clock_source : Bitfield<16, 2> {};
struct Dbgen : Bitfield<22, 1> {};
struct Waiten : Bitfield<23, 1> {};
struct Dozen : Bitfield<24, 1> {};
struct Stopen : Bitfield<25, 1> {};
};
struct Sample : Register<0xc, 32> {};
struct Period : Register<0x10,32> {};
Pwm(Genode::addr_t const mmio_base,
unsigned period,
unsigned sample,
Clk_src clk_src)
: Genode::Mmio(mmio_base)
{
write<Period>(period);
write<Sample>(sample);
Control::access_t ctrl = 0;
Control::Enable::set(ctrl, 1);
Control::Dbgen::set(ctrl, 1);
Control::Waiten::set(ctrl, 1);
Control::Dozen::set(ctrl, 1);
Control::Stopen::set(ctrl, 1);
Control::Clock_source::set(ctrl, clk_src);
write<Control>(ctrl);
}
};
struct Main
{
Genode::Env & _env;
Genode::Attached_rom_dataspace _config { _env, "config" };
/** FIXME: currently use PWM2 of i.MX53, use platform driver in future **/
Genode::Attached_io_mem_dataspace _ds { _env, 0x53fb8000, 0x4000 };
Genode::Constructible<Pwm> _pwm {};
Main(Genode::Env &env) : _env(env)
{
Genode::log("--- i.MX53 Pulse-width-modulation driver ---");
Genode::Xml_node config = _config.xml();
unsigned period = config.attribute_value<unsigned>("period", 0);
unsigned sample = config.attribute_value<unsigned>("sample", 0);
Genode::String<16> clk = config.attribute_value("clock_source",
Genode::String<16>());
Pwm::Clk_src src = Pwm::OFF;
if (clk == "ipg") src = Pwm::IPG;
if (clk == "ipg_highfreq") src = Pwm::IPG_HIGHFREQ;
if (clk == "ipg_32k") src = Pwm::IPG_32K;
_pwm.construct((Genode::addr_t)_ds.local_addr<void>(), period, sample, src);
}
};
void Component::construct(Genode::Env &env) { static Main main(env); }

View File

@@ -0,0 +1,36 @@
/*
* \brief Pulse width modulation
* \author Stefan Kalkowski
* \date 2013-03-05
*/
/*
* Copyright (C) 2013-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.
*/
#ifndef _DRIVERS__PWM__IMX53__PWM_H_
#define _DRIVERS__PWM__IMX53__PWM_H_
/* Genode includes */
#include <util/mmio.h>
struct Pwm : Genode::Mmio
{
struct Control : Register<0x0, 32> {};
struct Sample : Register<0xc, 32> {};
struct Period : Register<0x10,32> {};
Pwm(Genode::addr_t const mmio_base) : Genode::Mmio(mmio_base) { }
void enable_display()
{
write<Period>(0x64);
write<Sample>(0x64);
write<Control>(0x3c20001);
}
};
#endif /* _DRIVERS__PWM__IMX53__PWM_H_ */

View File

@@ -0,0 +1,4 @@
TARGET = imx53_pwd_drv
REQUIRES = arm_v7
SRC_CC = main.cc
LIBS = base