1// Copyright 2018 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 "vim-display.h"
6#include "hdmitx.h"
7
8#include <assert.h>
9#include <ddk/binding.h>
10#include <ddk/debug.h>
11#include <ddk/device.h>
12#include <ddk/driver.h>
13#include <ddk/io-buffer.h>
14#include <ddk/protocol/display-controller.h>
15#include <ddk/protocol/i2c-impl.h>
16#include <ddk/protocol/platform-defs.h>
17#include <ddk/protocol/platform-device.h>
18#include <fbl/auto_call.h>
19#include <fbl/auto_lock.h>
20#include <fbl/unique_ptr.h>
21#include <hw/arch_ops.h>
22#include <hw/reg.h>
23#include <stdint.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28#include <zircon/assert.h>
29#include <zircon/syscalls.h>
30
31/* Default formats */
32static const uint8_t _ginput_color_format   = HDMI_COLOR_FORMAT_444;
33static const uint8_t _gcolor_depth          = HDMI_COLOR_DEPTH_24B;
34
35static const zx_pixel_format_t _gsupported_pixel_formats = { ZX_PIXEL_FORMAT_RGB_x888 };
36
37typedef struct image_info {
38    zx_handle_t pmt;
39    zx_pixel_format_t format;
40    uint8_t canvas_idx[2];
41
42    list_node_t node;
43} image_info_t;
44
45void populate_added_display_args(vim2_display_t* display, added_display_args_t* args) {
46    args->display_id = display->display_id;
47    args->edid_present = true;
48    args->panel.i2c_bus_id = 0;
49    args->pixel_formats = &_gsupported_pixel_formats;
50    args->pixel_format_count = sizeof(_gsupported_pixel_formats) / sizeof(zx_pixel_format_t);
51    args->cursor_info_count = 0;
52}
53
54static uint32_t vim_compute_linear_stride(void* ctx, uint32_t width, zx_pixel_format_t format) {
55    // The vim2 display controller needs buffers with a stride that is an even
56    // multiple of 32.
57    return ROUNDUP(width, 32 / ZX_PIXEL_FORMAT_BYTES(format));
58}
59
60static void vim_set_display_controller_cb(void* ctx, void* cb_ctx, display_controller_cb_t* cb) {
61    vim2_display_t* display = static_cast<vim2_display_t*>(ctx);
62    mtx_lock(&display->display_lock);
63
64    display->dc_cb = cb;
65    display->dc_cb_ctx = cb_ctx;
66
67    if (display->display_attached) {
68        added_display_args_t args;
69        populate_added_display_args(display, &args);
70        display->dc_cb->on_displays_changed(display->dc_cb_ctx, &args, 1, NULL, 0);
71
72        if (args.is_standard_srgb_out) {
73            display->output_color_format = HDMI_COLOR_FORMAT_RGB;
74        } else {
75            display->output_color_format = HDMI_COLOR_FORMAT_444;
76        }
77    }
78    mtx_unlock(&display->display_lock);
79}
80
81static zx_status_t vim_import_vmo_image(void* ctx, image_t* image, zx_handle_t vmo, size_t offset) {
82    fbl::AllocChecker ac;
83    fbl::unique_ptr<image_info_t> import_info = fbl::make_unique_checked<image_info_t>(&ac);
84    if (!ac.check()) {
85        return ZX_ERR_NO_MEMORY;
86    }
87
88    vim2_display_t* display = static_cast<vim2_display_t*>(ctx);
89    fbl::AutoLock lock(&display->image_lock);
90
91    if (image->type != IMAGE_TYPE_SIMPLE)
92        return ZX_ERR_INVALID_ARGS;
93
94    import_info->format = image->pixel_format;
95    uint32_t stride = vim_compute_linear_stride(display, image->width, image->pixel_format);
96
97    if (image->pixel_format == ZX_PIXEL_FORMAT_RGB_x888) {
98        zx_status_t status = ZX_OK;
99        canvas_info_t info;
100        info.height = image->height;
101        info.stride_bytes = image->planes[0].bytes_per_row;
102        if (info.stride_bytes == 0) {
103            info.stride_bytes = stride * ZX_PIXEL_FORMAT_BYTES(image->pixel_format);
104        }
105        info.wrap = 0;
106        info.blkmode = 0;
107        info.endianness = 0;
108
109        zx_handle_t dup_vmo;
110        status = zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &dup_vmo);
111        if (status != ZX_OK) {
112            return status;
113        }
114
115        status = canvas_config(&display->canvas, dup_vmo, offset + image->planes[0].byte_offset,
116                               &info, &import_info->canvas_idx[0]);
117        if (status != ZX_OK) {
118            return ZX_ERR_NO_RESOURCES;
119        }
120        image->handle = (void*)(uint64_t)import_info->canvas_idx[0];
121    } else if (image->pixel_format == ZX_PIXEL_FORMAT_NV12) {
122        if (image->height % 2 != 0) {
123            return ZX_ERR_INVALID_ARGS;
124        }
125        zx_status_t status = ZX_OK;
126        canvas_info_t info;
127        info.height = image->height;
128        info.stride_bytes = image->planes[0].bytes_per_row;
129        if (info.stride_bytes == 0) {
130            info.stride_bytes = stride * ZX_PIXEL_FORMAT_BYTES(image->pixel_format);
131        }
132        info.wrap = 0;
133        info.blkmode = 0;
134        // Do 64-bit endianness conversion.
135        info.endianness = 7;
136
137        zx_handle_t dup_vmo;
138        status = zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &dup_vmo);
139        if (status != ZX_OK) {
140            return status;
141        }
142
143        status = canvas_config(&display->canvas, dup_vmo, offset + image->planes[0].byte_offset,
144                               &info, &import_info->canvas_idx[0]);
145        if (status != ZX_OK) {
146            return ZX_ERR_NO_RESOURCES;
147        }
148
149        status = zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &dup_vmo);
150        if (status != ZX_OK) {
151            canvas_free(&display->canvas, import_info->canvas_idx[0]);
152            return status;
153        }
154
155        info.height /= 2;
156        info.stride_bytes = image->planes[1].bytes_per_row;
157        if (info.stride_bytes == 0)
158            info.stride_bytes = stride * ZX_PIXEL_FORMAT_BYTES(image->pixel_format);
159
160        status = canvas_config(&display->canvas, dup_vmo, offset + image->planes[1].byte_offset,
161                               &info, &import_info->canvas_idx[1]);
162        if (status != ZX_OK) {
163            canvas_free(&display->canvas, import_info->canvas_idx[0]);
164            return ZX_ERR_NO_RESOURCES;
165        }
166        // The handle used by hardware is VVUUYY, so the UV plane is included twice.
167        image->handle = (void*)(((uint64_t)import_info->canvas_idx[1] << 16) |
168                                (import_info->canvas_idx[1] << 8) | import_info->canvas_idx[0]);
169    } else {
170        return ZX_ERR_INVALID_ARGS;
171    }
172
173    list_add_head(&display->imported_images, &import_info.release()->node);
174
175    return ZX_OK;
176}
177
178static void vim_release_image(void* ctx, image_t* image) {
179    vim2_display_t* display = static_cast<vim2_display_t*>(ctx);
180    mtx_lock(&display->image_lock);
181
182    image_info_t* info;
183    uint32_t canvas_idx0 = (uint64_t)image->handle & 0xff;
184    uint32_t canvas_idx1 = ((uint64_t)image->handle >> 8) & 0xff;
185    list_for_every_entry(&display->imported_images, info, image_info_t, node) {
186        if (info->canvas_idx[0] == canvas_idx0 && info->canvas_idx[1] == canvas_idx1) {
187            list_delete(&info->node);
188            break;
189        }
190    }
191
192    mtx_unlock(&display->image_lock);
193
194    if (info) {
195        canvas_free(&display->canvas, info->canvas_idx[0]);
196        if (info->format == ZX_PIXEL_FORMAT_NV12)
197            canvas_free(&display->canvas, info->canvas_idx[1]);
198        free(info);
199    }
200}
201
202static void vim_check_configuration(void* ctx,
203                                    const display_config_t** display_configs,
204                                    uint32_t* display_cfg_result,
205                                    uint32_t** layer_cfg_results,
206                                    uint32_t display_count) {
207    *display_cfg_result = CONFIG_DISPLAY_OK;
208    if (display_count != 1) {
209        if (display_count > 1) {
210            // The core display driver should never see a configuration with more
211            // than 1 display, so this is a bug in the core driver.
212            ZX_DEBUG_ASSERT(false);
213            *display_cfg_result = CONFIG_DISPLAY_TOO_MANY;
214        }
215        return;
216    }
217    vim2_display_t* display = static_cast<vim2_display_t*>(ctx);
218    mtx_lock(&display->display_lock);
219
220    // no-op, just wait for the client to try a new config
221    if (!display->display_attached || display_configs[0]->display_id != display->display_id) {
222        mtx_unlock(&display->display_lock);
223        return;
224    }
225
226    struct hdmi_param p;
227    if ((memcmp(&display->cur_display_mode, &display_configs[0]->mode, sizeof(display_mode_t))
228            && get_vic(&display_configs[0]->mode, &p) != ZX_OK)
229            || (display_configs[0]->mode.v_addressable % 8)) {
230        mtx_unlock(&display->display_lock);
231        *display_cfg_result = CONFIG_DISPLAY_UNSUPPORTED_MODES;
232        return;
233    }
234
235    bool success;
236    if (display_configs[0]->layer_count != 1) {
237        success = display_configs[0]->layer_count == 0;
238    } else {
239        uint32_t width = display_configs[0]->mode.h_addressable;
240        uint32_t height = display_configs[0]->mode.v_addressable;
241        primary_layer_t* layer = &display_configs[0]->layers[0]->cfg.primary;
242        frame_t frame = {
243                .x_pos = 0, .y_pos = 0, .width = width, .height = height,
244        };
245        uint32_t bytes_per_row = vim_compute_linear_stride(display,
246                                                           layer->image.width,
247                                                           layer->image.pixel_format)
248                * ZX_PIXEL_FORMAT_BYTES(layer->image.pixel_format);
249        success = display_configs[0]->layers[0]->type == LAYER_PRIMARY
250                && layer->transform_mode == FRAME_TRANSFORM_IDENTITY
251                && layer->image.width == width
252                && layer->image.height == height
253                && layer->image.planes[0].byte_offset == 0
254                && (layer->image.planes[0].bytes_per_row == bytes_per_row ||
255                    layer->image.planes[0].bytes_per_row == 0)
256                && memcmp(&layer->dest_frame, &frame, sizeof(frame_t)) == 0
257                && memcmp(&layer->src_frame, &frame, sizeof(frame_t)) == 0
258                && display_configs[0]->cc_flags == 0
259                && layer->alpha_mode == ALPHA_DISABLE;
260    }
261    if (!success) {
262        layer_cfg_results[0][0] = CLIENT_MERGE_BASE;
263        for (unsigned i = 1; i < display_configs[0]->layer_count; i++) {
264            layer_cfg_results[0][i] = CLIENT_MERGE_SRC;
265        }
266    }
267    mtx_unlock(&display->display_lock);
268}
269
270static void vim_apply_configuration(void* ctx,
271                                    const display_config_t** display_configs,
272                                    uint32_t display_count) {
273    vim2_display_t* display = static_cast<vim2_display_t*>(ctx);
274    mtx_lock(&display->display_lock);
275
276    if (display_count == 1 && display_configs[0]->layer_count) {
277        if (memcmp(&display->cur_display_mode, &display_configs[0]->mode, sizeof(display_mode_t))) {
278            zx_status_t status = get_vic(&display_configs[0]->mode, display->p);
279            if (status != ZX_OK) {
280                mtx_unlock(&display->display_lock);
281                zxlogf(ERROR, "Apply with bad mode\n");
282                return;
283            }
284
285            memcpy(&display->cur_display_mode, &display_configs[0]->mode, sizeof(display_mode_t));
286
287            init_hdmi_interface(display, display->p);
288            configure_osd(display, 1);
289            configure_vd(display, 0);
290        }
291
292        // The only way a checked configuration could now be invalid is if display was
293        // unplugged. If that's the case, then the upper layers will give a new configuration
294        // once they finish handling the unplug event. So just return.
295        if (!display->display_attached || display_configs[0]->display_id != display->display_id) {
296            mtx_unlock(&display->display_lock);
297            return;
298        }
299        if (display_configs[0]->layers[0]->cfg.primary.image.pixel_format == ZX_PIXEL_FORMAT_NV12) {
300            uint32_t addr =
301                (uint32_t)(uint64_t)display_configs[0]->layers[0]->cfg.primary.image.handle;
302            flip_vd(display, 0, addr);
303            disable_osd(display, 1);
304        } else {
305            uint8_t addr;
306            addr = (uint8_t)(uint64_t)display_configs[0]->layers[0]->cfg.primary.image.handle;
307            flip_osd(display, 1, addr);
308            disable_vd(display, 0);
309        }
310    } else {
311        disable_vd(display, 0);
312        disable_osd(display, 1);
313    }
314
315    mtx_unlock(&display->display_lock);
316}
317
318static zx_status_t allocate_vmo(void* ctx, uint64_t size, zx_handle_t* vmo_out) {
319    vim2_display_t* display = static_cast<vim2_display_t*>(ctx);
320    zx_status_t status = zx_vmo_create_contiguous(display->bti, size, 0, vmo_out);
321    static const char kVmoName[] = "vim_framebuffer";
322    if (status == ZX_OK)
323        zx_object_set_property(*vmo_out, ZX_PROP_NAME, kVmoName, sizeof(kVmoName));
324    return status;
325}
326
327static display_controller_protocol_ops_t display_controller_ops = {
328    .set_display_controller_cb = vim_set_display_controller_cb,
329    .import_vmo_image = vim_import_vmo_image,
330    .release_image = vim_release_image,
331    .check_configuration = vim_check_configuration,
332    .apply_configuration = vim_apply_configuration,
333    .compute_linear_stride = vim_compute_linear_stride,
334    .allocate_vmo = allocate_vmo,
335};
336
337static uint32_t get_bus_count(void* ctx) {
338    return 1;
339}
340
341static zx_status_t get_max_transfer_size(void* ctx, uint32_t bus_id, size_t* out_size) {
342    *out_size = UINT32_MAX;
343    return ZX_OK;
344}
345
346static zx_status_t set_bitrate(void* ctx, uint32_t bus_id, uint32_t bitrate) {
347    // no-op
348    return ZX_OK;
349}
350
351static zx_status_t transact(void* ctx, uint32_t bus_id, i2c_impl_op_t* ops, size_t count) {
352    vim2_display_t* display = static_cast<vim2_display_t*>(ctx);
353    mtx_lock(&display->i2c_lock);
354
355    uint8_t segment_num = 0;
356    uint8_t offset = 0;
357    for (unsigned i = 0; i < count; i++) {
358        auto op = ops[i];
359
360        // The HDMITX_DWC_I2CM registers are a limited interface to the i2c bus for the E-DDC
361        // protocol, which is good enough for the bus this device provides.
362        if (op.address == 0x30 && !op.is_read && op.length == 1) {
363            segment_num = *((const uint8_t*) op.buf);
364        } else if (op.address == 0x50 && !op.is_read && op.length == 1) {
365            offset = *((const uint8_t*) op.buf);
366        } else if (op.address == 0x50 && op.is_read) {
367            if (op.length % 8 != 0) {
368                mtx_unlock(&display->i2c_lock);
369                return ZX_ERR_NOT_SUPPORTED;
370            }
371
372            hdmitx_writereg(display, HDMITX_DWC_I2CM_SLAVE, 0x50);
373            hdmitx_writereg(display, HDMITX_DWC_I2CM_SEGADDR, 0x30);
374            hdmitx_writereg(display, HDMITX_DWC_I2CM_SEGPTR, segment_num);
375
376            for (uint32_t i = 0; i < op.length; i += 8) {
377                hdmitx_writereg(display, HDMITX_DWC_I2CM_ADDRESS, offset);
378                hdmitx_writereg(display, HDMITX_DWC_I2CM_OPERATION, 1 << 2);
379                offset = static_cast<uint8_t>(offset + 8);
380
381                uint32_t timeout = 0;
382                while ((!(hdmitx_readreg(display, HDMITX_DWC_IH_I2CM_STAT0) & (1 << 1)))
383                        && (timeout < 5)) {
384                    usleep(1000);
385                    timeout ++;
386                }
387                if (timeout == 5) {
388                    DISP_ERROR("HDMI DDC TimeOut\n");
389                    mtx_unlock(&display->i2c_lock);
390                    return ZX_ERR_TIMED_OUT;
391                }
392                usleep(1000);
393                hdmitx_writereg(display, HDMITX_DWC_IH_I2CM_STAT0, 1 << 1);        // clear INT
394
395                for (int j = 0; j < 8; j++) {
396                    uint32_t address = static_cast<uint32_t>(HDMITX_DWC_I2CM_READ_BUFF0 + j);
397                    ((uint8_t*) op.buf)[i + j] =
398                            static_cast<uint8_t>(hdmitx_readreg(display, address));
399                }
400            }
401        } else {
402            mtx_unlock(&display->i2c_lock);
403            return ZX_ERR_NOT_SUPPORTED;
404        }
405
406        if (op.stop) {
407            segment_num = 0;
408            offset = 0;
409        }
410    }
411
412    mtx_unlock(&display->i2c_lock);
413    return ZX_OK;
414}
415
416static i2c_impl_protocol_ops_t i2c_impl_ops = {
417    .get_bus_count = get_bus_count,
418    .get_max_transfer_size = get_max_transfer_size,
419    .set_bitrate = set_bitrate,
420    .transact = transact,
421};
422
423static void display_release(void* ctx) {
424    vim2_display_t* display = static_cast<vim2_display_t*>(ctx);
425
426    if (display) {
427        disable_osd(display, 1);
428        disable_vd(display, 0);
429        bool wait_for_vsync_shutdown = false;
430        if (display->vsync_interrupt != ZX_HANDLE_INVALID) {
431            zx_interrupt_trigger(display->vsync_interrupt, 0, 0);
432            wait_for_vsync_shutdown = true;
433        }
434
435        bool wait_for_main_shutdown = false;
436        if (display->inth != ZX_HANDLE_INVALID) {
437            zx_interrupt_trigger(display->inth, 0, 0);
438            wait_for_main_shutdown = true;
439        }
440
441        int res;
442        if (wait_for_vsync_shutdown) {
443            thrd_join(display->vsync_thread, &res);
444        }
445
446        if (wait_for_main_shutdown) {
447            thrd_join(display->main_thread, &res);
448        }
449
450        gpio_release_interrupt(&display->gpio);
451        io_buffer_release(&display->mmio_preset);
452        io_buffer_release(&display->mmio_hdmitx);
453        io_buffer_release(&display->mmio_hiu);
454        io_buffer_release(&display->mmio_vpu);
455        io_buffer_release(&display->mmio_hdmitx_sec);
456        io_buffer_release(&display->mmio_cbus);
457        zx_handle_close(display->bti);
458        zx_handle_close(display->vsync_interrupt);
459        zx_handle_close(display->inth);
460        free(display->p);
461    }
462    free(display);
463}
464
465static void display_unbind(void* ctx) {
466    vim2_display_t* display = static_cast<vim2_display_t*>(ctx);
467    vim2_audio_shutdown(&display->audio);
468    device_remove(display->mydevice);
469}
470
471static zx_status_t display_get_protocol(void* ctx, uint32_t proto_id, void* protocol) {
472    if (proto_id == ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL) {
473        auto ops = static_cast<display_controller_protocol_t*>(protocol);
474        ops->ctx = ctx;
475        ops->ops = &display_controller_ops;
476    } else if (proto_id == ZX_PROTOCOL_I2C_IMPL) {
477        auto ops = static_cast<i2c_impl_protocol_t*>(protocol);
478        ops->ctx = ctx;
479        ops->ops = &i2c_impl_ops;
480    } else {
481        return ZX_ERR_NOT_SUPPORTED;
482    }
483    return ZX_OK;
484}
485
486static zx_protocol_device_t main_device_proto = {
487    .version = DEVICE_OPS_VERSION,
488    .get_protocol = display_get_protocol,
489    .open = nullptr,
490    .open_at = nullptr,
491    .close = nullptr,
492    .unbind = display_unbind,
493    .release =  display_release,
494    .read = nullptr,
495    .write = nullptr,
496    .get_size = nullptr,
497    .ioctl = nullptr,
498    .suspend = nullptr,
499    .resume = nullptr,
500    .rxrpc = nullptr,
501    .message = nullptr,
502};
503
504static int hdmi_irq_handler(void *arg) {
505    vim2_display_t* display = static_cast<vim2_display_t*>(arg);
506    zx_status_t status;
507    while(1) {
508        status = zx_interrupt_wait(display->inth, NULL);
509        if (status != ZX_OK) {
510            DISP_ERROR("Waiting in Interrupt failed %d\n", status);
511            return -1;
512        }
513        usleep(500000);
514        uint8_t hpd;
515        status = gpio_read(&display->gpio, &hpd);
516        if (status != ZX_OK) {
517            DISP_ERROR("gpio_read failed HDMI HPD\n");
518            continue;
519        }
520
521        mtx_lock(&display->display_lock);
522
523        bool display_added = false;
524        added_display_args_t args;
525        uint64_t display_removed = INVALID_DISPLAY_ID;
526        if (hpd && !display->display_attached) {
527            DISP_ERROR("Display is connected\n");
528
529            display->display_attached = true;
530            memset(&display->cur_display_mode, 0, sizeof(display_mode_t));
531            populate_added_display_args(display, &args);
532            display_added = true;
533            gpio_set_polarity(&display->gpio, GPIO_POLARITY_LOW);
534        } else if (!hpd && display->display_attached) {
535            DISP_ERROR("Display Disconnected!\n");
536            hdmi_shutdown(display);
537
538            display_removed = display->display_id;
539            display->display_id++;
540            display->display_attached = false;
541
542            gpio_set_polarity(&display->gpio, GPIO_POLARITY_HIGH);
543        }
544
545        if (display->dc_cb &&
546                (display_removed != INVALID_DISPLAY_ID || display_added)) {
547            display->dc_cb->on_displays_changed(display->dc_cb_ctx,
548                                                &args,
549                                                display_added ? 1 : 0,
550                                                &display_removed,
551                                                display_removed != INVALID_DISPLAY_ID);
552            if (display_added) {
553                // See if we need to change output color to RGB
554                if (args.is_standard_srgb_out) {
555                    display->output_color_format = HDMI_COLOR_FORMAT_RGB;
556                } else {
557                    display->output_color_format = HDMI_COLOR_FORMAT_444;
558                }
559                display->audio_format_count = args.audio_format_count;
560
561                display->manufacturer_name = args.manufacturer_name;
562                memcpy(display->monitor_name, args.monitor_name, sizeof(args.monitor_name));
563                memcpy(display->monitor_serial, args.monitor_serial, sizeof(args.monitor_serial));
564                static_assert(sizeof(display->monitor_name) == sizeof(args.monitor_name), "");
565                static_assert(sizeof(display->monitor_serial) == sizeof(args.monitor_serial), "");
566            }
567        }
568
569        mtx_unlock(&display->display_lock);
570
571        if (display_removed != INVALID_DISPLAY_ID) {
572            vim2_audio_on_display_removed(display, display_removed);
573        }
574
575        if (display_added && args.audio_format_count) {
576            vim2_audio_on_display_added(display, display->display_id);
577        }
578    }
579}
580
581static int vsync_thread(void *arg)
582{
583    vim2_display_t* display = static_cast<vim2_display_t*>(arg);
584
585    for (;;) {
586        zx_status_t status;
587        zx_time_t timestamp;
588        status = zx_interrupt_wait(display->vsync_interrupt, &timestamp);
589        if (status != ZX_OK) {
590            DISP_INFO("Vsync wait failed");
591            break;
592        }
593
594        mtx_lock(&display->display_lock);
595
596        uint64_t display_id = display->display_id;
597        bool attached = display->display_attached;
598        void* live[2] = {};
599        uint32_t current_image_count = 0;
600        if (display->current_image_valid) {
601            live[current_image_count++] = (void*)(uint64_t)display->current_image;
602        }
603        if (display->vd1_image_valid) {
604            live[current_image_count++] = (void*)(uint64_t)display->vd1_image;
605        }
606
607        if (display->dc_cb && attached) {
608            display->dc_cb->on_display_vsync(display->dc_cb_ctx, display_id, timestamp, live,
609                                             current_image_count);
610        }
611        mtx_unlock(&display->display_lock);
612    }
613
614    return 0;
615}
616
617zx_status_t vim2_display_bind(void* ctx, zx_device_t* parent) {
618    vim2_display_t* display = static_cast<vim2_display_t*>(calloc(1, sizeof(vim2_display_t)));
619    if (!display) {
620        DISP_ERROR("Could not allocated display structure\n");
621        return ZX_ERR_NO_MEMORY;
622    }
623
624    zx_status_t status = ZX_ERR_INTERNAL;
625    display->parent = parent;
626
627    // If anything goes wrong from here on out, make sure to log the status code
628    // of the error, and destroy our display object.
629    auto cleanup = fbl::MakeAutoCall([&]() {
630        DISP_ERROR("bind failed! %d\n", status);
631        display_release(display);
632    });
633
634    status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &display->pdev);
635    if (status !=  ZX_OK) {
636        DISP_ERROR("Could not get parent protocol\n");
637        return status;
638    }
639
640    // Test for platform device get_board_info support.
641    pdev_board_info_t board_info;
642    pdev_get_board_info(&display->pdev, &board_info);
643    printf("BOARD INFO: %d %d %s %d\n", board_info.vid, board_info.pid, board_info.board_name,
644           board_info.board_revision);
645    assert(board_info.vid == PDEV_VID_KHADAS);
646    assert(board_info.pid == PDEV_PID_VIM2);
647    assert(!strcmp(board_info.board_name, "vim2"));
648    assert(board_info.board_revision == 1234);
649
650    // Fetch the device info and sanity check our resource counts.
651    pdev_device_info_t dev_info;
652    status = pdev_get_device_info(&display->pdev, &dev_info);
653    if (status != ZX_OK) {
654        DISP_ERROR("Failed to fetch device info (status %d)\n", status);
655        return status;
656    }
657
658    if (dev_info.mmio_count != MMIO_COUNT) {
659        DISP_ERROR("MMIO region count mismatch!  Expected %u regions to be supplied by board "
660                   "driver, but only %u were passed\n", MMIO_COUNT, dev_info.mmio_count);
661        return status;
662    }
663
664    if (dev_info.bti_count != BTI_COUNT) {
665        DISP_ERROR("BTI count mismatch!  Expected %u BTIs to be supplied by board "
666                   "driver, but only %u were passed\n", BTI_COUNT, dev_info.bti_count);
667        return status;
668    }
669
670    status = pdev_get_bti(&display->pdev, 0, &display->bti);
671    if (status != ZX_OK) {
672        DISP_ERROR("Could not get BTI handle\n");
673        return status;
674    }
675
676    status = device_get_protocol(parent, ZX_PROTOCOL_GPIO, &display->gpio);
677    if (status != ZX_OK) {
678        DISP_ERROR("Could not get Display GPIO protocol\n");
679        return status;
680    }
681
682    status = device_get_protocol(parent, ZX_PROTOCOL_AMLOGIC_CANVAS, &display->canvas);
683    if (status != ZX_OK) {
684        DISP_ERROR("Could not get Display CANVAS protocol\n");
685        return status;
686    }
687
688    // Map all the various MMIOs
689    status = pdev_map_mmio_buffer(&display->pdev, MMIO_PRESET, ZX_CACHE_POLICY_UNCACHED_DEVICE,
690        &display->mmio_preset);
691    if (status != ZX_OK) {
692        DISP_ERROR("Could not map display MMIO PRESET\n");
693        return status;
694    }
695
696    status = pdev_map_mmio_buffer(&display->pdev, MMIO_HDMITX, ZX_CACHE_POLICY_UNCACHED_DEVICE,
697        &display->mmio_hdmitx);
698    if (status != ZX_OK) {
699        DISP_ERROR("Could not map display MMIO HDMITX\n");
700        return status;
701    }
702
703    status = pdev_map_mmio_buffer(&display->pdev, MMIO_HIU, ZX_CACHE_POLICY_UNCACHED_DEVICE,
704        &display->mmio_hiu);
705    if (status != ZX_OK) {
706        DISP_ERROR("Could not map display MMIO HIU\n");
707        return status;
708    }
709
710    status = pdev_map_mmio_buffer(&display->pdev, MMIO_VPU, ZX_CACHE_POLICY_UNCACHED_DEVICE,
711        &display->mmio_vpu);
712    if (status != ZX_OK) {
713        DISP_ERROR("Could not map display MMIO VPU\n");
714        return status;
715    }
716
717    status = pdev_map_mmio_buffer(&display->pdev, MMIO_HDMTX_SEC, ZX_CACHE_POLICY_UNCACHED_DEVICE,
718        &display->mmio_hdmitx_sec);
719    if (status != ZX_OK) {
720        DISP_ERROR("Could not map display MMIO HDMITX SEC\n");
721        return status;
722    }
723
724    status = pdev_map_mmio_buffer(&display->pdev, MMIO_CBUS, ZX_CACHE_POLICY_UNCACHED_DEVICE,
725        &display->mmio_cbus);
726    if (status != ZX_OK) {
727        DISP_ERROR("Could not map display MMIO CBUS\n");
728        return status;
729    }
730
731    status = gpio_config_in(&display->gpio, GPIO_PULL_DOWN);
732    if (status != ZX_OK) {
733        DISP_ERROR("gpio_config_in failed for gpio\n");
734        return status;
735    }
736
737    status = gpio_get_interrupt(&display->gpio, ZX_INTERRUPT_MODE_LEVEL_HIGH, &display->inth);
738    if (status != ZX_OK) {
739        DISP_ERROR("gpio_get_interrupt failed for gpio\n");
740        return status;
741    }
742
743    status = pdev_map_interrupt(&display->pdev, 0, &display->vsync_interrupt);
744    if (status != ZX_OK) {
745        DISP_ERROR("Could not map vsync interrupt\n");
746        return status;
747    }
748
749    status = vim2_audio_create(&display->pdev, &display->audio);
750    if (status != ZX_OK) {
751        DISP_ERROR("Failed to create DAI controller (status %d)\n", status);
752        return status;
753    }
754
755    // For some reason the vsync interrupt enable bit needs to be cleared for
756    // vsync interrupts to occur at the correct rate.
757    *((uint32_t*)(static_cast<uint8_t*>(display->mmio_vpu.virt) + VPU_VIU_MISC_CTRL0)) &= ~(1 << 8);
758
759    fbl::AllocChecker ac;
760    display->p = new(&ac) struct hdmi_param();
761    if (!ac.check()) {
762        DISP_ERROR("Could not allocated hdmi param structure\n");
763        DISP_ERROR("bind failed! %d\n", status);
764        display_release(display);
765        return status;
766    }
767
768    // initialize HDMI
769    display->input_color_format = _ginput_color_format;
770    display->color_depth = _gcolor_depth;
771    init_hdmi_hardware(display);
772
773    device_add_args_t add_args = {
774        .version = DEVICE_ADD_ARGS_VERSION,
775        .name = "vim2-display",
776        .ctx = display,
777        .ops = &main_device_proto,
778        .props = nullptr,
779        .prop_count = 0,
780        .proto_id = ZX_PROTOCOL_DISPLAY_CONTROLLER_IMPL,
781        .proto_ops = &display_controller_ops,
782        .proxy_args = nullptr,
783        .flags = 0,
784    };
785
786    status = device_add(display->parent, &add_args, &display->mydevice);
787    if (status != ZX_OK) {
788        DISP_ERROR("Could not add device\n");
789        return status;
790    }
791
792    display->display_id = 1;
793    display->display_attached = false;
794    list_initialize(&display->imported_images);
795    mtx_init(&display->display_lock, mtx_plain);
796    mtx_init(&display->image_lock, mtx_plain);
797    mtx_init(&display->i2c_lock, mtx_plain);
798
799    thrd_create_with_name(&display->main_thread, hdmi_irq_handler, display, "hdmi_irq_handler");
800    thrd_create_with_name(&display->vsync_thread, vsync_thread, display, "vsync_thread");
801
802    // Things went well!  Cancel our cleanup auto call
803    cleanup.cancel();
804    return ZX_OK;
805}
806
807zx_status_t vim2_display_configure_audio_mode(const vim2_display_t* display,
808                                              uint32_t N,
809                                              uint32_t CTS,
810                                              uint32_t frame_rate,
811                                              uint32_t bits_per_sample) {
812    ZX_DEBUG_ASSERT(display != NULL);
813
814    if ((N > 0xFFFFF) || (CTS > 0xFFFFF) || (bits_per_sample < 16) || (bits_per_sample > 24)) {
815        vim2_display_disable_audio(display);
816        return ZX_ERR_INVALID_ARGS;
817    }
818
819    ZX_DEBUG_ASSERT(display != NULL);
820    hdmitx_writereg(display, HDMITX_DWC_AUD_CONF0,  0u);  // Make sure that I2S is deselected
821    hdmitx_writereg(display, HDMITX_DWC_AUD_SPDIF2, 0u);  // Deselect SPDIF
822
823    // Select non-HBR linear PCM, as well as the proper number of bits per sample.
824    hdmitx_writereg(display, HDMITX_DWC_AUD_SPDIF1, bits_per_sample);
825
826    // Set the N/CTS parameters using DesignWare's atomic update sequence
827    //
828    // For details, refer to...
829    // DesignWare Cores HDMI Transmitter Controler Databook v2.12a Sections 6.8.3 Table 6-282
830    hdmitx_writereg(display, HDMITX_DWC_AUD_N3,
831                   ((N >> AUD_N3_N_START_BIT) & AUD_N3_N_MASK) | AUD_N3_ATOMIC_WRITE);
832    hw_wmb();
833    hdmitx_writereg(display, HDMITX_DWC_AUD_CTS3,
834                   ((CTS >> AUD_CTS3_CTS_START_BIT) & AUD_CTS3_CTS_MASK));
835    hdmitx_writereg(display, HDMITX_DWC_AUD_CTS2,
836                   ((CTS >> AUD_CTS2_CTS_START_BIT) & AUD_CTS2_CTS_MASK));
837    hdmitx_writereg(display, HDMITX_DWC_AUD_CTS1,
838                   ((CTS >> AUD_CTS1_CTS_START_BIT) & AUD_CTS1_CTS_MASK));
839    hdmitx_writereg(display, HDMITX_DWC_AUD_N3,
840                   ((N >> AUD_N3_N_START_BIT) & AUD_N3_N_MASK) | AUD_N3_ATOMIC_WRITE);
841    hdmitx_writereg(display, HDMITX_DWC_AUD_N2, ((N >> AUD_N2_N_START_BIT) & AUD_N2_N_MASK));
842    hw_wmb();
843    hdmitx_writereg(display, HDMITX_DWC_AUD_N1, ((N >> AUD_N1_N_START_BIT) & AUD_N1_N_MASK));
844
845    // Select SPDIF data stream 0 (coming from the AmLogic section of the S912)
846    hdmitx_writereg(display, HDMITX_DWC_AUD_SPDIF2, AUD_SPDIF2_ENB_ISPDIFDATA0);
847
848    // Reset the SPDIF FIFO
849    hdmitx_writereg(display, HDMITX_DWC_AUD_SPDIF0, AUD_SPDIF0_SW_FIFO_RESET);
850    hw_wmb();
851
852    // Now, as required, reset the SPDIF sampler.
853    // See Section 6.9.1 of the DW HDMT TX controller databook
854    hdmitx_writereg(display, HDMITX_DWC_MC_SWRSTZREQ, 0xEF);
855    hw_wmb();
856
857    // Set up the audio infoframe.  Refer to the follow specifications for
858    // details about how to do this.
859    //
860    // DesignWare Cores HDMI Transmitter Controler Databook v2.12a Sections 6.5.35 - 6.5.37
861    // CTA-861-G Section 6.6
862
863    uint32_t CT = 0x01;   // Coding type == LPCM
864    uint32_t CC = 0x01;   // Channel count = 2
865    uint32_t CA = 0x00;   // Channel allocation; currently we hardcode FL/FR
866
867    // Sample size
868    uint32_t SS;
869    switch (bits_per_sample) {
870    case 16: SS = 0x01; break;
871    case 20: SS = 0x02; break;
872    case 24: SS = 0x03; break;
873    default: SS = 0x00; break; // "refer to stream"
874    }
875
876    // Sample frequency
877    uint32_t SF;
878    switch (frame_rate) {
879    case 32000:  SF = 0x01; break;
880    case 44100:  SF = 0x02; break;
881    case 48000:  SF = 0x03; break;
882    case 88200:  SF = 0x04; break;
883    case 96000:  SF = 0x05; break;
884    case 176400: SF = 0x06; break;
885    case 192000: SF = 0x07; break;
886    default:     SF = 0x00; break; // "refer to stream"
887    }
888
889    hdmitx_writereg(display, HDMITX_DWC_FC_AUDICONF0, (CT & 0xF) | ((CC & 0x7) << 4));
890    hdmitx_writereg(display, HDMITX_DWC_FC_AUDICONF1, (SF & 0x7) | ((SS & 0x3) << 4));
891    hdmitx_writereg(display, HDMITX_DWC_FC_AUDICONF2, CA);
892    // Right now, we just hardcode the following...
893    // LSV    : Level shift value == 0dB
894    // DM_INH : Downmix inhibit == down-mixing permitted.
895    // LFEPBL : LFE playback level unknown.
896    hdmitx_writereg(display, HDMITX_DWC_FC_AUDICONF3, 0u);
897
898    return ZX_OK;
899}
900
901void vim2_display_disable_audio(const vim2_display_t* display) {
902    ZX_DEBUG_ASSERT(display != NULL);
903    hdmitx_writereg(display, HDMITX_DWC_AUD_CONF0,  0u);  // Deselect I2S
904    hdmitx_writereg(display, HDMITX_DWC_AUD_SPDIF2, 0u);  // Deselect SPDIF
905
906    // Set the N/CTS parameters to 0 using DesignWare's atomic update sequence
907    hdmitx_writereg(display, HDMITX_DWC_AUD_N3, 0x80);
908    hdmitx_writereg(display, HDMITX_DWC_AUD_CTS3, 0u);
909    hdmitx_writereg(display, HDMITX_DWC_AUD_CTS2, 0u);
910    hdmitx_writereg(display, HDMITX_DWC_AUD_CTS1, 0u);
911    hdmitx_writereg(display, HDMITX_DWC_AUD_N3, 0x80);
912    hdmitx_writereg(display, HDMITX_DWC_AUD_N2, 0u);
913    hw_wmb();
914    hdmitx_writereg(display, HDMITX_DWC_AUD_N1, 0u);
915
916    // Reset the audio info frame to defaults.
917    hdmitx_writereg(display, HDMITX_DWC_FC_AUDICONF0, 0u);
918    hdmitx_writereg(display, HDMITX_DWC_FC_AUDICONF1, 0u);
919    hdmitx_writereg(display, HDMITX_DWC_FC_AUDICONF2, 0u);
920    hdmitx_writereg(display, HDMITX_DWC_FC_AUDICONF3, 0u);
921}
922
923static zx_driver_ops_t vim2_display_driver_ops = {
924    .version = DRIVER_OPS_VERSION,
925    .init = nullptr,
926    .bind = vim2_display_bind,
927    .create = nullptr,
928    .release = nullptr,
929};
930
931ZIRCON_DRIVER_BEGIN(vim2_display, vim2_display_driver_ops, "zircon", "0.1", 4)
932    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_DEV),
933    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_KHADAS),
934    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_VIM2),
935    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_VIM_DISPLAY),
936ZIRCON_DRIVER_END(vim_2display)
937