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