Landfall

Annotated pkg/landfall-examples/qi_lb60_lcd/qi_lb60_lcd.c

229:810ca800993b
11 months ago Paul Boddie Employ a picture representation compatible with 16-bit SPI transfers over DMA. cpm-library-improvements
paul@146 1
/*
paul@146 2
 * Access the LCD peripheral on the Ben NanoNote.
paul@146 3
 *
paul@146 4
 * Copyright (C) 2020, 2023 Paul Boddie <paul@boddie.org.uk>
paul@146 5
 *
paul@146 6
 * This program is free software; you can redistribute it and/or
paul@146 7
 * modify it under the terms of the GNU General Public License as
paul@146 8
 * published by the Free Software Foundation; either version 2 of
paul@146 9
 * the License, or (at your option) any later version.
paul@146 10
 *
paul@146 11
 * This program is distributed in the hope that it will be useful,
paul@146 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@146 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@146 14
 * GNU General Public License for more details.
paul@146 15
 *
paul@146 16
 * You should have received a copy of the GNU General Public License
paul@146 17
 * along with this program; if not, write to the Free Software
paul@146 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@146 19
 * Boston, MA  02110-1301, USA
paul@146 20
 */
paul@146 21
paul@146 22
#include <l4/devices/cpm-jz4740.h>
paul@146 23
paul@146 24
#include <l4/devices/lcd-jz4740.h>
paul@146 25
#include <l4/devices/lcd-jz4740-config.h>
paul@146 26
#include <l4/devices/lcd-jz4740-panel.h>
paul@146 27
#include <l4/devices/panel.h>
paul@146 28
paul@146 29
#include <l4/devices/memory.h>
paul@146 30
paul@146 31
#include <l4/re/c/dataspace.h>
paul@146 32
#include <l4/re/c/dma_space.h>
paul@146 33
#include <l4/re/c/mem_alloc.h>
paul@146 34
#include <l4/re/c/rm.h>
paul@146 35
#include <l4/re/c/util/cap_alloc.h>
paul@146 36
#include <l4/re/env.h>
paul@146 37
#include <l4/re/protocols.h>
paul@146 38
paul@146 39
#include <l4/sys/cache.h>
paul@146 40
#include <l4/sys/err.h>
paul@146 41
#include <l4/sys/factory.h>
paul@146 42
#include <l4/sys/icu.h>
paul@146 43
#include <l4/sys/ipc.h>
paul@146 44
#include <l4/sys/irq.h>
paul@146 45
#include <l4/sys/rcv_endpoint.h>
paul@146 46
paul@146 47
#include <l4/io/io.h>
paul@146 48
#include <l4/vbus/vbus.h>
paul@146 49
paul@146 50
#include <stdio.h>
paul@146 51
#include <unistd.h>
paul@146 52
#include <stdint.h>
paul@146 53
#include <string.h>
paul@146 54
#include <stdlib.h>
paul@146 55
paul@146 56
paul@146 57
paul@146 58
/* Device and resource discovery. */
paul@146 59
paul@146 60
static long item_in_range(long start, long end, long index)
paul@146 61
{
paul@146 62
  if (start < end)
paul@146 63
    return start + index;
paul@146 64
  else
paul@146 65
    return start - index;
paul@146 66
}
paul@146 67
paul@146 68
int main(void)
paul@146 69
{
paul@146 70
  long err;
paul@146 71
  int i;
paul@146 72
paul@146 73
  /* Peripheral memory. */
paul@146 74
paul@146 75
  l4_addr_t cpm_base = 0, cpm_base_end = 0;
paul@146 76
  l4_addr_t lcd_base = 0, lcd_base_end = 0;
paul@146 77
paul@146 78
  /* Peripheral abstractions. */
paul@146 79
paul@146 80
  void *cpm;
paul@146 81
  void *lcd;
paul@146 82
paul@146 83
  /* Allocated memory. */
paul@146 84
paul@146 85
  l4_cap_idx_t desc_mem, fb_mem, dma, vbus;
paul@146 86
  l4_size_t desc_size, desc_psize, fb_size, fb_psize;
paul@146 87
  l4_addr_t desc_addr, fb_addr;
paul@146 88
  l4re_dma_space_dma_addr_t desc_paddr, fb_paddr;
paul@146 89
  unsigned char *picture;
paul@146 90
  unsigned char *fb_picture, *fb_picture_row;
paul@146 91
  unsigned int x, y;
paul@146 92
paul@146 93
  /* Access to IRQs. */
paul@146 94
paul@146 95
  l4_uint32_t lcd_irq_start = 0, lcd_irq_end = 0;
paul@146 96
  l4_cap_idx_t icu, lcd_irq;
paul@146 97
paul@146 98
  /* Capability allocation. */
paul@146 99
paul@146 100
  desc_mem = l4re_util_cap_alloc();
paul@146 101
  fb_mem = l4re_util_cap_alloc();
paul@146 102
  dma = l4re_util_cap_alloc();
paul@146 103
  lcd_irq = l4re_util_cap_alloc();
paul@146 104
  icu = l4re_env_get_cap("icu");
paul@146 105
  vbus = l4re_env_get_cap("vbus");
paul@146 106
paul@146 107
  if (l4_is_invalid_cap(icu))
paul@146 108
  {
paul@146 109
    printf("No 'icu' capability available in the virtual bus.\n");
paul@146 110
    return 1;
paul@146 111
  }
paul@146 112
paul@146 113
  if (l4_is_invalid_cap(desc_mem) || l4_is_invalid_cap(fb_mem) ||
paul@146 114
      l4_is_invalid_cap(lcd_irq))
paul@146 115
  {
paul@146 116
    printf("Capabilities could not be reserved.\n");
paul@146 117
    return 1;
paul@146 118
  }
paul@146 119
paul@146 120
  /* Obtain resource details describing the interrupt for HDMI I2C. */
paul@146 121
paul@146 122
  printf("Access IRQ...\n");
paul@146 123
paul@146 124
  if (get_irq("jz4740-lcd", &lcd_irq_start, &lcd_irq_end) < 0)
paul@146 125
    return 1;
paul@146 126
paul@146 127
  printf("LCD IRQ range at %d...%d.\n", lcd_irq_start, lcd_irq_end);
paul@146 128
paul@146 129
  /* Create interrupt objects. */
paul@146 130
paul@146 131
  err = l4_error(l4_factory_create_irq(l4re_global_env->factory, lcd_irq));
paul@146 132
paul@146 133
  if (err)
paul@146 134
  {
paul@146 135
    printf("Could not create IRQ object: %lx\n", err);
paul@146 136
    return 1;
paul@146 137
  }
paul@146 138
paul@146 139
  /* Bind interrupt objects to IRQ numbers. */
paul@146 140
paul@146 141
  err = l4_error(l4_icu_bind(icu,
paul@146 142
                             item_in_range(lcd_irq_start, lcd_irq_end, 0),
paul@146 143
                             lcd_irq));
paul@146 144
paul@146 145
  if (err)
paul@146 146
  {
paul@146 147
    printf("Could not bind IRQ to the ICU: %ld\n", err);
paul@146 148
    return 1;
paul@146 149
  }
paul@146 150
paul@146 151
  /* Attach ourselves to the interrupt handler with some arbitrary labels. */
paul@146 152
paul@146 153
  err = l4_error(l4_rcv_ep_bind_thread(lcd_irq, l4re_env()->main_thread, 0x10));
paul@146 154
paul@146 155
  if (err)
paul@146 156
  {
paul@146 157
    printf("Could not attach to IRQs: %ld\n", err);
paul@146 158
    return 1;
paul@146 159
  }
paul@146 160
paul@146 161
  /* Obtain resource details describing I/O memory. */
paul@146 162
paul@146 163
  printf("Access CPM...\n");
paul@146 164
paul@146 165
  if (get_memory("jz4740-cpm", &cpm_base, &cpm_base_end) < 0)
paul@146 166
    return 1;
paul@146 167
paul@146 168
  printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end);
paul@146 169
paul@146 170
  printf("Access LCD...\n");
paul@146 171
paul@146 172
  if (get_memory("jz4740-lcd", &lcd_base, &lcd_base_end) < 0)
paul@146 173
    return 1;
paul@146 174
paul@146 175
  printf("LCD at 0x%lx...0x%lx.\n", lcd_base, lcd_base_end);
paul@146 176
paul@146 177
  /* Obtain CPM object. */
paul@146 178
paul@146 179
  cpm = jz4740_cpm_init(cpm_base);
paul@146 180
paul@146 181
  /* Obtain LCD reference. */
paul@146 182
paul@146 183
  printf("Set up LCD...\n");
paul@146 184
paul@146 185
  lcd = jz4740_lcd_init(lcd_base, panel_get());
paul@146 186
paul@146 187
  /* Test initialisation with a frequency appropriate for the test panel. */
paul@146 188
paul@151 189
  jz4740_cpm_stop_clock(cpm, Clock_lcd);
paul@160 190
  jz4740_cpm_set_frequency(cpm, Clock_lcd, jz4740_lcd_get_pixel_clock(lcd) * 3);
paul@211 191
  jz4740_cpm_set_frequency(cpm, Clock_lcd_pixel0, jz4740_lcd_get_pixel_clock(lcd));
paul@146 192
paul@146 193
  /* Create the DMA space. */
paul@146 194
paul@146 195
  err = l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma));
paul@146 196
paul@146 197
  if (err)
paul@146 198
  {
paul@146 199
    printf("Could not create DMA space: %s\n", l4sys_errtostr(err));
paul@146 200
    return 1;
paul@146 201
  }
paul@146 202
paul@146 203
  l4vbus_device_handle_t device = L4VBUS_NULL;
paul@146 204
  l4vbus_resource_t dma_resource;
paul@146 205
paul@146 206
  if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN))
paul@146 207
  {
paul@146 208
    printf("Could not find DMA domain.\n");
paul@146 209
    return 1;
paul@146 210
  }
paul@146 211
paul@146 212
  err = l4vbus_assign_dma_domain(vbus, dma_resource.start,
paul@146 213
                                 L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE,
paul@146 214
                                 dma);
paul@146 215
paul@146 216
  if (err)
paul@146 217
  {
paul@146 218
    printf("Could not assign DMA space: %s\n", l4sys_errtostr(err));
paul@146 219
    return 1;
paul@146 220
  }
paul@146 221
paul@146 222
  /* Allocate descriptors and framebuffer at 2**8 == 256 byte == 64 word alignment. */
paul@146 223
paul@146 224
  desc_size = jz4740_lcd_get_descriptors_size(lcd);
paul@146 225
  fb_size = jz4740_lcd_get_screen_size(lcd);
paul@146 226
paul@146 227
  err = l4re_ma_alloc_align(desc_size, desc_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8) ||
paul@146 228
        l4re_ma_alloc_align(fb_size, fb_mem, L4RE_MA_CONTINUOUS | L4RE_MA_PINNED, 8);
paul@146 229
paul@146 230
  if (err)
paul@146 231
  {
paul@146 232
    printf("Could not allocate memory: %s\n", l4sys_errtostr(err));
paul@146 233
    return 1;
paul@146 234
  }
paul@146 235
paul@146 236
  err = l4re_rm_attach((void **) &desc_addr, desc_size,
paul@146 237
                       L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_EAGER_MAP | L4RE_RM_F_RW,
paul@146 238
                       desc_mem, 0, L4_PAGESHIFT) ||
paul@146 239
        l4re_rm_attach((void **) &fb_addr, fb_size,
paul@146 240
                       L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_EAGER_MAP | L4RE_RM_F_RW,
paul@146 241
                       fb_mem, 0, L4_PAGESHIFT);
paul@146 242
paul@146 243
  if (err)
paul@146 244
  {
paul@146 245
    printf("Could not map memory: %s\n", l4sys_errtostr(err));
paul@146 246
    return 1;
paul@146 247
  }
paul@146 248
paul@146 249
  err = l4re_dma_space_map(dma, desc_mem | L4_CAP_FPAGE_RW, 0, &desc_psize, 0,
paul@146 250
                           L4RE_DMA_SPACE_TO_DEVICE, &desc_paddr) ||
paul@146 251
        l4re_dma_space_map(dma, fb_mem | L4_CAP_FPAGE_RW, 0, &fb_psize, 0,
paul@146 252
                           L4RE_DMA_SPACE_TO_DEVICE, &fb_paddr);
paul@146 253
paul@146 254
  if (err)
paul@146 255
  {
paul@146 256
    printf("Could not get physical addresses for memory: %s\n", l4sys_errtostr(err));
paul@146 257
    return 1;
paul@146 258
  }
paul@146 259
paul@146 260
  printf("Descriptors at %lx/%llx, size %d/%d.\n", desc_addr, desc_paddr, desc_size, desc_psize);
paul@146 261
  printf("Framebuffer at %lx/%llx, size %d/%d.\n", fb_addr, fb_paddr, fb_size, fb_psize);
paul@146 262
paul@146 263
  //memset((void *) fb_addr, 0x7f, fb_size);
paul@146 264
paul@146 265
  picture = (unsigned char *) malloc(gimp_image.width * gimp_image.height * gimp_image.bytes_per_pixel);
paul@146 266
paul@146 267
  GIMP_IMAGE_RUN_LENGTH_DECODE(picture,
paul@146 268
                               gimp_image.rle_pixel_data,
paul@146 269
                               gimp_image.width * gimp_image.height,
paul@146 270
                               gimp_image.bytes_per_pixel);
paul@146 271
paul@146 272
  fb_picture_row = (unsigned char *) fb_addr;
paul@146 273
paul@146 274
  for (y = 0; y < gimp_image.height; y++)
paul@146 275
  {
paul@146 276
    fb_picture = fb_picture_row;
paul@146 277
paul@146 278
    for (x = 0; x < gimp_image.width; x++)
paul@146 279
    {
paul@146 280
      *(fb_picture + 2) = *picture++;
paul@146 281
      *(fb_picture + 1) = *picture++;
paul@146 282
      *fb_picture = *picture++;
paul@146 283
      *(fb_picture + 3) = 0;
paul@146 284
      fb_picture += 4;
paul@146 285
    }
paul@146 286
paul@146 287
    fb_picture_row += jz4740_lcd_get_line_size(lcd);
paul@146 288
  }
paul@146 289
paul@146 290
  l4_cache_clean_data((unsigned long) fb_addr, (unsigned long) fb_addr + fb_size);
paul@146 291
paul@146 292
  printf("Start LCD clock and initialise LCD...\n");
paul@146 293
paul@151 294
  jz4740_cpm_start_clock(cpm, Clock_lcd);
paul@146 295
  l4_sleep(1); // 1ms == 1000us
paul@146 296
paul@146 297
  jz4740_lcd_disable(lcd);
paul@146 298
paul@146 299
  jz4740_lcd_set_irq(lcd, lcd_irq, Lcd_irq_frame_end);
paul@146 300
paul@146 301
  jz4740_lcd_config(lcd, (struct Jz4740_lcd_descriptor *) desc_addr,
paul@160 302
                         (struct Jz4740_lcd_descriptor *) (l4_addr_t) desc_paddr,
paul@146 303
                         fb_paddr);
paul@146 304
paul@146 305
  jz4740_lcd_enable(lcd);
paul@146 306
paul@146 307
  printf("LCD enabled: %s\n", jz4740_lcd_enabled(lcd) ? "yes" : "no");
paul@146 308
paul@146 309
  printf("Wait for interrupt conditions...\n");
paul@146 310
paul@146 311
  for (i = 0; i < 30000; i++)
paul@146 312
  {
paul@146 313
    if (jz4740_lcd_wait_for_irq(lcd))
paul@146 314
      continue;
paul@146 315
paul@146 316
    if (!(i % 60))
paul@146 317
      printf("IRQ #%d\n", i);
paul@146 318
  }
paul@146 319
paul@146 320
  /* Detach from the interrupts. */
paul@146 321
paul@146 322
  err = l4_error(l4_irq_detach(lcd_irq));
paul@146 323
paul@146 324
  if (err)
paul@146 325
    printf("Error detaching from IRQ: %ld\n", err);
paul@146 326
paul@146 327
  printf("Done.\n");
paul@146 328
paul@146 329
  return 0;
paul@146 330
}