1 /* 2 * Access the keypad GPIOs on the Letux 400 notebook computer. 3 * 4 * Copyright (C) 2018, 2024 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/devices/gpio-jz4730.h> 23 #include <l4/re/c/util/video/goos_fb.h> 24 #include <l4/re/c/video/view.h> 25 #include <l4/sys/cache.h> 26 #include <l4/util/util.h> 27 28 #include <stdio.h> 29 #include <stdint.h> 30 #include <unistd.h> 31 32 #include "memory.h" 33 34 enum Jz4730_keypad_gpio 35 { 36 Jz4730_keypad_gpio_inputs_count = 8, 37 Jz4730_keypad_gpio_outputs_count = 17, 38 }; 39 40 /* Port A input pins. */ 41 42 const uint8_t Jz4730_keypad_inputs[Jz4730_keypad_gpio_inputs_count] = { 43 0, 1, 2, 3, 4, 5, 6, 7 44 }; 45 46 const Pin_slice Jz4730_keypad_inputs_mask = {.mask=0x000000ff, .offset=0}; 47 48 /* Port D output pins. */ 49 50 const uint8_t Jz4730_keypad_outputs[Jz4730_keypad_gpio_outputs_count] = { 51 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 29 52 }; 53 54 const Pin_slice Jz4730_keypad_outputs_mask = {.mask=0x2000ffff, .offset=0}; 55 56 /* Video abstractions. */ 57 58 static l4re_util_video_goos_fb_t gfb; 59 static l4re_video_view_info_t fbi; 60 static void *fb; 61 62 /* Peripheral memory regions. */ 63 64 static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0; 65 66 /* GPIO abstractions. */ 67 68 static void *gpio_port_a, *gpio_port_d; 69 70 /* Keypad status. */ 71 72 uint32_t keypad[Jz4730_keypad_gpio_outputs_count]; 73 74 75 76 /* Initialise the pins for scanning the keypad. */ 77 78 static void init_keyscan(void) 79 { 80 jz4730_gpio_multi_setup(gpio_port_a, &Jz4730_keypad_inputs_mask, Fix_input, 0); 81 jz4730_gpio_multi_config_pull(gpio_port_a, &Jz4730_keypad_inputs_mask, Pull_up); 82 jz4730_gpio_multi_setup(gpio_port_d, &Jz4730_keypad_outputs_mask, Fix_input, 0); 83 } 84 85 /* 86 Scan the keypad by enabling each output column and inspecting each input row. 87 Store each column bitmap in the keypad array. 88 */ 89 90 static void scan_keypad(void) 91 { 92 uint8_t column, row; 93 uint32_t value; 94 95 for (column = 0; column < Jz4730_keypad_gpio_outputs_count; column++) 96 { 97 jz4730_gpio_setup(gpio_port_d, Jz4730_keypad_outputs[column], Fix_output, 0); 98 l4_sleep(1); 99 100 value = 0; 101 102 for (row = 0; row < Jz4730_keypad_gpio_inputs_count; row++) 103 value = (value << 1) | (jz4730_gpio_get(gpio_port_a, Jz4730_keypad_inputs[row]) ? 0 : 1); 104 105 keypad[column] = value; 106 107 jz4730_gpio_setup(gpio_port_d, Jz4730_keypad_outputs[column], Fix_input, 0); 108 } 109 } 110 111 static uint32_t bitmask(uint32_t size) 112 { 113 return (1 << size) - 1; 114 } 115 116 static uint32_t truncate_channel(uint32_t value, uint32_t size) 117 { 118 return (value >> (8 - size)) & bitmask(size); 119 } 120 121 static void set_pixel(uint32_t pos, uint8_t bpp, uint32_t value) 122 { 123 if (bpp <= 8) *(uint8_t *) pos = value; 124 else if (bpp <= 16) *(uint16_t *) pos = value; 125 else *(uint32_t *) pos = value; 126 } 127 128 /* Show the state of a key on the display. */ 129 130 static void show_keystate(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t rgb) 131 { 132 uint8_t bpp = l4re_video_bits_per_pixel(&fbi.pixel_info); 133 uint8_t bytes_per_pixel = fbi.pixel_info.bytes_per_pixel; 134 uint32_t bytes_per_line = fbi.bytes_per_line; 135 uint32_t pos; 136 uint32_t col, row; 137 138 rgb = (truncate_channel(rgb >> 16, fbi.pixel_info.r.size) << fbi.pixel_info.r.shift) | 139 (truncate_channel(rgb >> 8, fbi.pixel_info.g.size) << fbi.pixel_info.g.shift) | 140 (truncate_channel(rgb, fbi.pixel_info.b.size) << fbi.pixel_info.b.shift); 141 142 for (row = 0; row < h; row++) 143 { 144 pos = (uint32_t) fb + (y + row) * bytes_per_line + x * bytes_per_pixel; 145 146 for (col = 0; col < w; col++) 147 { 148 set_pixel(pos, bpp, rgb); 149 pos += bytes_per_pixel; 150 } 151 } 152 } 153 154 /* Show the keypad status on the display. */ 155 156 static void show_keypad(void) 157 { 158 uint32_t colsize = fbi.width / Jz4730_keypad_gpio_outputs_count, 159 rowsize = fbi.height / Jz4730_keypad_gpio_inputs_count; 160 uint8_t column, row; 161 uint32_t mask; 162 163 for (column = 0; column < Jz4730_keypad_gpio_outputs_count; column++) 164 165 for (row = 0, mask = 1 << (Jz4730_keypad_gpio_inputs_count - 1); 166 row < Jz4730_keypad_gpio_inputs_count; 167 row++, mask >>= 1) 168 169 show_keystate(column * colsize, row * rowsize, colsize, rowsize, 170 keypad[column] & mask ? 0xffffff : 0); 171 172 /* Refresh the display. */ 173 174 l4re_util_video_goos_fb_refresh(&gfb, 0, 0, fbi.width, fbi.height); 175 } 176 177 int main(void) 178 { 179 int result; 180 181 if ((result = get_memory("jz4730-gpio", &gpio_virt_base, &gpio_virt_base_end)) < 0) 182 return 1; 183 184 if (l4re_util_video_goos_fb_setup_name(&gfb, "fb")) 185 return 1; 186 187 if (l4re_util_video_goos_fb_view_info(&gfb, &fbi)) 188 return 1; 189 190 if (!(fb = l4re_util_video_goos_fb_attach_buffer(&gfb))) 191 return 1; 192 193 gpio_port_a = jz4730_gpio_init(gpio_virt_base, 0); 194 gpio_port_d = jz4730_gpio_init(gpio_virt_base, 3); 195 196 init_keyscan(); 197 198 while (1) 199 { 200 scan_keypad(); 201 show_keypad(); 202 } 203 204 return 0; 205 }