[vim][display] Implement display ioctls

The get_fb ioctl can grab the real framebuffer, so an unnecessary copy
is avoided. This code is mostly temporary until the real GPU display API
is implemented.

This is a reland of 1229a100c04db0ec1d3fb3fe545252f9d4e808ad with a fix
for cache flushing through the ioctl.

Change-Id: I3965399ac00fc23d580f84e33cfd604d2a232276
diff --git a/system/dev/display/vim-display/vim-display.c b/system/dev/display/vim-display/vim-display.c
index 755f9b3..d0e0418 100644
--- a/system/dev/display/vim-display/vim-display.c
+++ b/system/dev/display/vim-display/vim-display.c
@@ -2,25 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "vim-display.h"
+#include "hdmitx.h"
 #include <assert.h>
+#include <ddk/binding.h>
+#include <ddk/debug.h>
+#include <ddk/device.h>
+#include <ddk/driver.h>
+#include <ddk/io-buffer.h>
+#include <ddk/protocol/display.h>
+#include <ddk/protocol/platform-defs.h>
+#include <ddk/protocol/platform-device.h>
+#include <hw/reg.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <ddk/device.h>
-#include <ddk/driver.h>
-#include <ddk/debug.h>
-#include <ddk/binding.h>
-#include <ddk/io-buffer.h>
-#include <ddk/protocol/display.h>
-#include <ddk/protocol/platform-defs.h>
-#include <ddk/protocol/platform-device.h>
-#include <zircon/syscalls.h>
 #include <zircon/assert.h>
-#include <hw/reg.h>
-#include "vim-display.h"
-#include "hdmitx.h"
+#include <zircon/device/display.h>
+#include <zircon/syscalls.h>
 
 /* Default formats */
 static const uint8_t _ginput_color_format   = HDMI_COLOR_FORMAT_444;
@@ -55,18 +56,44 @@
     return ZX_OK;
 }
 
-static void vc_flush_framebuffer(void* ctx) {
-    vim2_display_t* display = ctx;
+static void flush_framebuffer(vim2_display_t* display) {
     pdev_vmo_buffer_cache_flush(&display->fbuffer, 0,
         (display->disp_info.stride * display->disp_info.height *
             ZX_PIXEL_FORMAT_BYTES(display->disp_info.format)));
 }
 
+static void vc_flush_framebuffer(void* ctx) {
+    flush_framebuffer(ctx);
+}
+
+static void vc_display_set_ownership_change_callback(void* ctx, zx_display_cb_t callback,
+                                                     void* cookie) {
+    vim2_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) {
+    vim2_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);
+    }
+}
+
 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
+    .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 void display_release(void* ctx) {
@@ -92,8 +119,78 @@
     .release =  display_release,
 };
 
+struct display_client_device {
+    vim2_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;
+    vim2_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.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;
+    vim2_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 = "vim2-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,
 };
 
 static zx_status_t setup_hdmi(vim2_display_t* display)
@@ -118,12 +215,13 @@
     display->disp_info.width  = display->p->timings.hactive;
     display->disp_info.height = display->p->timings.vactive;
     display->disp_info.stride = display->p->timings.hactive;
+    display->disp_info.pixelsize = ZX_PIXEL_FORMAT_BYTES(display->disp_info.format);
 
-   status = pdev_map_contig_buffer(&display->pdev,
-                        (display->disp_info.stride * display->disp_info.height *
-                            ZX_PIXEL_FORMAT_BYTES(display->disp_info.format)),
-                        0, ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE, ZX_CACHE_POLICY_CACHED,
-                        &display->fbuffer);
+    status = pdev_map_contig_buffer(&display->pdev,
+                                    (display->disp_info.stride * display->disp_info.height *
+                                     ZX_PIXEL_FORMAT_BYTES(display->disp_info.format)),
+                                    0, ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE, ZX_CACHE_POLICY_CACHED,
+                                    &display->fbuffer);
     if (status != ZX_OK) {
         return status;
     }
@@ -219,6 +317,7 @@
     }
 
     display->parent = parent;
+    display->console_visible = true;
 
     zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &display->pdev);
     if (status !=  ZX_OK) {
diff --git a/system/dev/display/vim-display/vim-display.h b/system/dev/display/vim-display/vim-display.h
index 31854c0..091808f 100644
--- a/system/dev/display/vim-display/vim-display.h
+++ b/system/dev/display/vim-display/vim-display.h
@@ -4,13 +4,15 @@
 
 #pragma once
 
+#include "edid.h"
 #include <assert.h>
+#include <ddk/protocol/display.h>
+#include <ddk/protocol/gpio.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "edid.h"
-#include <ddk/protocol/gpio.h>
+#include <zircon/device/display.h>
 
 #define DISP_ERROR(fmt, ...) zxlogf(ERROR, "[%s %d]" fmt, __func__, __LINE__, ##__VA_ARGS__)
 #define DISP_INFO(fmt, ...) zxlogf(INFO, "[%s %d]" fmt, __func__, __LINE__, ##__VA_ARGS__)
@@ -52,7 +54,9 @@
     disp_timing_t                       std_disp_timing;
     disp_timing_t                       pref_disp_timing;
 
-
+    bool console_visible;
+    zx_display_cb_t ownership_change_callback;
+    void* ownership_change_cookie;
 } vim2_display_t;
 
 zx_status_t configure_canvas(vim2_display_t* display);
@@ -60,4 +64,4 @@
 void osd_debug_dump_register_all(vim2_display_t* display);
 void osd_dump(vim2_display_t* display);
 zx_status_t get_preferred_res(vim2_display_t* display, uint16_t edid_buf_size);
-struct hdmi_param** get_supported_formats(void);
\ No newline at end of file
+struct hdmi_param** get_supported_formats(void);