1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <ddk/debug.h>
6#include <ddk/device.h>
7#include <ddk/driver.h>
8#include <ddk/protocol/pci.h>
9#include <ddktl/protocol/display-controller.h>
10#include <hw/pci.h>
11
12#include <assert.h>
13#include <zircon/process.h>
14#include <zircon/syscalls.h>
15#include <zircon/types.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <unistd.h>
20
21#include "simple-display.h"
22
23// implement display controller protocol
24static constexpr uint64_t kDisplayId = 1;
25
26static void* const kImageHandle = reinterpret_cast<void*>(0xdecafc0ffee);
27
28void SimpleDisplay::SetDisplayControllerCb(void* cb_ctx, display_controller_cb_t* cb) {
29    cb_ctx_ = cb_ctx;
30    cb_ = cb;
31
32    added_display_args_t args = {};
33    args.display_id = kDisplayId;
34    args.edid_present = false;
35    args.panel.params.height = height_;
36    args.panel.params.width = width_;
37    args.panel.params.refresh_rate_e2 = 3000; // Just guess that it's 30fps
38    args.pixel_formats = &format_;
39    args.pixel_format_count = 1;
40
41    cb->on_displays_changed(cb_ctx, &args, 1, nullptr, 0);
42}
43
44zx_status_t SimpleDisplay::ImportVmoImage(image_t* image, const zx::vmo& vmo, size_t offset) {
45    zx_info_handle_basic_t import_info;
46    size_t actual, avail;
47    zx_status_t status = vmo.get_info(ZX_INFO_HANDLE_BASIC,
48                                      &import_info, sizeof(import_info), &actual, &avail);
49    if (status != ZX_OK) {
50        return status;
51    }
52    if (import_info.koid != framebuffer_koid_) {
53        return ZX_ERR_INVALID_ARGS;
54    }
55    if (image->width != width_ || image->height != height_
56            || image->pixel_format != format_ || offset != 0) {
57        return ZX_ERR_INVALID_ARGS;
58    }
59    image->handle = kImageHandle;
60    return ZX_OK;
61}
62
63void SimpleDisplay::ReleaseImage(image_t* image) {
64    // noop
65}
66
67void SimpleDisplay::CheckConfiguration(const display_config_t** display_configs,
68                                       uint32_t* display_cfg_result,
69                                       uint32_t** layer_cfg_results,
70                                       uint32_t display_count) {
71    *display_cfg_result = CONFIG_DISPLAY_OK;
72    if (display_count != 1) {
73        ZX_DEBUG_ASSERT(display_count == 0);
74        return;
75    }
76    ZX_DEBUG_ASSERT(display_configs[0]->display_id == kDisplayId);
77    bool success;
78    if (display_configs[0]->layer_count != 1) {
79        success = false;
80    } else {
81        primary_layer_t* layer = &display_configs[0]->layers[0]->cfg.primary;
82        frame_t frame = {
83                .x_pos = 0, .y_pos = 0, .width = width_, .height = height_,
84        };
85        success = display_configs[0]->layers[0]->type == LAYER_PRIMARY
86                && layer->transform_mode == FRAME_TRANSFORM_IDENTITY
87                && layer->image.width == width_
88                && layer->image.height == height_
89                && memcmp(&layer->dest_frame, &frame, sizeof(frame_t)) == 0
90                && memcmp(&layer->src_frame, &frame, sizeof(frame_t)) == 0
91                && display_configs[0]->cc_flags == 0
92                && layer->alpha_mode == ALPHA_DISABLE;
93    }
94    if (!success) {
95        layer_cfg_results[0][0] = CLIENT_MERGE_BASE;
96        for (unsigned i = 1; i < display_configs[0]->layer_count; i++) {
97            layer_cfg_results[0][i] = CLIENT_MERGE_SRC;
98        }
99    }
100}
101
102void SimpleDisplay::ApplyConfiguration(const display_config_t** display_config,
103                                       uint32_t display_count) {
104    bool has_image = display_count != 0 && display_config[0]->layer_count != 0;
105    void* handles[] = { kImageHandle };
106    if (cb_) {
107        cb_->on_display_vsync(cb_ctx_, kDisplayId, zx_clock_get(ZX_CLOCK_MONOTONIC),
108                              handles, has_image);
109    }
110}
111
112uint32_t SimpleDisplay::ComputeLinearStride(uint32_t width, zx_pixel_format_t format) {
113    return (width == width_ && format == format_) ? stride_ : 0;
114}
115
116zx_status_t SimpleDisplay::AllocateVmo(uint64_t size, zx_handle_t* vmo_out) {
117    zx_info_handle_count handle_count;
118    size_t actual, avail;
119    zx_status_t status = framebuffer_handle_.get_info(ZX_INFO_HANDLE_COUNT, &handle_count,
120                                                      sizeof(handle_count), &actual, &avail);
121    if (status != ZX_OK) {
122        return status;
123    }
124    if (handle_count.handle_count != 1) {
125        return ZX_ERR_NO_RESOURCES;
126    }
127    if (size > height_ * stride_ * ZX_PIXEL_FORMAT_BYTES(format_)) {
128        return ZX_ERR_OUT_OF_RANGE;
129    }
130    return zx_handle_duplicate(framebuffer_handle_.get(), ZX_RIGHT_SAME_RIGHTS, vmo_out);
131}
132
133// implement device protocol
134
135void SimpleDisplay::DdkUnbind() {
136    DdkRemove();
137}
138
139void SimpleDisplay::DdkRelease() {
140    delete this;
141}
142
143// implement driver object:
144
145zx_status_t SimpleDisplay::Bind(const char* name, fbl::unique_ptr<SimpleDisplay>* vbe_ptr) {
146    zx_info_handle_basic_t framebuffer_info;
147    size_t actual, avail;
148    zx_status_t status = framebuffer_handle_.get_info(
149            ZX_INFO_HANDLE_BASIC, &framebuffer_info, sizeof(framebuffer_info), &actual, &avail);
150    if (status != ZX_OK) {
151        printf("%s: failed to id framebuffer: %d\n", name, status);
152        return status;
153    }
154    framebuffer_koid_ = framebuffer_info.koid;
155
156    status = DdkAdd(name);
157    if (status != ZX_OK) {
158        return status;
159    }
160    // DevMgr now owns this pointer, release it to avoid destroying the object
161    // when device goes out of scope.
162    __UNUSED auto ptr = vbe_ptr->release();
163
164    zxlogf(INFO, "%s: initialized display, %u x %u (stride=%u format=%08x)\n",
165           name, width_, height_, stride_, format_);
166
167    return ZX_OK;
168}
169
170SimpleDisplay::SimpleDisplay(zx_device_t* parent, zx_handle_t vmo,
171                             uintptr_t framebuffer, uint64_t framebuffer_size,
172                             uint32_t width, uint32_t height,
173                             uint32_t stride, zx_pixel_format_t format)
174        : DeviceType(parent), framebuffer_handle_(vmo),
175          framebuffer_(framebuffer), framebuffer_size_(framebuffer_size),
176          width_(width), height_(height), stride_(stride), format_(format) { }
177
178SimpleDisplay::~SimpleDisplay() {
179    zx_vmar_unmap(zx_vmar_root_self(), framebuffer_, framebuffer_size_);
180}
181
182zx_status_t bind_simple_pci_display_bootloader(zx_device_t* dev, const char* name, uint32_t bar) {
183    uint32_t format, width, height, stride;
184    zx_status_t status = zx_framebuffer_get_info(get_root_resource(), &format,
185                                                 &width, &height, &stride);
186    if (status != ZX_OK) {
187        printf("%s: failed to get bootloader dimensions: %d\n", name, status);
188        return ZX_ERR_NOT_SUPPORTED;
189    }
190
191    return bind_simple_pci_display(dev, name, bar, width, height, stride, format);
192}
193
194zx_status_t bind_simple_pci_display(zx_device_t* dev, const char* name, uint32_t bar,
195                                    uint32_t width, uint32_t height,
196                                    uint32_t stride, zx_pixel_format_t format) {
197    pci_protocol_t pci;
198    zx_status_t status;
199    if (device_get_protocol(dev, ZX_PROTOCOL_PCI, &pci)) {
200        return ZX_ERR_NOT_SUPPORTED;
201    }
202
203    void* framebuffer;
204    uint64_t framebuffer_size;
205    zx_handle_t framebuffer_handle;
206    // map framebuffer window
207    status = pci_map_bar(&pci, bar, ZX_CACHE_POLICY_WRITE_COMBINING,
208                         &framebuffer, &framebuffer_size, &framebuffer_handle);
209    if (status != ZX_OK) {
210        printf("%s: failed to map pci bar %d: %d\n", name, bar, status);
211        return status;
212    }
213
214    fbl::AllocChecker ac;
215    fbl::unique_ptr<SimpleDisplay> display(new (&ac) SimpleDisplay(
216            dev, framebuffer_handle, (uintptr_t) framebuffer,
217            framebuffer_size, width, height, stride, format));
218    if (!ac.check()) {
219        zx_handle_close(framebuffer_handle);
220        return ZX_ERR_NO_MEMORY;
221    }
222
223    return display->Bind(name, &display);
224}
225