1 /* 2 * Access the LCD and related GPIOs on the Ben NanoNote. 3 * 4 * (c) 2017, 2018 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/cpm-jz4740.h> 23 #include <l4/devices/gpio-jz4740.h> 24 #include <l4/io/io.h> 25 #include <l4/re/env.h> 26 #include <l4/re/c/mem_alloc.h> 27 #include <l4/re/c/rm.h> 28 #include <l4/re/c/util/cap_alloc.h> 29 #include <l4/sys/cache.h> 30 #include <l4/util/util.h> 31 #include <l4/vbus/vbus.h> 32 33 #include <stdio.h> 34 #include <stdint.h> 35 36 #include "nanonote_gpm940b0.h" 37 #include "jzlcd.h" 38 #include "xburst_types.h" 39 #include "memory.h" 40 41 enum Jz4740_lcd_gpio 42 { 43 Jz4740_lcd_gpio_spen = 21, /* serial command enable */ 44 Jz4740_lcd_gpio_spda = 22, /* serial command clock */ 45 Jz4740_lcd_gpio_spck = 23, /* serial command data */ 46 }; 47 48 /* Peripheral memory regions. */ 49 50 static l4_addr_t cpm_virt_base = 0, cpm_virt_base_end = 0; 51 static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0; 52 static l4_addr_t lcd_virt_base = 0, lcd_virt_base_end = 0; 53 54 /* Device abstractions. */ 55 56 static void *gpio_port_c; 57 static void *cpm_device = 0; 58 static vidinfo_t *panel_info = 0; 59 60 /* Framebuffer and descriptor regions. */ 61 62 static void *fb_vaddr = 0, *desc_vaddr = 0; 63 static l4_addr_t fb_paddr = 0, desc_paddr = 0; 64 static l4_size_t fb_size, desc_size; 65 66 67 68 // Write SPI values via the LCD GPIO pins. 69 70 static void spi_write_reg(uint8_t reg, uint8_t val) 71 { 72 uint8_t no; 73 uint16_t value; 74 75 jz4740_gpio_set(gpio_port_c, Jz4740_lcd_gpio_spen, 1); 76 jz4740_gpio_set(gpio_port_c, Jz4740_lcd_gpio_spck, 1); 77 jz4740_gpio_set(gpio_port_c, Jz4740_lcd_gpio_spda, 0); 78 jz4740_gpio_set(gpio_port_c, Jz4740_lcd_gpio_spen, 0); 79 80 value = ((reg << 8) | (val & 0xFF)); 81 82 /* Clock data using the clock and data outputs. */ 83 84 for (no = 0; no < 16; no++) 85 { 86 jz4740_gpio_set(gpio_port_c, Jz4740_lcd_gpio_spck, 0); 87 jz4740_gpio_set(gpio_port_c, Jz4740_lcd_gpio_spda, value & 0x8000 ? 1 : 0); 88 jz4740_gpio_set(gpio_port_c, Jz4740_lcd_gpio_spck, 1); 89 value = (value << 1); 90 } 91 92 jz4740_gpio_set(gpio_port_c, Jz4740_lcd_gpio_spen, 1); 93 } 94 95 // GPIO-based operations. 96 97 static void lcd_display_pin_init(void) 98 { 99 Pin_slice mask = {.offset=0, .mask=(1 << Jz4740_lcd_gpio_spen) | (1 << Jz4740_lcd_gpio_spck) | (1 << Jz4740_lcd_gpio_spda)}; 100 Pin_slice slcd8_mask = {.offset=0, .mask=0x003c00ff}; 101 102 /* Configure SPI pins. */ 103 104 jz4740_gpio_multi_setup(gpio_port_c, &mask, L4VBUS_GPIO_SETUP_OUTPUT, 0); 105 106 /* Configure SLCD8 pins. */ 107 108 jz4740_gpio_multi_config_pad(gpio_port_c, &slcd8_mask, Function_alt, 0); 109 } 110 111 static void lcd_display_on(void) 112 { 113 spi_write_reg(0x05, 0x1e); // GRB=0 (reset); PWM_DUTY=0b011 (70%, default); SHDB2=1, SHDB1=1 (power-related); STB=0 (standby) 114 spi_write_reg(0x05, 0x5e); // GRB=1 (normal operation); ... 115 spi_write_reg(0x07, 0x8d); // HBLK=141 (horizontal blanking period from start of hsync pulse to data start) 116 spi_write_reg(0x13, 0x01); // IN_SEL=1 (alignment mode) 117 spi_write_reg(0x05, 0x5f); // ...; STB=1 (not standby) 118 } 119 120 /* CPM operations. */ 121 122 static void lcd_set_timing(vidinfo_t *vid) 123 { 124 uint32_t pclk = jz4740_lcd_get_pixel_clock(vid); 125 126 jz4740_cpm_stop_lcd(cpm_device); 127 128 /* 129 Original comment: LCDClock > 2.5*Pixclock 130 However, the documentation indicates that a TFT panel needs a device clock 131 1.5 times that of the pixel clock, and a STN panel needs a device clock 3 132 times that of the pixel clock. 133 */ 134 135 jz4740_cpm_set_lcd_frequencies(cpm_device, pclk, 3); 136 jz4740_cpm_update_output_frequency(cpm_device); 137 jz4740_cpm_start_lcd(cpm_device); 138 139 l4_sleep(1); // 1ms == 1000us 140 } 141 142 143 144 static int setup_memory(void) 145 { 146 l4re_ds_t fbmem, descmem; 147 l4_size_t fb_size_out, desc_size_out; 148 int result = 0; 149 150 if (fb_vaddr) 151 return 0; 152 153 if (!panel_info) 154 return 1; 155 156 fb_size = jz4740_lcd_get_screen_size(panel_info); 157 desc_size = jz4740_lcd_get_descriptors_size(panel_info); 158 159 /* Obtain resource details describing the I/O memory. */ 160 161 if ((result = get_memory("jz4740-cpm", &cpm_virt_base, &cpm_virt_base_end)) < 0) 162 return 1; 163 164 if ((result = get_memory("jz4740-lcd", &lcd_virt_base, &lcd_virt_base_end)) < 0) 165 return 1; 166 167 if ((result = get_memory("jz4740-gpio", &gpio_virt_base, &gpio_virt_base_end)) < 0) 168 return 1; 169 170 /* Set the framebuffer up. */ 171 172 fbmem = l4re_util_cap_alloc(); 173 descmem = l4re_util_cap_alloc(); 174 175 if (l4_is_invalid_cap(fbmem) || l4_is_invalid_cap(descmem)) 176 return 1; 177 178 /* Allocate memory for the framebuffer at 2**6 == 64 byte == 16 word alignment, 179 also for the descriptors. */ 180 181 if (l4re_ma_alloc_align(fb_size, fbmem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 6) || 182 l4re_ma_alloc_align(desc_size, descmem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 6)) 183 return 1; 184 185 /* Map the allocated memory, obtaining virtual addresses. */ 186 187 if (l4re_rm_attach(&fb_vaddr, fb_size, 188 L4RE_RM_SEARCH_ADDR | L4RE_RM_EAGER_MAP, 189 fbmem, 0, L4_PAGESHIFT) || 190 l4re_rm_attach(&desc_vaddr, desc_size, 191 L4RE_RM_SEARCH_ADDR | L4RE_RM_EAGER_MAP, 192 descmem, 0, L4_PAGESHIFT)) 193 return 1; 194 195 fb_size_out = fb_size; 196 desc_size_out = desc_size; 197 198 if (l4re_ds_phys(fbmem, 0, &fb_paddr, &fb_size_out) || 199 l4re_ds_phys(descmem, 0, &desc_paddr, &desc_size_out)) 200 return 1; 201 202 if ((fb_size_out != fb_size) || (desc_size_out != desc_size)) 203 return 1; 204 205 cpm_device = jz4740_cpm_init(cpm_virt_base); 206 gpio_port_c = jz4740_gpio_init(gpio_virt_base + 0x200, gpio_virt_base + 0x300, 32); 207 208 return 0; 209 } 210 211 static void enable(void) 212 { 213 if (setup_memory()) 214 return; 215 216 jz4740_lcd_set_base(panel_info, (void *) lcd_virt_base); 217 jz4740_lcd_disable(panel_info); 218 219 // Initialise the LCD controller and structures. 220 221 jz4740_lcd_ctrl_init( 222 (struct jz_fb_dma_descriptor *) desc_vaddr, 223 (struct jz_fb_dma_descriptor *) desc_paddr, 224 fb_vaddr, 225 (void *) fb_paddr, 226 panel_info); 227 228 // Initialise the LCD peripheral. 229 230 jz4740_lcd_hw_init(panel_info); 231 232 // Initialise the clocks for the LCD controller. 233 234 lcd_set_timing(panel_info); 235 236 // Switch the display on using GPIO operations. 237 238 lcd_display_pin_init(); 239 lcd_display_on(); 240 241 // Finally, enable the peripheral. 242 243 jz4740_lcd_enable(panel_info); 244 } 245 246 247 248 int main(void) 249 { 250 l4_size_t *fb; 251 l4_size_t i, mask, value, onpix, offpix; 252 253 /* Configure the LCD. */ 254 255 panel_info = &nanonote_panel_info; 256 enable(); 257 258 fb = (l4_size_t *) fb_vaddr; 259 260 /* Try and show some values. */ 261 262 onpix = 0xffaaffaa; offpix = 0x11551155; 263 mask = 0x80000000; value = 0x80000001; 264 265 i = 0; 266 267 while (i < fb_size / 4) 268 { 269 fb[i] = value & mask ? onpix : offpix; 270 i++; 271 272 if ((i % 10) == 0) 273 { 274 if (mask == 1) mask = 0x80000000; 275 else mask >>= 1; 276 277 onpix = (onpix >> 8) | ((onpix & 0xff) << 24); 278 offpix = (offpix >> 8) | ((offpix & 0xff) << 24); 279 280 if (i == 3200) value = jz4740_cpm_get_lcd_pixel_divider(cpm_device); 281 else if (i == 6400) value = jz4740_cpm_get_lcd_pixel_frequency(cpm_device); 282 else if (i == 9600) value = (l4_size_t) fb_vaddr; 283 else if (i == 12800) value = fb_paddr; 284 else if (i == 16000) value = ((struct jz_fb_dma_descriptor *) desc_vaddr)[0].fsadr; 285 else if (i == 19200) value = REG32(lcd_virt_base + 0x00); 286 else if (i == 22400) value = REG32(lcd_virt_base + 0x04); 287 else if (i == 25600) value = REG32(lcd_virt_base + 0x30); 288 else if (i == 28800) value = REG32(lcd_virt_base + 0x40); 289 } 290 } 291 292 l4_cache_clean_data((long unsigned int) fb_vaddr, (long unsigned int) fb_vaddr + fb_size); 293 294 while (1); 295 296 return 0; 297 }