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, 2020 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/re/env.h> 27 28 #include <ipc/server.h> 29 #include "backlight_object_server.h" 30 #include "spi_client.h" 31 32 33 34 /* Backlight device. */ 35 36 class server_BacklightObject : public BacklightObject 37 { 38 SPI *_spi; 39 int _min = 55, _max = 90, _start = 70; 40 41 void set_duty(int level) 42 { 43 level = level < _min ? _min : (level > _max ? _max : level); 44 45 /* PWM_DUTY = R05h<5:3> = 5% increments from min to max */ 46 47 int duty = ((level - _min) / 5) << 3; 48 49 _spi->send(16, 0x0516 | duty); /* R05h: GRB=0 (reset); PWM_DUTY=duty; SHDB2=1, SHDB1=1 (power-related); STB=0 (standby) */ 50 _spi->send(16, 0x0546 | duty); /* R05h: GRB=1 (normal operation); ... */ 51 _spi->send(16, 0x078d); /* R07h: HBLK=141 (horizontal blanking period from start of hsync pulse to data start) */ 52 _spi->send(16, 0x1301); /* R13h: IN_SEL=1 (alignment mode) */ 53 _spi->send(16, 0x0547 | duty); /* R05h: ...; STB=1 (not standby) */ 54 } 55 56 public: 57 explicit server_BacklightObject(SPI *spi) 58 : _spi(spi) 59 { 60 } 61 62 /* Disable the backlight. */ 63 64 long disable() 65 { 66 _spi->send(16, 0x0546); /* R05h: GRB=1 (normal operation); SHDB2=1, SHDB1=1 (power-related); STB=0 (standby) */ 67 return L4_EOK; 68 } 69 70 /* Enable the backlight. */ 71 72 long enable() 73 { 74 set_duty(_start); 75 return L4_EOK; 76 } 77 78 /* Use the SPI device to update the brightness level. */ 79 80 long set_brightness(int level) 81 { 82 level = level < _min ? _min : (level > _max ? _max : level); 83 _spi->send(16, 0x0300 | level); /* R03h: brightness */ 84 return L4_EOK; 85 } 86 }; 87 88 89 90 int main(void) 91 { 92 /* Obtain a reference to the SPI device. */ 93 94 l4_cap_idx_t spi = l4re_env_get_cap("spi"); 95 if (!l4_is_valid_cap(spi)) return 1; 96 97 client_SPI spi_obj(spi); 98 99 /* Initialise and register a new server object. */ 100 101 server_BacklightObject obj(&spi_obj); 102 l4_cap_idx_t server; 103 104 if (ipc_server_bind("backlight", (l4_umword_t) &obj, &server)) return 1; 105 106 /* Enter the IPC server loop. */ 107 108 ipc_server_loop(BacklightObject_expected_items, &obj, 109 (ipc_server_handler_type) handle_BacklightObject); 110 return 0; 111 }