[astro][display] Update Astro's simple display driver to use the new Display API
Change-Id: I9021b715d3c783035712762933e6c2fd4f925066
diff --git a/system/dev/display/astro-display/astro-display.c b/system/dev/display/astro-display/astro-display.c
index caf2ba2..96150b0 100644
--- a/system/dev/display/astro-display/astro-display.c
+++ b/system/dev/display/astro-display/astro-display.c
@@ -9,7 +9,7 @@
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/io-buffer.h>
-#include <ddk/protocol/display.h>
+#include <ddk/protocol/display-controller.h>
#include <ddk/protocol/platform-defs.h>
#include <ddk/protocol/platform-device.h>
#include <hw/reg.h>
@@ -22,61 +22,186 @@
#include <zircon/device/display.h>
#include <zircon/syscalls.h>
-static zx_status_t vc_set_mode(void* ctx, zx_display_info_t* info) {
+static const zx_pixel_format_t _gsupported_pixel_formats = { ZX_PIXEL_FORMAT_RGB_565 };
+
+typedef struct image_info {
+ zx_handle_t pmt;
+ uint8_t canvas_idx;
+
+ list_node_t node;
+} image_info_t;
+
+static zx_status_t config_canvas(astro_display_t* display, zx_paddr_t paddr, uint8_t* idx) {
+ uint32_t fbh = display->height * 2;
+ uint32_t fbw = display->stride * 2;
+
+ // TODO: Find index dynamically
+ *idx = OSD2_DMC_CAV_INDEX;
+
+ DISP_INFO("Canvas Diminsions: w=%d h=%d\n", fbw, fbh);
+
+ // set framebuffer address in DMC, read/modify/write
+ WRITE32_DMC_REG(DMC_CAV_LUT_DATAL,
+ (((paddr + 7) >> 3) & DMC_CAV_ADDR_LMASK) |
+ ((((fbw + 7) >> 3) & DMC_CAV_WIDTH_LMASK) << DMC_CAV_WIDTH_LBIT));
+
+ WRITE32_DMC_REG(DMC_CAV_LUT_DATAH,
+ ((((fbw + 7) >> 3) >> DMC_CAV_WIDTH_LWID) << DMC_CAV_WIDTH_HBIT) |
+ ((fbh & DMC_CAV_HEIGHT_MASK) << DMC_CAV_HEIGHT_BIT));
+
+ WRITE32_DMC_REG(DMC_CAV_LUT_ADDR, DMC_CAV_LUT_ADDR_WR_EN | OSD2_DMC_CAV_INDEX );
+ // read a cbus to make sure last write finish.
+ READ32_DMC_REG(DMC_CAV_LUT_DATAH);
+
+ return ZX_OK;
+
+}
+
+static void astro_set_display_controller_cb(void* ctx, void* cb_ctx, display_controller_cb_t* cb) {
+ astro_display_t* display = ctx;
+ mtx_lock(&display->cb_lock);
+
+ mtx_lock(&display->display_lock);
+
+ display->dc_cb = cb;
+ display->dc_cb_ctx = cb_ctx;
+
+ uint64_t display_id = display->display_id;
+ mtx_unlock(&display->display_lock);
+
+ display->dc_cb->on_displays_changed(display->dc_cb_ctx, &display_id, 1, NULL, 0);
+ mtx_unlock(&display->cb_lock);
+}
+
+static zx_status_t astro_get_display_info(void* ctx, uint64_t display_id, display_info_t* info) {
+ astro_display_t* display = ctx;
+ mtx_lock(&display->display_lock);
+ if (display_id != display->display_id) {
+ mtx_unlock(&display->display_lock);
+ return ZX_ERR_NOT_FOUND;
+ }
+
+ info->edid_present = false;
+ info->panel.params.height = display->height;
+ info->panel.params.width = display->width;
+ info->panel.params.refresh_rate_e2 = 3000; // Just guess that it's 30fps
+ info->pixel_formats = &_gsupported_pixel_formats;
+ info->pixel_format_count = sizeof(_gsupported_pixel_formats) / sizeof(zx_pixel_format_t);
+
+ mtx_unlock(&display->display_lock);
return ZX_OK;
}
-static zx_status_t vc_get_mode(void* ctx, zx_display_info_t* info) {
+static zx_status_t astro_import_vmo_image(void* ctx, image_t* image, zx_handle_t vmo, size_t offset) {
+ image_info_t* import_info = calloc(1, sizeof(image_info_t));
+ if (import_info == NULL) {
+ return ZX_ERR_NO_MEMORY;
+ }
+
+ unsigned pixel_size = ZX_PIXEL_FORMAT_BYTES(image->pixel_format);
+ unsigned size = ROUNDUP(image->width * image->height * pixel_size, PAGE_SIZE);
+ unsigned num_pages = size / PAGE_SIZE;
+ zx_paddr_t paddr[num_pages];
+
astro_display_t* display = ctx;
- memcpy(info, &display->disp_info, sizeof(zx_display_info_t));
+ mtx_lock(&display->image_lock);
+
+ zx_status_t status = zx_bti_pin(display->bti, ZX_BTI_PERM_READ, vmo, offset, size,
+ paddr, num_pages, &import_info->pmt);
+ if (status != ZX_OK) {
+ goto fail;
+ }
+
+ for (unsigned i = 0; i < num_pages - 1; i++) {
+ if (paddr[i] + PAGE_SIZE != paddr[i + 1]) {
+ status = ZX_ERR_INVALID_ARGS;
+ goto fail;
+ }
+ }
+
+ if (config_canvas(display, paddr[0], &import_info->canvas_idx) != ZX_OK) {
+ status = ZX_ERR_NO_RESOURCES;
+ goto fail;
+ }
+
+ list_add_head(&display->imported_images, &import_info->node);
+ image->handle = (void*) (uint64_t) import_info->canvas_idx;
+
+ mtx_unlock(&display->image_lock);
+
return ZX_OK;
+fail:
+ mtx_unlock(&display->image_lock);
+
+ if (import_info->pmt != ZX_HANDLE_INVALID) {
+ zx_handle_close(import_info->pmt);
+ }
+ free(import_info);
+ return status;
}
-static zx_status_t vc_get_framebuffer(void* ctx, void** framebuffer) {
- if (!framebuffer) return ZX_ERR_INVALID_ARGS;
+static void astro_release_image(void* ctx, image_t* image) {
astro_display_t* display = ctx;
- *framebuffer = io_buffer_virt(&display->fbuffer);
- return ZX_OK;
-}
+ mtx_lock(&display->image_lock);
-static void flush_framebuffer(astro_display_t* display) {
- io_buffer_cache_flush(&display->fbuffer, 0,
- (display->disp_info.stride * display->disp_info.height *
- ZX_PIXEL_FORMAT_BYTES(display->disp_info.format)));
-}
+ image_info_t* info;
+ list_for_every_entry(&display->imported_images, info, image_info_t, node) {
+ if ((void*) (uint64_t) info->canvas_idx == image->handle) {
+ list_delete(&info->node);
+ break;
+ }
+ }
-static void vc_flush_framebuffer(void* ctx) {
- flush_framebuffer(ctx);
-}
+ mtx_unlock(&display->image_lock);
-static void vc_display_set_ownership_change_callback(void* ctx, zx_display_cb_t callback,
- void* cookie) {
- astro_display_t* display = ctx;
- display->ownership_change_callback = callback;
- display->ownership_change_cookie = cookie;
-}
-
-static void vc_display_acquire_or_release_display(void* ctx, bool acquire) {
- astro_display_t* display = ctx;
-
- if (acquire) {
- display->console_visible = true;
- if (display->ownership_change_callback)
- display->ownership_change_callback(true, display->ownership_change_cookie);
- } else if (!acquire) {
- display->console_visible = false;
- if (display->ownership_change_callback)
- display->ownership_change_callback(false, display->ownership_change_cookie);
+ if (info) {
+ // free_canvas_entry(display, info->canvas_idx);
+ zx_handle_close(info->pmt);
+ free(info);
}
}
-static display_protocol_ops_t vc_display_proto = {
- .set_mode = vc_set_mode,
- .get_mode = vc_get_mode,
- .get_framebuffer = vc_get_framebuffer,
- .flush = vc_flush_framebuffer,
- .set_ownership_change_callback = vc_display_set_ownership_change_callback,
- .acquire_or_release_display = vc_display_acquire_or_release_display,
+static bool astro_check_configuration(void* ctx,
+ display_config_t** display_configs, uint32_t display_count) {
+ if (display_count != 1) {
+ return display_count == 0;
+ }
+ astro_display_t* display = ctx;
+ mtx_lock(&display->display_lock);
+ bool res = (display_configs[0]->display_id == display->display_id
+ && display_configs[0]->mode.h_addressable == display->width
+ && display_configs[0]->image.width == display->width
+ && display_configs[0]->mode.v_addressable == display->height
+ && display_configs[0]->image.height == display->height);
+ mtx_unlock(&display->display_lock);
+ return res;
+}
+
+static void astro_apply_configuration(void* ctx,
+ display_config_t** display_configs, uint32_t display_count) {
+ // TODO: Nothing to do for now
+}
+
+static uint32_t astro_compute_linear_stride(void* ctx, uint32_t width, zx_pixel_format_t format) {
+ // The astro display controller needs buffers with a stride that is an even
+ // multiple of 32.
+ return ROUNDUP(width, 32 / ZX_PIXEL_FORMAT_BYTES(format));
+}
+
+static zx_status_t allocate_vmo(void* ctx, uint64_t size, zx_handle_t* vmo_out) {
+ astro_display_t* display = ctx;
+ return zx_vmo_create_contiguous(display->bti, size, 0, vmo_out);
+}
+
+static display_controller_protocol_ops_t display_controller_ops = {
+ .set_display_controller_cb = astro_set_display_controller_cb,
+ .get_display_info = astro_get_display_info,
+ .import_vmo_image = astro_import_vmo_image,
+ .release_image = astro_release_image,
+ .check_configuration = astro_check_configuration,
+ .apply_configuration = astro_apply_configuration,
+ .compute_linear_stride = astro_compute_linear_stride,
+ .allocate_vmo = allocate_vmo,
};
static void display_release(void* ctx) {
@@ -94,81 +219,6 @@
.release = display_release,
};
-struct display_client_device {
- astro_display_t* display;
- zx_device_t* device;
-};
-
-static zx_status_t display_client_ioctl(void* ctx, uint32_t op, const void* in_buf, size_t in_len,
- void* out_buf, size_t out_len, size_t* out_actual) {
- struct display_client_device* client_struct = ctx;
- astro_display_t* display = client_struct->display;
- switch (op) {
- case IOCTL_DISPLAY_GET_FB: {
- if (out_len < sizeof(ioctl_display_get_fb_t))
- return ZX_ERR_INVALID_ARGS;
- ioctl_display_get_fb_t* description = (ioctl_display_get_fb_t*)(out_buf);
- zx_status_t status = zx_handle_duplicate(display->fbuffer.vmo_handle, ZX_RIGHT_SAME_RIGHTS,
- &description->vmo);
- if (status != ZX_OK)
- return ZX_ERR_NO_RESOURCES;
- description->info = display->disp_info;
- *out_actual = sizeof(ioctl_display_get_fb_t);
- if (display->ownership_change_callback)
- display->ownership_change_callback(false, display->ownership_change_cookie);
- return ZX_OK;
- }
- case IOCTL_DISPLAY_FLUSH_FB:
- case IOCTL_DISPLAY_FLUSH_FB_REGION:
- flush_framebuffer(display);
- return ZX_OK;
- default:
- DISP_ERROR("Invalid ioctl %d\n", op);
- return ZX_ERR_INVALID_ARGS;
- }
-}
-
-static zx_status_t display_client_close(void* ctx, uint32_t flags) {
- struct display_client_device* client_struct = ctx;
- astro_display_t* display = client_struct->display;
- if (display->ownership_change_callback)
- display->ownership_change_callback(true, display->ownership_change_cookie);
- free(ctx);
- return ZX_OK;
-}
-
-static zx_protocol_device_t client_device_proto = {
- .version = DEVICE_OPS_VERSION,
- .ioctl = display_client_ioctl,
- .close = display_client_close,
-};
-
-static zx_status_t vc_open(void* ctx, zx_device_t** dev_out, uint32_t flags) {
- struct display_client_device* s = calloc(1, sizeof(struct display_client_device));
-
- s->display = ctx;
-
- device_add_args_t vc_fbuff_args = {
- .version = DEVICE_ADD_ARGS_VERSION,
- .name = "astro-display",
- .ctx = s,
- .ops = &client_device_proto,
- .flags = DEVICE_ADD_INSTANCE,
- };
- zx_status_t status = device_add(s->display->fbdevice, &vc_fbuff_args, &s->device);
- if (status != ZX_OK) {
- free(s);
- return status;
- }
- *dev_out = s->device;
- return ZX_OK;
-}
-
-static zx_protocol_device_t display_device_proto = {
- .version = DEVICE_OPS_VERSION,
- .open = vc_open,
-};
-
/* Table from Linux source */
/* TODO: Need to separate backlight driver from display driver */
static const uint8_t backlight_init_table[] = {
@@ -198,49 +248,31 @@
}
}
-static void config_canvas(astro_display_t* display) {
- uint32_t fbh = display->disp_info.height * 2;
- uint32_t fbw = display->disp_info.stride * 2;
-
- DISP_INFO("Canvas Diminsions: w=%d h=%d\n", fbw, fbh);
-
- // set framebuffer address in DMC, read/modify/write
- WRITE32_DMC_REG(DMC_CAV_LUT_DATAL,
- (((io_buffer_phys(&display->fbuffer) + 7) >> 3) & DMC_CAV_ADDR_LMASK) |
- ((((fbw + 7) >> 3) & DMC_CAV_WIDTH_LMASK) << DMC_CAV_WIDTH_LBIT));
-
- WRITE32_DMC_REG(DMC_CAV_LUT_DATAH,
- ((((fbw + 7) >> 3) >> DMC_CAV_WIDTH_LWID) << DMC_CAV_WIDTH_HBIT) |
- ((fbh & DMC_CAV_HEIGHT_MASK) << DMC_CAV_HEIGHT_BIT));
-
- WRITE32_DMC_REG(DMC_CAV_LUT_ADDR, DMC_CAV_LUT_ADDR_WR_EN | OSD2_DMC_CAV_INDEX );
- // read a cbus to make sure last write finish.
- READ32_DMC_REG(DMC_CAV_LUT_DATAH);
-
-}
-
static zx_status_t setup_display_if(astro_display_t* display) {
zx_status_t status;
+ mtx_lock(&display->cb_lock);
+ mtx_lock(&display->display_lock);
+
+ uint64_t display_added = INVALID_DISPLAY_ID;
+ uint64_t display_removed = INVALID_DISPLAY_ID;
+
// allocate frame buffer
- display->disp_info.format = ZX_PIXEL_FORMAT_RGB_565;
- display->disp_info.width = 608;
- display->disp_info.height = 1024;
- display->disp_info.pixelsize = ZX_PIXEL_FORMAT_BYTES(display->disp_info.format);
- // The astro display controller needs buffers with a stride that is an even
- // multiple of 32.
- display->disp_info.stride = ROUNDUP(display->disp_info.width,
- 32 / display->disp_info.pixelsize);
+ display->format = ZX_PIXEL_FORMAT_RGB_565;
+ display->width = 608;
+ display->height = 1024;
+ display->stride = astro_compute_linear_stride(
+ display, display->width, display->format);
status = io_buffer_init(&display->fbuffer, display->bti,
- (display->disp_info.stride * display->disp_info.height *
- ZX_PIXEL_FORMAT_BYTES(display->disp_info.format)),
+ (display->stride * display->height *
+ ZX_PIXEL_FORMAT_BYTES(display->format)),
IO_BUFFER_RW | IO_BUFFER_CONTIG);
if (status != ZX_OK) {
- return status;
+ goto fail;
}
- config_canvas(display);
+ config_canvas(display, io_buffer_phys(&display->fbuffer), &display->fb_canvas_idx);
init_backlight(display);
zx_set_framebuffer(get_root_resource(), display->fbuffer.vmo_handle,
@@ -248,21 +280,25 @@
display->disp_info.width, display->disp_info.height,
display->disp_info.stride);
- device_add_args_t vc_fbuff_args = {
- .version = DEVICE_ADD_ARGS_VERSION,
- .name = "astro-display",
- .ctx = display,
- .ops = &display_device_proto,
- .proto_id = ZX_PROTOCOL_DISPLAY,
- .proto_ops = &vc_display_proto,
- };
+ display_added = display->display_id;
- status = device_add(display->mydevice, &vc_fbuff_args, &display->fbdevice);
- if (status != ZX_OK) {
- free(display);
- return status;
+ mtx_unlock(&display->display_lock);
+
+ if (display->dc_cb) {
+ display->dc_cb->on_displays_changed(display->dc_cb_ctx,
+ &display_added,
+ display_added != INVALID_DISPLAY_ID,
+ &display_removed,
+ display_removed != INVALID_DISPLAY_ID);
}
+ mtx_unlock(&display->cb_lock);
+
return ZX_OK;
+
+fail:
+ mtx_unlock(&display->display_lock);
+ mtx_unlock(&display->cb_lock);
+ return status;
}
static int main_astro_display_thread(void *arg) {
@@ -315,15 +351,26 @@
goto fail;
}
- device_add_args_t vc_fbuff_args = {
+ device_add_args_t add_args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = "astro-display",
.ctx = display,
.ops = &main_device_proto,
- .flags = (DEVICE_ADD_NON_BINDABLE | DEVICE_ADD_INVISIBLE),
+ .proto_id = ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL,
+ .proto_ops = &display_controller_ops,
};
- status = device_add(display->parent, &vc_fbuff_args, &display->mydevice);
+ status = device_add(display->parent, &add_args, &display->mydevice);
+ if (status != ZX_OK) {
+ DISP_ERROR("Could not add device\n");
+ goto fail;
+ }
+
+ display->display_id = 1;
+ list_initialize(&display->imported_images);
+ mtx_init(&display->display_lock, mtx_plain);
+ mtx_init(&display->image_lock, mtx_plain);
+ mtx_init(&display->cb_lock, mtx_plain);
thrd_create_with_name(&display->main_thread, main_astro_display_thread, display,
"main_astro_display_thread");
diff --git a/system/dev/display/astro-display/astro-display.h b/system/dev/display/astro-display/astro-display.h
index 016047d..4c9ab72 100644
--- a/system/dev/display/astro-display/astro-display.h
+++ b/system/dev/display/astro-display/astro-display.h
@@ -13,7 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <zircon/device/display.h>
+#include <ddk/protocol/display-controller.h>
#include <ddk/device.h>
#include <ddk/io-buffer.h>
#include <ddk/protocol/platform-device.h>
@@ -62,10 +62,29 @@
gpio_protocol_t gpio;
i2c_protocol_t i2c;
thrd_t main_thread;
+ // Lock for general display state, in particular display_id.
+ mtx_t display_lock;
+ // Lock for imported images.
+ mtx_t image_lock;
+ // Lock for the display callback, for enforcing an ordering on
+ // hotplug callbacks. Should be acquired before display_lock.
+ mtx_t cb_lock;
+ // TODO(stevensd): This can race if this is changed right after
+ // vsync but before the interrupt is handled.
+ uint8_t current_image;
io_buffer_t mmio_dmc;
io_buffer_t fbuffer;
zx_display_info_t disp_info;
+ uint8_t fb_canvas_idx;
+ zx_handle_t vsync_interrupt;
+
+ // The current display id (if display_attached), or the next display id
+ uint64_t display_id;
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ zx_pixel_format_t format;
uint8_t input_color_format;
uint8_t output_color_format;
@@ -74,6 +93,11 @@
bool console_visible;
zx_display_cb_t ownership_change_callback;
void* ownership_change_cookie;
+
+ display_controller_cb_t* dc_cb;
+ void* dc_cb_ctx;
+ list_node_t imported_images;
+
} astro_display_t;