blob: b6e997193e9aafb913bb14f53eb9de0660e5dfb1 [file] [log] [blame] [edit]
/*
* 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;
}