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, ×tamp); 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