1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/landfall-examples/qi_lb60_lcd/qi_lb60_lcd.c Mon Jun 05 14:46:47 2023 +0200
1.3 @@ -0,0 +1,330 @@
1.4 +/*
1.5 + * Access the LCD peripheral on the Ben NanoNote.
1.6 + *
1.7 + * Copyright (C) 2020, 2023 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <l4/devices/cpm-jz4740.h>
1.26 +
1.27 +#include <l4/devices/lcd-jz4740.h>
1.28 +#include <l4/devices/lcd-jz4740-config.h>
1.29 +#include <l4/devices/lcd-jz4740-panel.h>
1.30 +#include <l4/devices/panel.h>
1.31 +
1.32 +#include <l4/devices/memory.h>
1.33 +
1.34 +#include <l4/re/c/dataspace.h>
1.35 +#include <l4/re/c/dma_space.h>
1.36 +#include <l4/re/c/mem_alloc.h>
1.37 +#include <l4/re/c/rm.h>
1.38 +#include <l4/re/c/util/cap_alloc.h>
1.39 +#include <l4/re/env.h>
1.40 +#include <l4/re/protocols.h>
1.41 +
1.42 +#include <l4/sys/cache.h>
1.43 +#include <l4/sys/err.h>
1.44 +#include <l4/sys/factory.h>
1.45 +#include <l4/sys/icu.h>
1.46 +#include <l4/sys/ipc.h>
1.47 +#include <l4/sys/irq.h>
1.48 +#include <l4/sys/rcv_endpoint.h>
1.49 +
1.50 +#include <l4/io/io.h>
1.51 +#include <l4/vbus/vbus.h>
1.52 +
1.53 +#include <stdio.h>
1.54 +#include <unistd.h>
1.55 +#include <stdint.h>
1.56 +#include <string.h>
1.57 +#include <stdlib.h>
1.58 +
1.59 +
1.60 +
1.61 +/* Device and resource discovery. */
1.62 +
1.63 +static long item_in_range(long start, long end, long index)
1.64 +{
1.65 + if (start < end)
1.66 + return start + index;
1.67 + else
1.68 + return start - index;
1.69 +}
1.70 +
1.71 +int main(void)
1.72 +{
1.73 + long err;
1.74 + int i;
1.75 +
1.76 + /* Peripheral memory. */
1.77 +
1.78 + l4_addr_t cpm_base = 0, cpm_base_end = 0;
1.79 + l4_addr_t lcd_base = 0, lcd_base_end = 0;
1.80 +
1.81 + /* Peripheral abstractions. */
1.82 +
1.83 + void *cpm;
1.84 + void *lcd;
1.85 +
1.86 + /* Allocated memory. */
1.87 +
1.88 + l4_cap_idx_t desc_mem, fb_mem, dma, vbus;
1.89 + l4_size_t desc_size, desc_psize, fb_size, fb_psize;
1.90 + l4_addr_t desc_addr, fb_addr;
1.91 + l4re_dma_space_dma_addr_t desc_paddr, fb_paddr;
1.92 + unsigned char *picture;
1.93 + unsigned char *fb_picture, *fb_picture_row;
1.94 + unsigned int x, y;
1.95 +
1.96 + /* Access to IRQs. */
1.97 +
1.98 + l4_uint32_t lcd_irq_start = 0, lcd_irq_end = 0;
1.99 + l4_cap_idx_t icu, lcd_irq;
1.100 +
1.101 + /* Capability allocation. */
1.102 +
1.103 + desc_mem = l4re_util_cap_alloc();
1.104 + fb_mem = l4re_util_cap_alloc();
1.105 + dma = l4re_util_cap_alloc();
1.106 + lcd_irq = l4re_util_cap_alloc();
1.107 + icu = l4re_env_get_cap("icu");
1.108 + vbus = l4re_env_get_cap("vbus");
1.109 +
1.110 + if (l4_is_invalid_cap(icu))
1.111 + {
1.112 + printf("No 'icu' capability available in the virtual bus.\n");
1.113 + return 1;
1.114 + }
1.115 +
1.116 + if (l4_is_invalid_cap(desc_mem) || l4_is_invalid_cap(fb_mem) ||
1.117 + l4_is_invalid_cap(lcd_irq))
1.118 + {
1.119 + printf("Capabilities could not be reserved.\n");
1.120 + return 1;
1.121 + }
1.122 +
1.123 + /* Obtain resource details describing the interrupt for HDMI I2C. */
1.124 +
1.125 + printf("Access IRQ...\n");
1.126 +
1.127 + if (get_irq("jz4740-lcd", &lcd_irq_start, &lcd_irq_end) < 0)
1.128 + return 1;
1.129 +
1.130 + printf("LCD IRQ range at %d...%d.\n", lcd_irq_start, lcd_irq_end);
1.131 +
1.132 + /* Create interrupt objects. */
1.133 +
1.134 + err = l4_error(l4_factory_create_irq(l4re_global_env->factory, lcd_irq));
1.135 +
1.136 + if (err)
1.137 + {
1.138 + printf("Could not create IRQ object: %lx\n", err);
1.139 + return 1;
1.140 + }
1.141 +
1.142 + /* Bind interrupt objects to IRQ numbers. */
1.143 +
1.144 + err = l4_error(l4_icu_bind(icu,
1.145 + item_in_range(lcd_irq_start, lcd_irq_end, 0),
1.146 + lcd_irq));
1.147 +
1.148 + if (err)
1.149 + {
1.150 + printf("Could not bind IRQ to the ICU: %ld\n", err);
1.151 + return 1;
1.152 + }
1.153 +
1.154 + /* Attach ourselves to the interrupt handler with some arbitrary labels. */
1.155 +
1.156 + err = l4_error(l4_rcv_ep_bind_thread(lcd_irq, l4re_env()->main_thread, 0x10));
1.157 +
1.158 + if (err)
1.159 + {
1.160 + printf("Could not attach to IRQs: %ld\n", err);
1.161 + return 1;
1.162 + }
1.163 +
1.164 + /* Obtain resource details describing I/O memory. */
1.165 +
1.166 + printf("Access CPM...\n");
1.167 +
1.168 + if (get_memory("jz4740-cpm", &cpm_base, &cpm_base_end) < 0)
1.169 + return 1;
1.170 +
1.171 + printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end);
1.172 +
1.173 + printf("Access LCD...\n");
1.174 +
1.175 + if (get_memory("jz4740-lcd", &lcd_base, &lcd_base_end) < 0)
1.176 + return 1;
1.177 +
1.178 + printf("LCD at 0x%lx...0x%lx.\n", lcd_base, lcd_base_end);
1.179 +
1.180 + /* Obtain CPM object. */
1.181 +
1.182 + cpm = jz4740_cpm_init(cpm_base);
1.183 +
1.184 + /* Obtain LCD reference. */
1.185 +
1.186 + printf("Set up LCD...\n");
1.187 +
1.188 + lcd = jz4740_lcd_init(lcd_base, panel_get());
1.189 +
1.190 + /* Test initialisation with a frequency appropriate for the test panel. */
1.191 +
1.192 + jz4740_cpm_stop_lcd(cpm);
1.193 + jz4740_cpm_set_lcd_frequencies(cpm, jz4740_lcd_get_pixel_clock(lcd), 3);
1.194 + jz4740_cpm_update_output_frequency(cpm);
1.195 +
1.196 + /* Create the DMA space. */
1.197 +
1.198 + err = l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma));
1.199 +
1.200 + if (err)
1.201 + {
1.202 + printf("Could not create DMA space: %s\n", l4sys_errtostr(err));
1.203 + return 1;
1.204 + }
1.205 +
1.206 + l4vbus_device_handle_t device = L4VBUS_NULL;
1.207 + l4vbus_resource_t dma_resource;
1.208 +
1.209 + if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN))
1.210 + {
1.211 + printf("Could not find DMA domain.\n");
1.212 + return 1;
1.213 + }
1.214 +
1.215 + err = l4vbus_assign_dma_domain(vbus, dma_resource.start,
1.216 + L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE,
1.217 + dma);
1.218 +
1.219 + if (err)
1.220 + {
1.221 + printf("Could not assign DMA space: %s\n", l4sys_errtostr(err));
1.222 + return 1;
1.223 + }
1.224 +
1.225 + /* Allocate descriptors and framebuffer at 2**8 == 256 byte == 64 word alignment. */
1.226 +
1.227 + desc_size = jz4740_lcd_get_descriptors_size(lcd);
1.228 + fb_size = jz4740_lcd_get_screen_size(lcd);
1.229 +
1.230 + err = l4re_ma_alloc_align(desc_size, desc_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8) ||
1.231 + l4re_ma_alloc_align(fb_size, fb_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8);
1.232 +
1.233 + if (err)
1.234 + {
1.235 + printf("Could not allocate memory: %s\n", l4sys_errtostr(err));
1.236 + return 1;
1.237 + }
1.238 +
1.239 + err = l4re_rm_attach((void **) &desc_addr, desc_size,
1.240 + L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_EAGER_MAP | L4RE_RM_F_RW,
1.241 + desc_mem, 0, L4_PAGESHIFT) ||
1.242 + l4re_rm_attach((void **) &fb_addr, fb_size,
1.243 + L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_EAGER_MAP | L4RE_RM_F_RW,
1.244 + fb_mem, 0, L4_PAGESHIFT);
1.245 +
1.246 + if (err)
1.247 + {
1.248 + printf("Could not map memory: %s\n", l4sys_errtostr(err));
1.249 + return 1;
1.250 + }
1.251 +
1.252 + err = l4re_dma_space_map(dma, desc_mem | L4_CAP_FPAGE_RW, 0, &desc_psize, 0,
1.253 + L4RE_DMA_SPACE_TO_DEVICE, &desc_paddr) ||
1.254 + l4re_dma_space_map(dma, fb_mem | L4_CAP_FPAGE_RW, 0, &fb_psize, 0,
1.255 + L4RE_DMA_SPACE_TO_DEVICE, &fb_paddr);
1.256 +
1.257 + if (err)
1.258 + {
1.259 + printf("Could not get physical addresses for memory: %s\n", l4sys_errtostr(err));
1.260 + return 1;
1.261 + }
1.262 +
1.263 + printf("Descriptors at %lx/%llx, size %d/%d.\n", desc_addr, desc_paddr, desc_size, desc_psize);
1.264 + printf("Framebuffer at %lx/%llx, size %d/%d.\n", fb_addr, fb_paddr, fb_size, fb_psize);
1.265 +
1.266 + //memset((void *) fb_addr, 0x7f, fb_size);
1.267 +
1.268 + picture = (unsigned char *) malloc(gimp_image.width * gimp_image.height * gimp_image.bytes_per_pixel);
1.269 +
1.270 + GIMP_IMAGE_RUN_LENGTH_DECODE(picture,
1.271 + gimp_image.rle_pixel_data,
1.272 + gimp_image.width * gimp_image.height,
1.273 + gimp_image.bytes_per_pixel);
1.274 +
1.275 + fb_picture_row = (unsigned char *) fb_addr;
1.276 +
1.277 + for (y = 0; y < gimp_image.height; y++)
1.278 + {
1.279 + fb_picture = fb_picture_row;
1.280 +
1.281 + for (x = 0; x < gimp_image.width; x++)
1.282 + {
1.283 + *(fb_picture + 2) = *picture++;
1.284 + *(fb_picture + 1) = *picture++;
1.285 + *fb_picture = *picture++;
1.286 + *(fb_picture + 3) = 0;
1.287 + fb_picture += 4;
1.288 + }
1.289 +
1.290 + fb_picture_row += jz4740_lcd_get_line_size(lcd);
1.291 + }
1.292 +
1.293 + l4_cache_clean_data((unsigned long) fb_addr, (unsigned long) fb_addr + fb_size);
1.294 +
1.295 + printf("Start LCD clock and initialise LCD...\n");
1.296 +
1.297 + jz4740_cpm_start_lcd(cpm);
1.298 + l4_sleep(1); // 1ms == 1000us
1.299 +
1.300 + jz4740_lcd_disable(lcd);
1.301 +
1.302 + jz4740_lcd_set_irq(lcd, lcd_irq, Lcd_irq_frame_end);
1.303 +
1.304 + jz4740_lcd_config(lcd, (struct Jz4740_lcd_descriptor *) desc_addr,
1.305 + (struct Jz4740_lcd_descriptor *) desc_paddr,
1.306 + fb_paddr);
1.307 +
1.308 + jz4740_lcd_enable(lcd);
1.309 +
1.310 + printf("LCD enabled: %s\n", jz4740_lcd_enabled(lcd) ? "yes" : "no");
1.311 +
1.312 + printf("Wait for interrupt conditions...\n");
1.313 +
1.314 + for (i = 0; i < 30000; i++)
1.315 + {
1.316 + if (jz4740_lcd_wait_for_irq(lcd))
1.317 + continue;
1.318 +
1.319 + if (!(i % 60))
1.320 + printf("IRQ #%d\n", i);
1.321 + }
1.322 +
1.323 + /* Detach from the interrupts. */
1.324 +
1.325 + err = l4_error(l4_irq_detach(lcd_irq));
1.326 +
1.327 + if (err)
1.328 + printf("Error detaching from IRQ: %ld\n", err);
1.329 +
1.330 + printf("Done.\n");
1.331 +
1.332 + return 0;
1.333 +}