| /* |
| * Copyright 2017, Data61 |
| * Commonwealth Scientific and Industrial Research Organisation (CSIRO) |
| * ABN 41 687 119 230. |
| * |
| * This software may be distributed and modified according to the terms of |
| * the BSD 2-Clause license. Note that NO WARRANTY is provided. |
| * See "LICENSE_BSD2.txt" for details. |
| * |
| * @TAG(DATA61_BSD) |
| */ |
| /* The code in this file was partially extracted from David Pollack's summer |
| * project, ssh://hg//data/hg_root/summer_students/davidp/sel4vga. |
| */ |
| |
| #include <assert.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <bga/bga.h> |
| |
| struct bga { |
| void *framebuffer; |
| |
| /* IO port functions. */ |
| uint16_t (*read)(uint16_t port); |
| void (*write)(uint16_t port, uint16_t value); |
| |
| /* Current configuration. */ |
| unsigned int width; |
| unsigned int height; |
| unsigned int bpp; |
| }; |
| |
| /* The BGA device is controlled by operating on two IO ports, first the index |
| * port to indicate what register you are trying to read/write and then the |
| * data port to read or write the actual register. |
| */ |
| static const uint16_t INDEX = 0x1ce; |
| static const uint16_t DATA = 0x1cf; |
| static void write_data(bga_p device, uint16_t index, uint16_t data) |
| { |
| assert(device != NULL); |
| device->write(INDEX, index); |
| device->write(DATA, data); |
| } |
| static uint16_t read_data(bga_p device, uint16_t index) |
| { |
| assert(device != NULL); |
| device->write(INDEX, index); |
| return device->read(DATA); |
| } |
| |
| /* After writing INDEX_ENABLED to the index port, you can write either 0x0000 |
| * or 0x0001 to the data port to disable or enable the device respectively. |
| */ |
| static const uint16_t INDEX_ENABLED = 0x0004; |
| static void disable(bga_p device) |
| { |
| const uint16_t data_disable = 0x0000; |
| write_data(device, INDEX_ENABLED, data_disable); |
| } |
| static void enable(bga_p device) |
| { |
| /* Note that we unconditionally use a linear frame buffer and clear the |
| * screen. Not ideal, but the emphasis is on simplicity in this driver. |
| */ |
| const uint16_t data_enable = 0x0001; |
| const uint16_t lfb = 0x0040; |
| write_data(device, INDEX_ENABLED, data_enable | lfb); |
| } |
| |
| uint16_t bga_version(bga_p device) |
| { |
| assert(device != NULL); |
| const uint16_t index_id = 0x0000; /* Index to read version information. */ |
| const uint16_t mask = (uint16_t)~0xb0c0; |
| uint16_t result = read_data(device, index_id); |
| return result & mask; |
| } |
| |
| bga_p bga_init(void *framebuffer, |
| void (*ioport_write)(uint16_t port, uint16_t value), |
| uint16_t (*ioport_read)(uint16_t port)) |
| { |
| bga_p device = (bga_p)malloc(sizeof(struct bga)); |
| if (device == NULL) { |
| return device; |
| } |
| |
| memset(device, 0, sizeof(*device)); |
| device->framebuffer = framebuffer; |
| device->write = ioport_write; |
| device->read = ioport_read; |
| |
| return device; |
| } |
| |
| int bga_destroy(bga_p device) |
| { |
| free(device); |
| return 0; |
| } |
| |
| int bga_set_mode(bga_p device, unsigned int width, unsigned int height, unsigned int bpp) |
| { |
| /* We need to disable the device to change these parameters. */ |
| disable(device); |
| |
| /* Relevant indicies to write to. */ |
| const uint16_t x_resolution = 0x0001; |
| const uint16_t y_resolution = 0x0002; |
| const uint16_t bits_per_pixel = 0x0003; |
| |
| /* Setup the requested settings. */ |
| write_data(device, x_resolution, width); |
| device->width = width; |
| write_data(device, y_resolution, height); |
| device->height = height; |
| write_data(device, bits_per_pixel, bpp); |
| device->bpp = bpp; |
| |
| /* Finally re-enable the device to have the settings take effect. */ |
| enable(device); |
| |
| return 0; |
| } |
| |
| int bga_set_pixel(bga_p device, unsigned int x, unsigned int y, char *value) |
| { |
| char *target; |
| unsigned int coord_factor; |
| size_t len; |
| |
| /* XXX: None of these other than 24-bit have been tested and they are most |
| * likely incorrect. |
| */ |
| |
| switch (device->bpp) { |
| case 8: { |
| coord_factor = 1; |
| len = 1; |
| break; |
| } |
| case 15: |
| case 16: { |
| coord_factor = 2; |
| len = 2; |
| break; |
| } |
| case 24: { |
| coord_factor = 3; |
| len = 3; |
| break; |
| } |
| case 32: { |
| coord_factor = 4; |
| len = 3; |
| break; |
| } |
| default: { |
| /* Unsupported BPP. */ |
| return -1; |
| } |
| } |
| |
| /* Determine where we need to write and copy the pixel data over. */ |
| target = ((char *)device->framebuffer) + (y * device->width + x) * coord_factor; |
| (void)memcpy(target, value, len); |
| |
| return 0; |
| } |
| |
| void *bga_get_framebuffer(bga_p device) |
| { |
| return device->framebuffer; |
| } |