diff --git a/repos/os/src/drivers/touch/synaptics_dsx/i2c.h b/repos/os/src/drivers/touch/synaptics_dsx/i2c.h new file mode 100644 index 0000000000..ed096ad48c --- /dev/null +++ b/repos/os/src/drivers/touch/synaptics_dsx/i2c.h @@ -0,0 +1,183 @@ +/* + * \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 +#include + +/* local includes */ +#include "irq_handler.h" + +namespace I2c +{ + class I2c; +} + + +class I2c::I2c : Genode::Mmio +{ + private: + + struct Address : public Register<0x0, 16> + { + struct Addr : Bitfield<1,7> {}; + }; + + struct Freq_divider : public Register<0x4, 16> {}; + + struct Control : public Register<0x8, 16> + { + 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, 16> + { + 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, 16> { }; + + + class No_ack : Genode::Exception {}; + + Irq_handler & _irq_handler; + + void _busy() { while (!read()); } + + void _start() + { + /* clock enable */ + /* input root 90 is 25Mhz target is 400Khz, divide by 64 */ + write(0x2a); + write(0); + write(Control::Enable::bits(1)); + + while (!read()) { ; } + + write(1); + + _busy(); + + write(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(0); + + /* clock disable */ + } + + void _write(Genode::uint8_t value) + { + write(value); + + do { _irq_handler.wait(); } + while (!read()); + + write(0); + _irq_handler.ack(); + + if (read()) throw No_ack(); + } + + public: + + I2c(Genode::addr_t const base, Irq_handler &irq_handler) + : Mmio(base), + _irq_handler(irq_handler) + { + write(0); + write(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(0); + if (num > 1) + write(0); + read(); /* dummy read */ + + for (Genode::size_t i = 0; i < num; i++) { + + do { _irq_handler.wait(); } + while (!read()); + + write(0); + + if (i == num-1) { + write(0); + write(0); + while (read()) ; + } else if (i == num-2) { + write(1); + } + + buf[i] = read(); + _irq_handler.ack(); + } + + _stop(); + return; + } catch(No_ack) { + Genode::log("no ack"); + _stop(); + } + } + } +}; + +#endif /* _DRIVERS__INPUT__SPEC__IMX53__I2C_H_ */ diff --git a/repos/os/src/drivers/touch/synaptics_dsx/irq_handler.h b/repos/os/src/drivers/touch/synaptics_dsx/irq_handler.h new file mode 100644 index 0000000000..277ded979f --- /dev/null +++ b/repos/os/src/drivers/touch/synaptics_dsx/irq_handler.h @@ -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 + +class Irq_handler +{ + private: + + Genode::Env &_env; + Genode::Irq_session_client _irq; + Genode::Io_signal_handler _handler; + + unsigned _sem_cnt = 1; + + void _handle() { _sem_cnt = 0; } + + public: + + Irq_handler(Genode::Env &env, Genode::Irq_session_capability irq) + : + _env(env), _irq(irq), + _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_ */ diff --git a/repos/os/src/drivers/touch/synaptics_dsx/main.cc b/repos/os/src/drivers/touch/synaptics_dsx/main.cc new file mode 100644 index 0000000000..90db02715f --- /dev/null +++ b/repos/os/src/drivers/touch/synaptics_dsx/main.cc @@ -0,0 +1,192 @@ +/** + * \brief Synaptics DSX touch input + * \author Sebastian Sumpf + * \date 2020-09-03 + */ + +/* + * 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 +#include +#include + +#include +#include +#include +#include + +using namespace Genode; + + +struct Finger_data +{ + uint8_t status; + uint8_t x_lsb; + uint8_t x_msb; + uint8_t y_lsb; + uint8_t y_msb; + uint8_t wx; + uint8_t wy; + + unsigned x() const + { + return 1080 - ((x_msb << 8) | x_lsb); + } + + unsigned y() const + { + return 1920 - ((y_msb << 8) | y_lsb); + } + + void dump() const + { + log("status: ", Hex(status), + " x_lsb: ", x_lsb, " x_msb: ", x_msb, + " y_lsb: ", y_lsb, " y_msb: ", y_msb, + " wx: ", wx, " wy: ", wy, + " x: ", x(), + " y: ", y()); + } +}; + + +struct Synaptics +{ + enum { FINGERS = 5, I2C_ADDR = 0x20, }; + enum Gpio_irq { IRQ = 135 }; + + Genode::Env &env; + Irq_handler _i2c_irq_handler; + Attached_dataspace _i2c_ds; + I2c::I2c _i2c { (addr_t)_i2c_ds.local_addr(), _i2c_irq_handler }; + Gpio::Connection _gpio { env, IRQ }; + Irq_session_client _irq { _gpio.irq_session(Gpio::Session::LOW_LEVEL) }; + Io_signal_handler _irq_dispatcher { env.ep(), *this, &Synaptics::_handle_irq }; + Event::Connection _event { env }; + uint8_t _buf[10]; + bool _button[FINGERS] { }; + + void _handle_event(Event::Session_client::Batch &batch) + { + /* retrieve status */ + Finger_data fingers[FINGERS]; + _buf[0] = 6; + _i2c.send(I2C_ADDR, _buf, 1); + _i2c.recv(I2C_ADDR, (uint8_t *)fingers, sizeof(fingers)); + + for (int i = 0; i < FINGERS; i++) { + Finger_data ¤t = fingers[i]; + + Input::Touch_id id { i }; + + if (current.status == 0) { + if (_button[i]) { + batch.submit(Input::Release{Input::BTN_LEFT}); + batch.submit(Input::Touch_release{id}); + _button[i] = false; + } + continue; + } + + batch.submit(Input::Absolute_motion { (int)current.x(), (int)current.y() }); + batch.submit(Input::Touch { id, (float)current.x(), (float)current.y() }); + + if (_button[i] == false) { + batch.submit(Input::Press { Input::BTN_LEFT }); + } + _button[i] = true; + } + } + + void _handle_irq() + { + /* read device IRQ */ + _buf[0] = 4; + _i2c.send(I2C_ADDR, _buf, 1); + _i2c.recv(I2C_ADDR, _buf, 2); + + _event.with_batch([&] (Event::Session_client::Batch &batch) { + _handle_event(batch); + }); + + _irq.ack_irq(); + } + + Synaptics(Env &env, Dataspace_capability io_mem, + Irq_session_capability irq) + : env(env), + _i2c_irq_handler(env, irq), + _i2c_ds(env.rm(), io_mem) + { + + /* set page 0 */ + _buf[0] = 0xff; + _buf[1] = 0; + _i2c.send(I2C_ADDR, _buf, 2); + + /* enable interrupt */ + _buf[0] = 0xf; + _buf[1] = 0x16; + _i2c.send(I2C_ADDR, _buf, 2); + + /* set configured */ + _buf[0] = 0xe; + _buf[1] = 0x84; + _i2c.send(I2C_ADDR, _buf, 2); + + /* GPIO touchscreen handling */ + _gpio.direction(Gpio::Session::IN); + + _irq.sigh(_irq_dispatcher); + _irq.ack_irq(); + } +}; + + +struct Main +{ + Platform::Connection _platform_connection; + Constructible _synaptics { }; + + Main(Env &env) + : _platform_connection(env) + { + using namespace Platform; + + Device_capability cap; + try { + cap = _platform_connection.device_by_index(0); + } catch (...) { + error("Could not acquire device resources"); + return; + } + + if (cap.valid() == false) { + return; + } + + Device_client device { cap }; + + Dataspace_capability io_mem = device.io_mem_dataspace(); + if (io_mem.valid() == false) { + Genode::warning("No 'io_mem' node present ... skipping"); + return; + } + + Irq_session_capability irq = device.irq(); + if (irq.valid() == false) { + Genode::warning("No 'irq' node present ... skipping"); + return; + } + + _synaptics.construct(env, io_mem, irq); + } +}; + +void Component::construct(Genode::Env &env) { static Main main(env); } diff --git a/repos/os/src/drivers/touch/synaptics_dsx/target.mk b/repos/os/src/drivers/touch/synaptics_dsx/target.mk new file mode 100644 index 0000000000..37b9932d55 --- /dev/null +++ b/repos/os/src/drivers/touch/synaptics_dsx/target.mk @@ -0,0 +1,5 @@ +TARGET = imx8_synaptics_touch_drv +REQUIRES = arm_v8 +SRC_CC = main.cc +LIBS = base +INC_DIR += $(PRG_DIR)