1 /* 2 * Access a SPI server to update a display panel backlight. 3 * This server is specific to the Ben NanoNote. 4 * 5 * (c) 2018 Paul Boddie <paul@boddie.org.uk> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA 21 */ 22 23 #include <l4/cxx/ipc_server> 24 #include <l4/re/env> 25 #include <l4/re/util/object_registry> 26 #include <l4/sys/capability> 27 28 #include <stdlib.h> 29 30 #include <l4/devices/spi-client.h> 31 #include "backlight-ops.h" 32 33 /* SPI access abstractions. */ 34 35 static L4::Cap<Spi_device_interface> spi_device; 36 37 38 39 /* Backlight device. */ 40 41 class Backlight_device_server : public L4::Server_object_t<L4::Kobject> 42 { 43 int _min = 55, _max = 90, _start = 70; 44 45 void set_duty(int level) 46 { 47 level = level < _min ? _min : (level > _max ? _max : level); 48 49 /* PWM_DUTY = R05h<5:3> = 5% increments from min to max */ 50 51 int duty = ((level - _min) / 5) << 3; 52 53 spi_device->send(16, 0x0516 | duty); /* R05h: GRB=0 (reset); PWM_DUTY=duty; SHDB2=1, SHDB1=1 (power-related); STB=0 (standby) */ 54 spi_device->send(16, 0x0546 | duty); /* R05h: GRB=1 (normal operation); ... */ 55 spi_device->send(16, 0x078d); /* R07h: HBLK=141 (horizontal blanking period from start of hsync pulse to data start) */ 56 spi_device->send(16, 0x1301); /* R13h: IN_SEL=1 (alignment mode) */ 57 spi_device->send(16, 0x0547 | duty); /* R05h: ...; STB=1 (not standby) */ 58 } 59 60 public: 61 /* Dispatch incoming requests. */ 62 63 int dispatch(l4_umword_t obj, L4::Ipc::Iostream &ios) 64 { 65 l4_msgtag_t tag; 66 int arg; 67 68 (void) obj; 69 ios >> tag; 70 71 switch (tag.label()) 72 { 73 case Backlight_op_disable: 74 disable(); 75 return L4_EOK; 76 77 case Backlight_op_enable: 78 enable(); 79 return L4_EOK; 80 81 case Backlight_op_set_brightness: 82 ios >> arg; 83 set_brightness(arg); 84 return L4_EOK; 85 86 default: 87 return -L4_EBADPROTO; 88 } 89 } 90 91 void disable() 92 { 93 spi_device->send(16, 0x055e); 94 } 95 96 void enable() 97 { 98 set_duty(_start); 99 } 100 101 /* Use the SPI device to update the brightness level. */ 102 103 void set_brightness(int level) 104 { 105 level = level < _min ? _min : (level > _max ? _max : level); 106 spi_device->send(16, 0x0300 | level); /* R03h: brightness */ 107 } 108 }; 109 110 static L4Re::Util::Registry_server<> server; 111 112 113 114 int main(void) 115 { 116 /* Obtain a reference to the SPI device. */ 117 118 spi_device = L4Re::Env::env()->get_cap<Spi_device_interface>("spi"); 119 if (!spi_device.is_valid()) return 1; 120 121 /* Initialise and register a new server object. */ 122 123 Backlight_device_server server_obj; 124 server.registry()->register_obj(&server_obj, "backlight"); 125 126 /* Enter the IPC server loop. */ 127 128 server.loop(); 129 return 0; 130 }