| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #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/platform-defs.h> |
| #include <ddk/protocol/platform/device.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "hdmitx.h" |
| #include "registers.h" |
| #include "vim-display.h" |
| #include <hw/reg.h> |
| #include <zircon/assert.h> |
| #include <zircon/syscalls.h> |
| |
| enum { |
| IDX_OSD1_START, // 0 |
| OSD1_IDX_CFG_W0 = IDX_OSD1_START, // 0 |
| OSD1_IDX_CTRL_STAT, // 1 |
| OSD1_IDX_MISC, // 2 |
| IDX_OSD1_END, // 3 |
| |
| IDX_OSD2_START = IDX_OSD1_END, // 3 |
| OSD2_IDX_CFG_W0 = IDX_OSD2_START, // 3 |
| OSD2_IDX_CTRL_STAT, // 4 |
| OSD2_IDX_MISC, // 5 |
| IDX_OSD2_END, // 6 |
| |
| IDX_VD1_START = IDX_OSD2_END, // 6 |
| VD1_IDX_IF_GEN = IDX_VD1_START, // 6 |
| VD1_IDX_IF_CANVAS, // 7 |
| VD1_IDX_IF_MISC, // 8 |
| IDX_VD1_END, // 9 |
| |
| IDX_VD2_START = IDX_VD1_END, // 9 |
| VD2_IDX_IF_GEN = IDX_VD2_START, // 9 |
| VD2_IDX_IF_CANVAS, // 10 |
| VD2_IDX_IF_MISC, // 11 |
| IDX_VD2_END, // 12 |
| |
| IDX_MAX = IDX_VD2_END, // 12 |
| }; |
| |
| constexpr uint32_t kRdmaTableMaxIndex = IDX_MAX; |
| |
| #define OSD_INDEX_START(x) (IDX_OSD1_START + (x * 3)) |
| #define OSD_INDEX_END(x) (IDX_OSD1_END + (x * 3)) |
| #define VD_INDEX_START(x) (IDX_VD1_START + (x * 3)) |
| #define VD_INDEX_END(x) (IDX_VD1_END + (x * 3)) |
| #define INDEX_START(x) (x * 3) |
| |
| void reset_rdma_table(vim2_display_t* display); |
| void set_rdma_table_value(vim2_display_t* display, uint32_t channel, uint32_t idx, uint32_t val); |
| void flush_rdma_table(vim2_display_t* display, uint32_t channel); |
| zx_status_t setup_rdma(vim2_display_t* display); |
| int get_next_avail_rdma_channel(vim2_display_t* display); |
| |
| int rdma_thread(void *arg) { |
| zx_status_t status; |
| vim2_display_t* display = static_cast<vim2_display_t*>(arg); |
| for (;;) { |
| status = zx_interrupt_wait(display->rdma_interrupt, nullptr); |
| if (status != ZX_OK) { |
| DISP_ERROR("RDMA Interrupt wait failed\n"); |
| break; |
| } |
| // RDMA completed. Remove source for all finished DMA channels |
| for (int i = 0; i < kMaxRdmaChannels; i++) { |
| if (display->mmio_vpu->Read32(VPU_RDMA_STATUS) & RDMA_STATUS_DONE(i)) { |
| uint32_t regVal = display->mmio_vpu->Read32(VPU_RDMA_ACCESS_AUTO); |
| regVal &= ~RDMA_ACCESS_AUTO_INT_EN(i); // VSYNC interrupt source |
| display->mmio_vpu->Write32(regVal, VPU_RDMA_ACCESS_AUTO); |
| // clear interrupts |
| display->mmio_vpu->Write32(display->mmio_vpu->Read32(VPU_RDMA_CTRL) | |
| RDMA_CTRL_INT_DONE(i), VPU_RDMA_CTRL); |
| display->rdma_container.rdma_chnl_container[i].active = false; |
| } |
| } |
| } |
| |
| return status; |
| } |
| |
| void osd_debug_dump_register_all(vim2_display_t* display) { |
| uint32_t reg = 0; |
| uint32_t offset = 0; |
| uint32_t index = 0; |
| |
| reg = VPU_VPU_VIU_VENC_MUX_CTRL; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = VPU_VPP_MISC; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = VPU_VPP_OFIFO_SIZE; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = VPU_VPP_HOLD_LINES; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = VPU_VPP_OSD_SC_CTRL0; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = VPU_VPP_OSD_SCI_WH_M1; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = VPU_VPP_OSD_SCO_H_START_END; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = VPU_VPP_OSD_SCO_V_START_END; |
| DISP_INFO("reg[0x%x]: 0x%08x\n\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = VPU_VPP_POSTBLEND_H_SIZE; |
| DISP_INFO("reg[0x%x]: 0x%08x\n\n", (reg >> 2), READ32_VPU_REG(reg)); |
| |
| for (index = 0; index < 2; index++) { |
| if (index == 1) |
| offset = (0x20 << 2); |
| reg = offset + VPU_VIU_OSD1_FIFO_CTRL_STAT; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = offset + VPU_VIU_OSD1_CTRL_STAT; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = offset + VPU_VIU_OSD1_BLK0_CFG_W0; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = offset + VPU_VIU_OSD1_BLK0_CFG_W1; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = offset + VPU_VIU_OSD1_BLK0_CFG_W2; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = offset + VPU_VIU_OSD1_BLK0_CFG_W3; |
| DISP_INFO("reg[0x%x]: 0x%08x\n", (reg >> 2), READ32_VPU_REG(reg)); |
| reg = VPU_VIU_OSD1_BLK0_CFG_W4; |
| if (index == 1) |
| reg = VPU_VIU_OSD2_BLK0_CFG_W4; |
| DISP_INFO("reg[0x%x]: 0x%08x\n\n", (reg >> 2), READ32_VPU_REG(reg)); |
| } |
| } |
| |
| void disable_vd(vim2_display* display, uint32_t vd_index) { |
| display->vd1_image_valid = false; |
| auto* const vpu = &*display->mmio_vpu; |
| registers::Vd(vd_index).IfGenReg().ReadFrom(vpu).set_enable(false).WriteTo(vpu); |
| registers::VpuVppMisc::Get() |
| .ReadFrom(vpu) |
| .set_vd1_enable_postblend(false) |
| .WriteTo(vpu); |
| } |
| |
| void configure_vd(vim2_display* display, uint32_t vd_index) { |
| disable_vd(display, vd_index); |
| auto* const vpu = &*display->mmio_vpu; |
| uint32_t x_start, x_end, y_start, y_end; |
| x_start = y_start = 0; |
| x_end = display->cur_display_mode.h_addressable - 1; |
| y_end = display->cur_display_mode.v_addressable - 1; |
| |
| auto vd = registers::Vd(vd_index); |
| vd.IfLumaX0().FromValue(0).set_end(x_end).set_start(x_start).WriteTo(vpu); |
| vd.IfLumaY0().FromValue(0).set_end(y_end).set_start(y_start).WriteTo(vpu); |
| vd.IfChromaX0().FromValue(0).set_end(x_end / 2).set_start(x_start / 2).WriteTo(vpu); |
| vd.IfChromaY0().FromValue(0).set_end(y_end / 2).set_start(y_start / 2).WriteTo(vpu); |
| vd.IfGenReg2().FromValue(0).set_color_map(1).WriteTo(vpu); |
| vd.FmtCtrl() |
| .FromValue(0) |
| .set_vertical_enable(true) |
| .set_vertical_phase_step(8) |
| .set_vertical_initial_phase(0xc) |
| .set_vertical_repeat_line0(true) |
| .set_horizontal_enable(true) |
| .set_horizontal_yc_ratio(1) |
| .WriteTo(vpu); |
| vd.FmtW() |
| .FromValue(0) |
| .set_horizontal_width(display->cur_display_mode.h_addressable) |
| .set_vertical_width(display->cur_display_mode.h_addressable / 2) |
| .WriteTo(vpu); |
| |
| vd.IfRptLoop().FromValue(0).WriteTo(vpu); |
| vd.IfLuma0RptPat().FromValue(0).WriteTo(vpu); |
| vd.IfChroma0RptPat().FromValue(0).WriteTo(vpu); |
| vd.IfLumaPsel().FromValue(0).WriteTo(vpu); |
| vd.IfChromaPsel().FromValue(0).WriteTo(vpu); |
| } |
| |
| void flip_vd(vim2_display* display, uint32_t vd_index, uint32_t index) { |
| display->vd1_image_valid = true; |
| display->vd1_image = index; |
| auto* const vpu = &*display->mmio_vpu; |
| auto vd = registers::Vd(vd_index); |
| |
| // Get the first available channel |
| int rdma_channel = get_next_avail_rdma_channel(display); |
| if (rdma_channel < 0) { |
| ZX_DEBUG_ASSERT(false); |
| return; |
| } |
| |
| display->rdma_container.rdma_chnl_container[rdma_channel].active = true; |
| DISP_SPEW("Channel used is %d, idx = %d\n", rdma_channel, index); |
| |
| set_rdma_table_value(display, rdma_channel, |
| INDEX_START(vd_index) + VD1_IDX_IF_GEN, vd.IfGenReg() |
| .FromValue(0) |
| .set_enable(true) |
| .set_separate_en(true) |
| .set_chro_rpt_lastl_ctrl(true) |
| .set_hold_lines(3) |
| .set_urgent_luma(true) |
| .set_urgent_chroma(true) |
| .reg_value()); |
| set_rdma_table_value(display, rdma_channel, |
| INDEX_START(vd_index) + VD1_IDX_IF_CANVAS, vd.IfCanvas0() |
| .FromValue(index).reg_value()); |
| set_rdma_table_value(display, rdma_channel, INDEX_START(vd_index) + VD1_IDX_IF_MISC, |
| registers::VpuVppMisc::Get() |
| .ReadFrom(vpu) |
| .set_vd1_enable_postblend(true) |
| .reg_value()); |
| |
| flush_rdma_table(display, rdma_channel); |
| |
| // Write the start and end address of the table. End address is the last address that the |
| // RDMA engine reads from |
| zx_paddr_t phys = display->rdma_container.rdma_chnl_container[rdma_channel].phys_offset; |
| display->mmio_vpu->Write32(static_cast<uint32_t>(phys + |
| VD_INDEX_START(vd_index) * |
| sizeof(RdmaTable)), |
| VPU_RDMA_AHB_START_ADDR(rdma_channel)); |
| display->mmio_vpu->Write32(static_cast<uint32_t>(phys + |
| (VD_INDEX_END(vd_index) * |
| sizeof(RdmaTable) - 4)), |
| VPU_RDMA_AHB_END_ADDR(rdma_channel)); |
| |
| // Enable Auto mode: Non-Increment, VSync Interrupt Driven, Write |
| uint32_t regVal = vpu->Read32(VPU_RDMA_ACCESS_AUTO); |
| regVal = RDMA_ACCESS_AUTO_INT_EN(rdma_channel); // VSYNC interrupt source |
| regVal |= RDMA_ACCESS_AUTO_WRITE(rdma_channel); // Write |
| vpu->Write32(regVal, VPU_RDMA_ACCESS_AUTO); |
| } |
| |
| void disable_osd(vim2_display_t* display, uint32_t osd_index) { |
| display->current_image_valid = false; |
| auto* const vpu = &*display->mmio_vpu; |
| auto osd = registers::Osd(osd_index); |
| osd |
| .CtrlStat() |
| .ReadFrom(vpu) |
| .set_osd_blk_enable(false) |
| .WriteTo(vpu); |
| if (osd_index == 0) { |
| registers::VpuVppMisc::Get() |
| .ReadFrom(vpu) |
| .set_osd1_enable_postblend(false) |
| .WriteTo(vpu); |
| } else { |
| registers::VpuVppMisc::Get() |
| .ReadFrom(vpu) |
| .set_osd2_enable_postblend(false) |
| .WriteTo(vpu); |
| } |
| } |
| |
| // Disables the OSD until a flip happens |
| zx_status_t configure_osd(vim2_display_t* display, uint32_t osd_index) { |
| uint32_t x_start, x_end, y_start, y_end; |
| x_start = y_start = 0; |
| x_end = display->cur_display_mode.h_addressable - 1; |
| y_end = display->cur_display_mode.v_addressable - 1; |
| |
| disable_osd(display, osd_index); |
| auto* const vpu = &*display->mmio_vpu; |
| auto osd = registers::Osd(osd_index); |
| registers::VpuVppOsdScCtrl0::Get().FromValue(0).WriteTo(vpu); |
| |
| osd.CtrlStat2() |
| .ReadFrom(vpu) |
| .set_replaced_alpha_en(true) |
| .set_replaced_alpha(0xff) |
| .WriteTo(vpu); |
| |
| osd.Blk0CfgW1() |
| .FromValue(0) |
| .set_virtual_canvas_x_end(x_end) |
| .set_virtual_canvas_x_start(x_start) |
| .WriteTo(vpu); |
| osd.Blk0CfgW2() |
| .FromValue(0) |
| .set_virtual_canvas_y_end(y_end) |
| .set_virtual_canvas_y_start(y_start) |
| .WriteTo(vpu); |
| osd.Blk0CfgW3().FromValue(0).set_display_h_end(x_end).set_display_h_start(x_start).WriteTo( |
| vpu); |
| osd.Blk0CfgW4().FromValue(0).set_display_v_end(y_end).set_display_v_start(y_start).WriteTo( |
| vpu); |
| |
| registers::VpuVppOsdScoHStartEnd::Get().FromValue(0).WriteTo(vpu); |
| registers::VpuVppOsdScoVStartEnd::Get().FromValue(0).WriteTo(vpu); |
| |
| registers::VpuVppPostblendHSize::Get() |
| .FromValue(display->cur_display_mode.h_addressable) |
| .WriteTo(vpu); |
| registers::VpuVppOsdSciWhM1::Get().FromValue(0).WriteTo(vpu); |
| |
| return ZX_OK; |
| } |
| |
| void flip_osd(vim2_display_t* display, uint32_t osd_index, uint8_t idx) { |
| display->current_image = idx; |
| display->current_image_valid = true; |
| auto* const vpu = &*display->mmio_vpu; |
| auto osd = registers::Osd(osd_index); |
| |
| // Get the first available channel |
| int rdma_channel = get_next_avail_rdma_channel(display); |
| if (rdma_channel < 0) { |
| ZX_DEBUG_ASSERT(false); |
| return; |
| } |
| display->rdma_container.rdma_chnl_container[rdma_channel].active = true; |
| DISP_SPEW("Channel used is %d, idx = %d\n", rdma_channel, idx); |
| |
| set_rdma_table_value(display, rdma_channel, |
| INDEX_START(osd_index) + OSD1_IDX_CFG_W0, osd.Blk0CfgW0() |
| .FromValue(0) |
| .set_tbl_addr(idx) |
| .set_little_endian(true) |
| .set_block_mode(registers::VpuViuOsdBlk0CfgW0::kBlockMode32Bit) |
| .set_rgb_en(true) |
| .set_color_matrix(registers::VpuViuOsdBlk0CfgW0::kColorMatrixARGB8888) |
| .reg_value()); |
| |
| set_rdma_table_value(display, rdma_channel, |
| INDEX_START(osd_index) + OSD1_IDX_CTRL_STAT, osd.CtrlStat() |
| .ReadFrom(vpu) |
| .set_osd_blk_enable(true) |
| .reg_value()); |
| if (osd_index == 0) { |
| set_rdma_table_value(display, rdma_channel, INDEX_START(osd_index) + OSD1_IDX_MISC, |
| registers::VpuVppMisc::Get() |
| .ReadFrom(vpu) |
| .set_osd1_enable_postblend(true) |
| .reg_value()); |
| } else { |
| set_rdma_table_value(display, rdma_channel, INDEX_START(osd_index) + OSD1_IDX_MISC, |
| registers::VpuVppMisc::Get() |
| .ReadFrom(vpu) |
| .set_osd2_enable_postblend(true) |
| .reg_value()); |
| } |
| |
| flush_rdma_table(display, rdma_channel); |
| |
| // Write the start and end address of the table. End address is the last address that the |
| // RDMA engine reads from |
| zx_paddr_t phys = display->rdma_container.rdma_chnl_container[rdma_channel].phys_offset; |
| display->mmio_vpu->Write32(static_cast<uint32_t>(phys + |
| OSD_INDEX_START(osd_index) * |
| sizeof(RdmaTable)), |
| VPU_RDMA_AHB_START_ADDR(rdma_channel)); |
| display->mmio_vpu->Write32(static_cast<uint32_t>(phys + |
| (OSD_INDEX_END(osd_index) * |
| sizeof(RdmaTable) - 4)), |
| VPU_RDMA_AHB_END_ADDR(rdma_channel)); |
| |
| // Enable Auto mode: Non-Increment, VSync Interrupt Driven, Write |
| uint32_t regVal = vpu->Read32(VPU_RDMA_ACCESS_AUTO); |
| regVal |= RDMA_ACCESS_AUTO_INT_EN(rdma_channel); // VSYNC interrupt source |
| regVal |= RDMA_ACCESS_AUTO_WRITE(rdma_channel); // Write |
| vpu->Write32(regVal, VPU_RDMA_ACCESS_AUTO); |
| } |
| |
| void reset_rdma_table(vim2_display_t* display) { |
| |
| for (int i = 0; i < kMaxRdmaChannels; i++) { |
| // Setup RDMA Table Register values |
| uint8_t* virt_offset = display->rdma_container.rdma_chnl_container[i].virt_offset; |
| RdmaTable* rdma_table = reinterpret_cast<RdmaTable*>(virt_offset); |
| rdma_table[OSD1_IDX_CFG_W0].reg = (VPU_VIU_OSD1_BLK0_CFG_W0 >> 2); |
| rdma_table[OSD1_IDX_CTRL_STAT].reg = (VPU_VIU_OSD1_CTRL_STAT >> 2); |
| rdma_table[OSD1_IDX_MISC].reg = (VPU_VPP_MISC >> 2); |
| |
| rdma_table[OSD2_IDX_CFG_W0].reg = (VPU_VIU_OSD2_BLK0_CFG_W0 >> 2); |
| rdma_table[OSD2_IDX_CTRL_STAT].reg = (VPU_VIU_OSD2_CTRL_STAT >> 2); |
| rdma_table[OSD2_IDX_MISC].reg = (VPU_VPP_MISC >> 2); |
| |
| rdma_table[VD1_IDX_IF_GEN].reg = (VPU_VD1_IF0_GEN_REG >> 2); |
| rdma_table[VD1_IDX_IF_CANVAS].reg = (VPU_VD1_IF0_CANVAS0 >> 2); |
| rdma_table[VD1_IDX_IF_MISC].reg = (VPU_VPP_MISC >> 2); |
| |
| rdma_table[VD2_IDX_IF_GEN].reg = (VPU_VD2_IF0_GEN_REG >> 2); |
| rdma_table[VD2_IDX_IF_CANVAS].reg = (VPU_VD2_IF0_CANVAS0 >> 2); |
| rdma_table[VD2_IDX_IF_MISC].reg = (VPU_VPP_MISC >> 2); |
| } |
| } |
| |
| void set_rdma_table_value(vim2_display_t* display, uint32_t channel, uint32_t idx, uint32_t val) { |
| ZX_DEBUG_ASSERT(idx < kRdmaTableMaxIndex); |
| ZX_DEBUG_ASSERT(channel < kMaxRdmaChannels); |
| uint8_t* virt_offset = display->rdma_container.rdma_chnl_container[channel].virt_offset; |
| RdmaTable* rdma_table = reinterpret_cast<RdmaTable*>(virt_offset); |
| rdma_table[idx].val = val; |
| } |
| |
| int get_next_avail_rdma_channel(vim2_display_t* display) { |
| // The next RDMA channel is the one that is not being used by hardware |
| // A channel is considered available if it's not busy OR the done bit is set |
| uint8_t retry_count = 0; |
| while (retry_count++ < kMaxRetries) { |
| for (int i = 0; i < kMaxRdmaChannels; i++) { |
| if (!display->rdma_container.rdma_chnl_container[i].active) { |
| return i; |
| } |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(1))); |
| } |
| return -1; |
| } |
| |
| void flush_rdma_table(vim2_display_t* display, uint32_t channel) { |
| uint8_t* virt = display->rdma_container.rdma_chnl_container[channel].virt_offset; |
| zx_status_t status = zx_cache_flush(virt, sizeof(RdmaTable) * kRdmaTableMaxIndex, |
| ZX_CACHE_FLUSH_DATA | ZX_CACHE_FLUSH_INVALIDATE); |
| if (status != ZX_OK) { |
| DISP_ERROR("Could not clean cache %d\n", status); |
| return; |
| } |
| } |
| |
| zx_status_t setup_rdma(vim2_display_t* display) { |
| DISP_SPEW("Setting up RDMA\n"); |
| zx_status_t status; |
| |
| // since we are flushing the caches, make sure the tables are at least cache_line apart |
| ZX_DEBUG_ASSERT(kChannelBaseOffset > zx_system_get_dcache_line_size()); |
| static_assert((kMaxRdmaChannels * kChannelBaseOffset) < ZX_PAGE_SIZE); |
| |
| // Allocate one page for RDMA Table |
| status = zx_vmo_create(ZX_PAGE_SIZE, 0, &display->rdma_container.rdma_vmo); |
| if (status != ZX_OK) { |
| DISP_ERROR("Could not create RDMA VMO (%d)\n", status); |
| return status; |
| } |
| |
| status = zx_bti_pin(display->bti, ZX_BTI_PERM_READ | ZX_BTI_PERM_WRITE, |
| display->rdma_container.rdma_vmo, |
| 0, ZX_PAGE_SIZE, &display->rdma_container.rdma_phys, 1, |
| &display->rdma_container.rdma_pmt); |
| if (status != ZX_OK) { |
| DISP_ERROR("Could not pin RDMA VMO (%d)\n", status); |
| return status; |
| } |
| |
| status = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, |
| 0, display->rdma_container.rdma_vmo, 0, ZX_PAGE_SIZE, |
| reinterpret_cast<zx_vaddr_t*>(&display->rdma_container.rdma_vbuf)); |
| if (status != ZX_OK) { |
| DISP_ERROR("Could not map vmar (%d)\n", status); |
| return status; |
| } |
| |
| // Initialize each rdma channel container |
| for (int i = 0; i < kMaxRdmaChannels; i++) { |
| zx_paddr_t phys = display->rdma_container.rdma_phys + (i * kChannelBaseOffset); |
| uint8_t* virt = display->rdma_container.rdma_vbuf + (i * kChannelBaseOffset); |
| display->rdma_container.rdma_chnl_container[i].phys_offset = phys; |
| display->rdma_container.rdma_chnl_container[i].virt_offset = virt; |
| display->rdma_container.rdma_chnl_container[i].active = false; |
| } |
| |
| // Setup RDMA_CTRL: |
| // Default: no reset, no clock gating, burst size 4x16B for read and write |
| // DDR Read/Write request urgent |
| uint32_t regVal = RDMA_CTRL_READ_URGENT | RDMA_CTRL_WRITE_URGENT; |
| display->mmio_vpu->Write32(regVal, VPU_RDMA_CTRL); |
| |
| reset_rdma_table(display); |
| |
| return status; |
| } |
| |
| void release_osd(vim2_display_t* display) { |
| zx_interrupt_destroy(display->rdma_interrupt); |
| int res; |
| thrd_join(display->rdma_thread,&res); |
| zx_pmt_unpin(display->rdma_container.rdma_pmt); |
| zx_vmar_unmap(zx_vmar_root_self(), |
| reinterpret_cast<zx_vaddr_t>(display->rdma_container.rdma_vbuf), |
| ZX_PAGE_SIZE); |
| } |