1/* 2 * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6/* The code in this file was partially extracted from David Pollack's summer 7 * project, ssh://hg//data/hg_root/summer_students/davidp/sel4vga. 8 */ 9 10#include <assert.h> 11#include <string.h> 12#include <stdlib.h> 13#include <stdint.h> 14#include <bga/bga.h> 15 16struct bga { 17 void *framebuffer; 18 19 /* IO port functions. */ 20 uint16_t (*read)(uint16_t port); 21 void (*write)(uint16_t port, uint16_t value); 22 23 /* Current configuration. */ 24 unsigned int width; 25 unsigned int height; 26 unsigned int bpp; 27}; 28 29/* The BGA device is controlled by operating on two IO ports, first the index 30 * port to indicate what register you are trying to read/write and then the 31 * data port to read or write the actual register. 32 */ 33static const uint16_t INDEX = 0x1ce; 34static const uint16_t DATA = 0x1cf; 35static void write_data(bga_p device, uint16_t index, uint16_t data) 36{ 37 assert(device != NULL); 38 device->write(INDEX, index); 39 device->write(DATA, data); 40} 41static uint16_t read_data(bga_p device, uint16_t index) 42{ 43 assert(device != NULL); 44 device->write(INDEX, index); 45 return device->read(DATA); 46} 47 48/* After writing INDEX_ENABLED to the index port, you can write either 0x0000 49 * or 0x0001 to the data port to disable or enable the device respectively. 50 */ 51static const uint16_t INDEX_ENABLED = 0x0004; 52static void disable(bga_p device) 53{ 54 const uint16_t data_disable = 0x0000; 55 write_data(device, INDEX_ENABLED, data_disable); 56} 57static void enable(bga_p device) 58{ 59 /* Note that we unconditionally use a linear frame buffer and clear the 60 * screen. Not ideal, but the emphasis is on simplicity in this driver. 61 */ 62 const uint16_t data_enable = 0x0001; 63 const uint16_t lfb = 0x0040; 64 write_data(device, INDEX_ENABLED, data_enable | lfb); 65} 66 67uint16_t bga_version(bga_p device) 68{ 69 assert(device != NULL); 70 const uint16_t index_id = 0x0000; /* Index to read version information. */ 71 const uint16_t mask = (uint16_t)~0xb0c0; 72 uint16_t result = read_data(device, index_id); 73 return result & mask; 74} 75 76bga_p bga_init(void *framebuffer, 77 void (*ioport_write)(uint16_t port, uint16_t value), 78 uint16_t (*ioport_read)(uint16_t port)) 79{ 80 bga_p device = (bga_p)malloc(sizeof(struct bga)); 81 if (device == NULL) { 82 return device; 83 } 84 85 memset(device, 0, sizeof(*device)); 86 device->framebuffer = framebuffer; 87 device->write = ioport_write; 88 device->read = ioport_read; 89 90 return device; 91} 92 93int bga_destroy(bga_p device) 94{ 95 free(device); 96 return 0; 97} 98 99int bga_set_mode(bga_p device, unsigned int width, unsigned int height, unsigned int bpp) 100{ 101 /* We need to disable the device to change these parameters. */ 102 disable(device); 103 104 /* Relevant indicies to write to. */ 105 const uint16_t x_resolution = 0x0001; 106 const uint16_t y_resolution = 0x0002; 107 const uint16_t bits_per_pixel = 0x0003; 108 109 /* Setup the requested settings. */ 110 write_data(device, x_resolution, width); 111 device->width = width; 112 write_data(device, y_resolution, height); 113 device->height = height; 114 write_data(device, bits_per_pixel, bpp); 115 device->bpp = bpp; 116 117 /* Finally re-enable the device to have the settings take effect. */ 118 enable(device); 119 120 return 0; 121} 122 123int bga_set_pixel(bga_p device, unsigned int x, unsigned int y, char *value) 124{ 125 char *target; 126 unsigned int coord_factor; 127 size_t len; 128 129 /* XXX: None of these other than 24-bit have been tested and they are most 130 * likely incorrect. 131 */ 132 133 switch (device->bpp) { 134 case 8: { 135 coord_factor = 1; 136 len = 1; 137 break; 138 } 139 case 15: 140 case 16: { 141 coord_factor = 2; 142 len = 2; 143 break; 144 } 145 case 24: { 146 coord_factor = 3; 147 len = 3; 148 break; 149 } 150 case 32: { 151 coord_factor = 4; 152 len = 3; 153 break; 154 } 155 default: { 156 /* Unsupported BPP. */ 157 return -1; 158 } 159 } 160 161 /* Determine where we need to write and copy the pixel data over. */ 162 target = ((char *)device->framebuffer) + (y * device->width + x) * coord_factor; 163 (void)memcpy(target, value, len); 164 165 return 0; 166} 167 168void *bga_get_framebuffer(bga_p device) 169{ 170 return device->framebuffer; 171} 172