| /* |
| * Copyright 2023 Google LLC |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "sw/device/lib/spi_display/spi_display.h" |
| |
| #include "hw/ip/spi_host/data/spi_host_regs.h" // Generated. |
| #include "sw/device/lib/spi_display/DEV_Config.h" |
| #include "sw/device/lib/spi_display/LCD_Driver.h" |
| #include "sw/device/lib/util.h" |
| |
| dif_result_t spi_display_init(spi_display_t* spi_display, |
| dif_spi_host_t* spi_host, |
| dif_tlul_mailbox_t* mailbox) { |
| spi_display->spi_host = spi_host; |
| spi_display->mailbox = mailbox; |
| spi_display->gpio_write_done = false; |
| spi_display->spi_idle_seen = false; |
| |
| // Set the SPI_EVENT interrupt, and enable the idle event. |
| uint32_t intr_enable = |
| mmio_region_read32(spi_host->base_addr, SPI_HOST_INTR_ENABLE_REG_OFFSET); |
| intr_enable = |
| bitfield_bit32_write(intr_enable, SPI_HOST_INTR_ENABLE_SPI_EVENT_BIT, 1); |
| mmio_region_write32(spi_host->base_addr, SPI_HOST_INTR_ENABLE_REG_OFFSET, |
| intr_enable); |
| |
| uint32_t event_enable = |
| mmio_region_read32(spi_host->base_addr, SPI_HOST_EVENT_ENABLE_REG_OFFSET); |
| event_enable = |
| bitfield_bit32_write(event_enable, SPI_HOST_EVENT_ENABLE_IDLE_BIT, 1); |
| mmio_region_write32(spi_host->base_addr, SPI_HOST_EVENT_ENABLE_REG_OFFSET, |
| event_enable); |
| |
| // TODO(atv): Dependency inject our SPI functions? |
| LCD_Init(spi_display); |
| |
| return kDifOk; |
| } |
| |
| dif_result_t spi_display_irq_init(spi_display_t* spi_display, |
| const dif_rv_plic_t* plic, |
| dif_rv_plic_irq_id_t irq_id, |
| dif_rv_plic_target_t target) { |
| CHECK_DIF_OK( |
| dif_rv_plic_irq_set_enabled(plic, irq_id, target, kDifToggleEnabled)); |
| CHECK_DIF_OK( |
| dif_rv_plic_irq_set_priority(plic, irq_id, kDifRvPlicMaxPriority)); |
| return kDifOk; |
| } |
| |
| dif_result_t spi_display_spi_irq_handler(spi_display_t* spi_display) { |
| mmio_region_write32(spi_display->spi_host->base_addr, |
| SPI_HOST_INTR_STATE_REG_OFFSET, |
| mmio_region_read32(spi_display->spi_host->base_addr, |
| SPI_HOST_INTR_STATE_REG_OFFSET)); |
| spi_display->spi_idle_seen = true; |
| return kDifOk; |
| } |
| |
| dif_result_t spi_display_smc_mailbox_irq_handler(spi_display_t* spi_display) { |
| uint32_t message; |
| CHECK_DIF_OK(dif_tlul_mailbox_irq_acknowledge(spi_display->mailbox, |
| kDifTlulMailboxIrqRtirq)); |
| CHECK_DIF_OK(dif_tlul_mailbox_read_message(spi_display->mailbox, &message)); |
| spi_display->gpio_write_done = true; |
| return kDifOk; |
| } |
| |
| void spi_display_byte_write(spi_display_t* spi_display, uint8_t value) { |
| dif_spi_host_segment_t transaction[] = { |
| { |
| .type = kDifSpiHostSegmentTypeTx, |
| .tx = |
| { |
| .width = kDifSpiHostWidthStandard, |
| .buf = &value, |
| .length = sizeof(value), |
| }, |
| }, |
| }; |
| CHECK_DIF_OK(dif_spi_host_transaction(spi_display->spi_host, /*csid=*/0, |
| transaction, ARRAYSIZE(transaction))); |
| } |
| |
| void spi_display_block_write(spi_display_t* spi_display, const uint8_t* buffer, |
| size_t bytes) { |
| dif_spi_host_segment_t transaction[] = { |
| { |
| .type = kDifSpiHostSegmentTypeTx, |
| .tx = |
| { |
| .width = kDifSpiHostWidthStandard, |
| .buf = 0, |
| .length = 0, |
| }, |
| }, |
| }; |
| size_t remaining = bytes; |
| size_t offset = 0; |
| while (remaining) { |
| size_t tx_len = MIN(256, remaining); |
| transaction[0].tx.length = tx_len; |
| transaction[0].tx.buf = buffer + offset; |
| spi_display->spi_idle_seen = false; |
| CHECK_DIF_OK(dif_spi_host_transaction(spi_display->spi_host, 0, transaction, |
| ARRAYSIZE(transaction))); |
| while (!spi_display->spi_idle_seen) { |
| asm volatile("wfi"); |
| } |
| spi_display->spi_idle_seen = false; |
| offset += tx_len; |
| remaining -= tx_len; |
| } |
| } |
| |
| void spi_display_gpio_write(spi_display_t* spi_display, UWORD pin, |
| UBYTE value) { |
| // Don't worry about it since the hardware controls it. |
| if (pin == DEV_CS_PIN) { |
| return; |
| } |
| |
| uint32_t msg = ((pin & 0xFFFF) << 16) | (value & 0xFFFF); |
| spi_display->gpio_write_done = false; |
| CHECK_DIF_OK(dif_tlul_mailbox_send_message(spi_display->mailbox, &msg)); |
| while (!spi_display->gpio_write_done) { |
| asm volatile("wfi"); |
| } |
| spi_display->gpio_write_done = false; |
| } |
| |
| // Implementations needed for LCD_Driver.cpp |
| |
| void DEV_Delay_ms(void* ctx, UWORD value) { |
| spi_display_t* spi_display = (spi_display_t*)ctx; |
| busy_spin_micros(value * 1000); |
| } |
| |
| void DEV_SPI_BLOCK_WRITE(void* ctx, const uint8_t* buffer, size_t bytes) { |
| spi_display_t* spi_display = (spi_display_t*)ctx; |
| spi_display_block_write(ctx, buffer, bytes); |
| } |
| |
| void DEV_SPI_WRITE(void* ctx, UBYTE value) { |
| spi_display_t* spi_display = (spi_display_t*)ctx; |
| spi_display_byte_write(ctx, value); |
| } |
| |
| void DEV_Digital_Write(void* ctx, UWORD pin, UBYTE value) { |
| spi_display_t* spi_display = (spi_display_t*)ctx; |
| spi_display_gpio_write(ctx, pin, value); |
| } |