blob: 8c63b35d975db2194805f9c6d150824c612f77a1 [file] [log] [blame]
/*
* 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);
}