/* * xlnx_dpdma.c * * Copyright (C) 2015 : GreenSocs Ltd * http://www.greensocs.com/ , email: info@greensocs.com * * Developed by : * Frederic Konrad * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see . * */ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/module.h" #include "hw/dma/xlnx_dpdma.h" #include "hw/irq.h" #include "migration/vmstate.h" #ifndef DEBUG_DPDMA #define DEBUG_DPDMA 0 #endif #define DPRINTF(fmt, ...) do { \ if (DEBUG_DPDMA) { \ qemu_log("xlnx_dpdma: " fmt , ## __VA_ARGS__); \ } \ } while (0) /* * Registers offset for DPDMA. */ #define DPDMA_ERR_CTRL (0x0000) #define DPDMA_ISR (0x0004 >> 2) #define DPDMA_IMR (0x0008 >> 2) #define DPDMA_IEN (0x000C >> 2) #define DPDMA_IDS (0x0010 >> 2) #define DPDMA_EISR (0x0014 >> 2) #define DPDMA_EIMR (0x0018 >> 2) #define DPDMA_EIEN (0x001C >> 2) #define DPDMA_EIDS (0x0020 >> 2) #define DPDMA_CNTL (0x0100 >> 2) #define DPDMA_GBL (0x0104 >> 2) #define DPDMA_GBL_TRG_CH(n) (1 << n) #define DPDMA_GBL_RTRG_CH(n) (1 << 6 << n) #define DPDMA_ALC0_CNTL (0x0108 >> 2) #define DPDMA_ALC0_STATUS (0x010C >> 2) #define DPDMA_ALC0_MAX (0x0110 >> 2) #define DPDMA_ALC0_MIN (0x0114 >> 2) #define DPDMA_ALC0_ACC (0x0118 >> 2) #define DPDMA_ALC0_ACC_TRAN (0x011C >> 2) #define DPDMA_ALC1_CNTL (0x0120 >> 2) #define DPDMA_ALC1_STATUS (0x0124 >> 2) #define DPDMA_ALC1_MAX (0x0128 >> 2) #define DPDMA_ALC1_MIN (0x012C >> 2) #define DPDMA_ALC1_ACC (0x0130 >> 2) #define DPDMA_ALC1_ACC_TRAN (0x0134 >> 2) #define DPDMA_DSCR_STRT_ADDRE_CH(n) ((0x0200 + n * 0x100) >> 2) #define DPDMA_DSCR_STRT_ADDR_CH(n) ((0x0204 + n * 0x100) >> 2) #define DPDMA_DSCR_NEXT_ADDRE_CH(n) ((0x0208 + n * 0x100) >> 2) #define DPDMA_DSCR_NEXT_ADDR_CH(n) ((0x020C + n * 0x100) >> 2) #define DPDMA_PYLD_CUR_ADDRE_CH(n) ((0x0210 + n * 0x100) >> 2) #define DPDMA_PYLD_CUR_ADDR_CH(n) ((0x0214 + n * 0x100) >> 2) #define DPDMA_CNTL_CH(n) ((0x0218 + n * 0x100) >> 2) #define DPDMA_CNTL_CH_EN (1) #define DPDMA_CNTL_CH_PAUSED (1 << 1) #define DPDMA_STATUS_CH(n) ((0x021C + n * 0x100) >> 2) #define DPDMA_STATUS_BURST_TYPE (1 << 4) #define DPDMA_STATUS_MODE (1 << 5) #define DPDMA_STATUS_EN_CRC (1 << 6) #define DPDMA_STATUS_LAST_DSCR (1 << 7) #define DPDMA_STATUS_LDSCR_FRAME (1 << 8) #define DPDMA_STATUS_IGNR_DONE (1 << 9) #define DPDMA_STATUS_DSCR_DONE (1 << 10) #define DPDMA_STATUS_EN_DSCR_UP (1 << 11) #define DPDMA_STATUS_EN_DSCR_INTR (1 << 12) #define DPDMA_STATUS_PREAMBLE_OFF (13) #define DPDMA_VDO_CH(n) ((0x0220 + n * 0x100) >> 2) #define DPDMA_PYLD_SZ_CH(n) ((0x0224 + n * 0x100) >> 2) #define DPDMA_DSCR_ID_CH(n) ((0x0228 + n * 0x100) >> 2) /* * Descriptor control field. */ #define CONTROL_PREAMBLE_VALUE 0xA5 #define DSCR_CTRL_PREAMBLE 0xFF #define DSCR_CTRL_EN_DSCR_DONE_INTR (1 << 8) #define DSCR_CTRL_EN_DSCR_UPDATE (1 << 9) #define DSCR_CTRL_IGNORE_DONE (1 << 10) #define DSCR_CTRL_AXI_BURST_TYPE (1 << 11) #define DSCR_CTRL_AXCACHE (0x0F << 12) #define DSCR_CTRL_AXPROT (0x2 << 16) #define DSCR_CTRL_DESCRIPTOR_MODE (1 << 18) #define DSCR_CTRL_LAST_DESCRIPTOR (1 << 19) #define DSCR_CTRL_ENABLE_CRC (1 << 20) #define DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME (1 << 21) /* * Descriptor timestamp field. */ #define STATUS_DONE (1 << 31) #define DPDMA_FRAG_MAX_SZ (4096) enum DPDMABurstType { DPDMA_INCR = 0, DPDMA_FIXED = 1 }; enum DPDMAMode { DPDMA_CONTIGOUS = 0, DPDMA_FRAGMENTED = 1 }; struct DPDMADescriptor { uint32_t control; uint32_t descriptor_id; /* transfer size in byte. */ uint32_t xfer_size; uint32_t line_size_stride; uint32_t timestamp_lsb; uint32_t timestamp_msb; /* contains extension for both descriptor and source. */ uint32_t address_extension; uint32_t next_descriptor; uint32_t source_address; uint32_t address_extension_23; uint32_t address_extension_45; uint32_t source_address2; uint32_t source_address3; uint32_t source_address4; uint32_t source_address5; uint32_t crc; }; typedef enum DPDMABurstType DPDMABurstType; typedef enum DPDMAMode DPDMAMode; typedef struct DPDMADescriptor DPDMADescriptor; static bool xlnx_dpdma_desc_is_last(DPDMADescriptor *desc) { return ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR) != 0); } static bool xlnx_dpdma_desc_is_last_of_frame(DPDMADescriptor *desc) { return ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME) != 0); } static uint64_t xlnx_dpdma_desc_get_source_address(DPDMADescriptor *desc, uint8_t frag) { uint64_t addr = 0; assert(frag < 5); switch (frag) { case 0: addr = (uint64_t)desc->source_address + (extract64(desc->address_extension, 16, 16) << 32); break; case 1: addr = (uint64_t)desc->source_address2 + (extract64(desc->address_extension_23, 0, 16) << 32); break; case 2: addr = (uint64_t)desc->source_address3 + (extract64(desc->address_extension_23, 16, 16) << 32); break; case 3: addr = (uint64_t)desc->source_address4 + (extract64(desc->address_extension_45, 0, 16) << 32); break; case 4: addr = (uint64_t)desc->source_address5 + (extract64(desc->address_extension_45, 16, 16) << 32); break; default: addr = 0; break; } return addr; } static uint32_t xlnx_dpdma_desc_get_transfer_size(DPDMADescriptor *desc) { return desc->xfer_size; } static uint32_t xlnx_dpdma_desc_get_line_size(DPDMADescriptor *desc) { return extract32(desc->line_size_stride, 0, 18); } static uint32_t xlnx_dpdma_desc_get_line_stride(DPDMADescriptor *desc) { return extract32(desc->line_size_stride, 18, 14) * 16; } static inline bool xlnx_dpdma_desc_crc_enabled(DPDMADescriptor *desc) { return (desc->control & DSCR_CTRL_ENABLE_CRC) != 0; } static inline bool xlnx_dpdma_desc_check_crc(DPDMADescriptor *desc) { uint32_t *p = (uint32_t *)desc; uint32_t crc = 0; uint8_t i; /* * CRC is calculated on the whole descriptor except the last 32bits word * using 32bits addition. */ for (i = 0; i < 15; i++) { crc += p[i]; } return crc == desc->crc; } static inline bool xlnx_dpdma_desc_completion_interrupt(DPDMADescriptor *desc) { return (desc->control & DSCR_CTRL_EN_DSCR_DONE_INTR) != 0; } static inline bool xlnx_dpdma_desc_is_valid(DPDMADescriptor *desc) { return (desc->control & DSCR_CTRL_PREAMBLE) == CONTROL_PREAMBLE_VALUE; } static inline bool xlnx_dpdma_desc_is_contiguous(DPDMADescriptor *desc) { return (desc->control & DSCR_CTRL_DESCRIPTOR_MODE) == 0; } static inline bool xlnx_dpdma_desc_update_enabled(DPDMADescriptor *desc) { return (desc->control & DSCR_CTRL_EN_DSCR_UPDATE) != 0; } static inline void xlnx_dpdma_desc_set_done(DPDMADescriptor *desc) { desc->timestamp_msb |= STATUS_DONE; } static inline bool xlnx_dpdma_desc_is_already_done(DPDMADescriptor *desc) { return (desc->timestamp_msb & STATUS_DONE) != 0; } static inline bool xlnx_dpdma_desc_ignore_done_bit(DPDMADescriptor *desc) { return (desc->control & DSCR_CTRL_IGNORE_DONE) != 0; } static const VMStateDescription vmstate_xlnx_dpdma = { .name = TYPE_XLNX_DPDMA, .version_id = 1, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(registers, XlnxDPDMAState, XLNX_DPDMA_REG_ARRAY_SIZE), VMSTATE_BOOL_ARRAY(operation_finished, XlnxDPDMAState, 6), VMSTATE_END_OF_LIST() } }; static void xlnx_dpdma_update_irq(XlnxDPDMAState *s) { bool flags; flags = ((s->registers[DPDMA_ISR] & (~s->registers[DPDMA_IMR])) || (s->registers[DPDMA_EISR] & (~s->registers[DPDMA_EIMR]))); qemu_set_irq(s->irq, flags); } static uint64_t xlnx_dpdma_descriptor_start_address(XlnxDPDMAState *s, uint8_t channel) { return (s->registers[DPDMA_DSCR_STRT_ADDRE_CH(channel)] << 16) + s->registers[DPDMA_DSCR_STRT_ADDR_CH(channel)]; } static uint64_t xlnx_dpdma_descriptor_next_address(XlnxDPDMAState *s, uint8_t channel) { return ((uint64_t)s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] << 32) + s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)]; } static bool xlnx_dpdma_is_channel_enabled(XlnxDPDMAState *s, uint8_t channel) { return (s->registers[DPDMA_CNTL_CH(channel)] & DPDMA_CNTL_CH_EN) != 0; } static bool xlnx_dpdma_is_channel_paused(XlnxDPDMAState *s, uint8_t channel) { return (s->registers[DPDMA_CNTL_CH(channel)] & DPDMA_CNTL_CH_PAUSED) != 0; } static inline bool xlnx_dpdma_is_channel_retriggered(XlnxDPDMAState *s, uint8_t channel) { /* Clear the retriggered bit after reading it. */ bool channel_is_retriggered = s->registers[DPDMA_GBL] & DPDMA_GBL_RTRG_CH(channel); s->registers[DPDMA_GBL] &= ~DPDMA_GBL_RTRG_CH(channel); return channel_is_retriggered; } static inline bool xlnx_dpdma_is_channel_triggered(XlnxDPDMAState *s, uint8_t channel) { return s->registers[DPDMA_GBL] & DPDMA_GBL_TRG_CH(channel); } static void xlnx_dpdma_update_desc_info(XlnxDPDMAState *s, uint8_t channel, DPDMADescriptor *desc) { s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] = extract32(desc->address_extension, 0, 16); s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)] = desc->next_descriptor; s->registers[DPDMA_PYLD_CUR_ADDRE_CH(channel)] = extract32(desc->address_extension, 16, 16); s->registers[DPDMA_PYLD_CUR_ADDR_CH(channel)] = desc->source_address; s->registers[DPDMA_VDO_CH(channel)] = extract32(desc->line_size_stride, 18, 14) + (extract32(desc->line_size_stride, 0, 18) << 14); s->registers[DPDMA_PYLD_SZ_CH(channel)] = desc->xfer_size; s->registers[DPDMA_DSCR_ID_CH(channel)] = desc->descriptor_id; /* Compute the status register with the descriptor information. */ s->registers[DPDMA_STATUS_CH(channel)] = extract32(desc->control, 0, 8) << 13; if ((desc->control & DSCR_CTRL_EN_DSCR_DONE_INTR) != 0) { s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_DSCR_INTR; } if ((desc->control & DSCR_CTRL_EN_DSCR_UPDATE) != 0) { s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_DSCR_UP; } if ((desc->timestamp_msb & STATUS_DONE) != 0) { s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_DSCR_DONE; } if ((desc->control & DSCR_CTRL_IGNORE_DONE) != 0) { s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_IGNR_DONE; } if ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME) != 0) { s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_LDSCR_FRAME; } if ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR) != 0) { s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_LAST_DSCR; } if ((desc->control & DSCR_CTRL_ENABLE_CRC) != 0) { s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_CRC; } if ((desc->control & DSCR_CTRL_DESCRIPTOR_MODE) != 0) { s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_MODE; } if ((desc->control & DSCR_CTRL_AXI_BURST_TYPE) != 0) { s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_BURST_TYPE; } } static void xlnx_dpdma_dump_descriptor(DPDMADescriptor *desc) { if (DEBUG_DPDMA) { qemu_log("DUMP DESCRIPTOR:\n"); qemu_hexdump(stdout, "", desc, sizeof(DPDMADescriptor)); } } static uint64_t xlnx_dpdma_read(void *opaque, hwaddr offset, unsigned size) { XlnxDPDMAState *s = XLNX_DPDMA(opaque); DPRINTF("read @%" HWADDR_PRIx "\n", offset); offset = offset >> 2; switch (offset) { /* * Trying to read a write only register. */ case DPDMA_GBL: return 0; default: assert(offset <= (0xFFC >> 2)); return s->registers[offset]; } return 0; } static void xlnx_dpdma_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { XlnxDPDMAState *s = XLNX_DPDMA(opaque); DPRINTF("write @%" HWADDR_PRIx " = %" PRIx64 "\n", offset, value); offset = offset >> 2; switch (offset) { case DPDMA_ISR: s->registers[DPDMA_ISR] &= ~value; xlnx_dpdma_update_irq(s); break; case DPDMA_IEN: s->registers[DPDMA_IMR] &= ~value; break; case DPDMA_IDS: s->registers[DPDMA_IMR] |= value; break; case DPDMA_EISR: s->registers[DPDMA_EISR] &= ~value; xlnx_dpdma_update_irq(s); break; case DPDMA_EIEN: s->registers[DPDMA_EIMR] &= ~value; break; case DPDMA_EIDS: s->registers[DPDMA_EIMR] |= value; break; case DPDMA_IMR: case DPDMA_EIMR: case DPDMA_DSCR_NEXT_ADDRE_CH(0): case DPDMA_DSCR_NEXT_ADDRE_CH(1): case DPDMA_DSCR_NEXT_ADDRE_CH(2): case DPDMA_DSCR_NEXT_ADDRE_CH(3): case DPDMA_DSCR_NEXT_ADDRE_CH(4): case DPDMA_DSCR_NEXT_ADDRE_CH(5): case DPDMA_DSCR_NEXT_ADDR_CH(0): case DPDMA_DSCR_NEXT_ADDR_CH(1): case DPDMA_DSCR_NEXT_ADDR_CH(2): case DPDMA_DSCR_NEXT_ADDR_CH(3): case DPDMA_DSCR_NEXT_ADDR_CH(4): case DPDMA_DSCR_NEXT_ADDR_CH(5): case DPDMA_PYLD_CUR_ADDRE_CH(0): case DPDMA_PYLD_CUR_ADDRE_CH(1): case DPDMA_PYLD_CUR_ADDRE_CH(2): case DPDMA_PYLD_CUR_ADDRE_CH(3): case DPDMA_PYLD_CUR_ADDRE_CH(4): case DPDMA_PYLD_CUR_ADDRE_CH(5): case DPDMA_PYLD_CUR_ADDR_CH(0): case DPDMA_PYLD_CUR_ADDR_CH(1): case DPDMA_PYLD_CUR_ADDR_CH(2): case DPDMA_PYLD_CUR_ADDR_CH(3): case DPDMA_PYLD_CUR_ADDR_CH(4): case DPDMA_PYLD_CUR_ADDR_CH(5): case DPDMA_STATUS_CH(0): case DPDMA_STATUS_CH(1): case DPDMA_STATUS_CH(2): case DPDMA_STATUS_CH(3): case DPDMA_STATUS_CH(4): case DPDMA_STATUS_CH(5): case DPDMA_VDO_CH(0): case DPDMA_VDO_CH(1): case DPDMA_VDO_CH(2): case DPDMA_VDO_CH(3): case DPDMA_VDO_CH(4): case DPDMA_VDO_CH(5): case DPDMA_PYLD_SZ_CH(0): case DPDMA_PYLD_SZ_CH(1): case DPDMA_PYLD_SZ_CH(2): case DPDMA_PYLD_SZ_CH(3): case DPDMA_PYLD_SZ_CH(4): case DPDMA_PYLD_SZ_CH(5): case DPDMA_DSCR_ID_CH(0): case DPDMA_DSCR_ID_CH(1): case DPDMA_DSCR_ID_CH(2): case DPDMA_DSCR_ID_CH(3): case DPDMA_DSCR_ID_CH(4): case DPDMA_DSCR_ID_CH(5): /* * Trying to write to a read only register.. */ break; case DPDMA_GBL: /* * This is a write only register so it's read as zero in the read * callback. * We store the value anyway so we can know if the channel is * enabled. */ s->registers[offset] |= value & 0x00000FFF; break; case DPDMA_DSCR_STRT_ADDRE_CH(0): case DPDMA_DSCR_STRT_ADDRE_CH(1): case DPDMA_DSCR_STRT_ADDRE_CH(2): case DPDMA_DSCR_STRT_ADDRE_CH(3): case DPDMA_DSCR_STRT_ADDRE_CH(4): case DPDMA_DSCR_STRT_ADDRE_CH(5): value &= 0x0000FFFF; s->registers[offset] = value; break; case DPDMA_CNTL_CH(0): s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(0); value &= 0x3FFFFFFF; s->registers[offset] = value; break; case DPDMA_CNTL_CH(1): s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(1); value &= 0x3FFFFFFF; s->registers[offset] = value; break; case DPDMA_CNTL_CH(2): s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(2); value &= 0x3FFFFFFF; s->registers[offset] = value; break; case DPDMA_CNTL_CH(3): s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(3); value &= 0x3FFFFFFF; s->registers[offset] = value; break; case DPDMA_CNTL_CH(4): s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(4); value &= 0x3FFFFFFF; s->registers[offset] = value; break; case DPDMA_CNTL_CH(5): s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(5); value &= 0x3FFFFFFF; s->registers[offset] = value; break; default: assert(offset <= (0xFFC >> 2)); s->registers[offset] = value; break; } } static const MemoryRegionOps dma_ops = { .read = xlnx_dpdma_read, .write = xlnx_dpdma_write, .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, }, .impl = { .min_access_size = 4, .max_access_size = 4, }, }; static void xlnx_dpdma_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); XlnxDPDMAState *s = XLNX_DPDMA(obj); memory_region_init_io(&s->iomem, obj, &dma_ops, s, TYPE_XLNX_DPDMA, 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); } static void xlnx_dpdma_reset(DeviceState *dev) { XlnxDPDMAState *s = XLNX_DPDMA(dev); size_t i; memset(s->registers, 0, sizeof(s->registers)); s->registers[DPDMA_IMR] = 0x07FFFFFF; s->registers[DPDMA_EIMR] = 0xFFFFFFFF; s->registers[DPDMA_ALC0_MIN] = 0x0000FFFF; s->registers[DPDMA_ALC1_MIN] = 0x0000FFFF; for (i = 0; i < 6; i++) { s->data[i] = NULL; s->operation_finished[i] = true; } } static void xlnx_dpdma_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->vmsd = &vmstate_xlnx_dpdma; device_class_set_legacy_reset(dc, xlnx_dpdma_reset); } static const TypeInfo xlnx_dpdma_info = { .name = TYPE_XLNX_DPDMA, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(XlnxDPDMAState), .instance_init = xlnx_dpdma_init, .class_init = xlnx_dpdma_class_init, }; static void xlnx_dpdma_register_types(void) { type_register_static(&xlnx_dpdma_info); } static MemTxResult xlnx_dpdma_read_descriptor(XlnxDPDMAState *s, uint64_t desc_addr, DPDMADescriptor *desc) { MemTxResult res = dma_memory_read(&address_space_memory, desc_addr, desc, sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED); if (res) { return res; } /* Convert from LE into host endianness. */ desc->control = le32_to_cpu(desc->control); desc->descriptor_id = le32_to_cpu(desc->descriptor_id); desc->xfer_size = le32_to_cpu(desc->xfer_size); desc->line_size_stride = le32_to_cpu(desc->line_size_stride); desc->timestamp_lsb = le32_to_cpu(desc->timestamp_lsb); desc->timestamp_msb = le32_to_cpu(desc->timestamp_msb); desc->address_extension = le32_to_cpu(desc->address_extension); desc->next_descriptor = le32_to_cpu(desc->next_descriptor); desc->source_address = le32_to_cpu(desc->source_address); desc->address_extension_23 = le32_to_cpu(desc->address_extension_23); desc->address_extension_45 = le32_to_cpu(desc->address_extension_45); desc->source_address2 = le32_to_cpu(desc->source_address2); desc->source_address3 = le32_to_cpu(desc->source_address3); desc->source_address4 = le32_to_cpu(desc->source_address4); desc->source_address5 = le32_to_cpu(desc->source_address5); desc->crc = le32_to_cpu(desc->crc); return res; } static MemTxResult xlnx_dpdma_write_descriptor(uint64_t desc_addr, DPDMADescriptor *desc) { DPDMADescriptor tmp_desc = *desc; /* Convert from host endianness into LE. */ tmp_desc.control = cpu_to_le32(tmp_desc.control); tmp_desc.descriptor_id = cpu_to_le32(tmp_desc.descriptor_id); tmp_desc.xfer_size = cpu_to_le32(tmp_desc.xfer_size); tmp_desc.line_size_stride = cpu_to_le32(tmp_desc.line_size_stride); tmp_desc.timestamp_lsb = cpu_to_le32(tmp_desc.timestamp_lsb); tmp_desc.timestamp_msb = cpu_to_le32(tmp_desc.timestamp_msb); tmp_desc.address_extension = cpu_to_le32(tmp_desc.address_extension); tmp_desc.next_descriptor = cpu_to_le32(tmp_desc.next_descriptor); tmp_desc.source_address = cpu_to_le32(tmp_desc.source_address); tmp_desc.address_extension_23 = cpu_to_le32(tmp_desc.address_extension_23); tmp_desc.address_extension_45 = cpu_to_le32(tmp_desc.address_extension_45); tmp_desc.source_address2 = cpu_to_le32(tmp_desc.source_address2); tmp_desc.source_address3 = cpu_to_le32(tmp_desc.source_address3); tmp_desc.source_address4 = cpu_to_le32(tmp_desc.source_address4); tmp_desc.source_address5 = cpu_to_le32(tmp_desc.source_address5); tmp_desc.crc = cpu_to_le32(tmp_desc.crc); return dma_memory_write(&address_space_memory, desc_addr, &tmp_desc, sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED); } size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel, bool one_desc) { uint64_t desc_addr; uint64_t source_addr[6]; DPDMADescriptor desc; bool done = false; size_t ptr = 0; assert(channel <= 5); DPRINTF("start dpdma channel 0x%" PRIX8 "\n", channel); if (!xlnx_dpdma_is_channel_triggered(s, channel)) { DPRINTF("Channel isn't triggered..\n"); return 0; } if (!xlnx_dpdma_is_channel_enabled(s, channel)) { DPRINTF("Channel isn't enabled..\n"); return 0; } if (xlnx_dpdma_is_channel_paused(s, channel)) { DPRINTF("Channel is paused..\n"); return 0; } do { if ((s->operation_finished[channel]) || xlnx_dpdma_is_channel_retriggered(s, channel)) { desc_addr = xlnx_dpdma_descriptor_start_address(s, channel); s->operation_finished[channel] = false; } else { desc_addr = xlnx_dpdma_descriptor_next_address(s, channel); } if (xlnx_dpdma_read_descriptor(s, desc_addr, &desc)) { s->registers[DPDMA_EISR] |= ((1 << 1) << channel); xlnx_dpdma_update_irq(s); s->operation_finished[channel] = true; DPRINTF("Can't get the descriptor.\n"); break; } xlnx_dpdma_update_desc_info(s, channel, &desc); #ifdef DEBUG_DPDMA xlnx_dpdma_dump_descriptor(&desc); #endif DPRINTF("location of the descriptor: %" PRIx64 "\n", desc_addr); if (!xlnx_dpdma_desc_is_valid(&desc)) { s->registers[DPDMA_EISR] |= ((1 << 7) << channel); xlnx_dpdma_update_irq(s); s->operation_finished[channel] = true; DPRINTF("Invalid descriptor..\n"); break; } if (xlnx_dpdma_desc_crc_enabled(&desc) && !xlnx_dpdma_desc_check_crc(&desc)) { s->registers[DPDMA_EISR] |= ((1 << 13) << channel); xlnx_dpdma_update_irq(s); s->operation_finished[channel] = true; DPRINTF("Bad CRC for descriptor..\n"); break; } if (xlnx_dpdma_desc_is_already_done(&desc) && !xlnx_dpdma_desc_ignore_done_bit(&desc)) { /* We are trying to process an already processed descriptor. */ s->registers[DPDMA_EISR] |= ((1 << 25) << channel); xlnx_dpdma_update_irq(s); s->operation_finished[channel] = true; DPRINTF("Already processed descriptor..\n"); break; } done = xlnx_dpdma_desc_is_last(&desc) || xlnx_dpdma_desc_is_last_of_frame(&desc); s->operation_finished[channel] = done; if (s->data[channel]) { int64_t transfer_len = xlnx_dpdma_desc_get_transfer_size(&desc); uint32_t line_size = xlnx_dpdma_desc_get_line_size(&desc); uint32_t line_stride = xlnx_dpdma_desc_get_line_stride(&desc); if (xlnx_dpdma_desc_is_contiguous(&desc)) { source_addr[0] = xlnx_dpdma_desc_get_source_address(&desc, 0); while (transfer_len != 0) { if (dma_memory_read(&address_space_memory, source_addr[0], &s->data[channel][ptr], line_size, MEMTXATTRS_UNSPECIFIED)) { s->registers[DPDMA_ISR] |= ((1 << 12) << channel); xlnx_dpdma_update_irq(s); DPRINTF("Can't get data.\n"); break; } ptr += line_size; transfer_len -= line_size; source_addr[0] += line_stride; } } else { DPRINTF("Source address:\n"); int frag; for (frag = 0; frag < 5; frag++) { source_addr[frag] = xlnx_dpdma_desc_get_source_address(&desc, frag); DPRINTF("Fragment %u: %" PRIx64 "\n", frag + 1, source_addr[frag]); } frag = 0; while ((transfer_len < 0) && (frag < 5)) { size_t fragment_len = DPDMA_FRAG_MAX_SZ - (source_addr[frag] % DPDMA_FRAG_MAX_SZ); if (dma_memory_read(&address_space_memory, source_addr[frag], &(s->data[channel][ptr]), fragment_len, MEMTXATTRS_UNSPECIFIED)) { s->registers[DPDMA_ISR] |= ((1 << 12) << channel); xlnx_dpdma_update_irq(s); DPRINTF("Can't get data.\n"); break; } ptr += fragment_len; transfer_len -= fragment_len; frag += 1; } } } if (xlnx_dpdma_desc_update_enabled(&desc)) { /* The descriptor need to be updated when it's completed. */ DPRINTF("update the descriptor with the done flag set.\n"); xlnx_dpdma_desc_set_done(&desc); if (xlnx_dpdma_write_descriptor(desc_addr, &desc)) { DPRINTF("Can't write the descriptor.\n"); /* TODO: check hardware behaviour for memory write failure */ } } if (xlnx_dpdma_desc_completion_interrupt(&desc)) { DPRINTF("completion interrupt enabled!\n"); s->registers[DPDMA_ISR] |= (1 << channel); xlnx_dpdma_update_irq(s); } } while (!done && !one_desc); return ptr; } void xlnx_dpdma_set_host_data_location(XlnxDPDMAState *s, uint8_t channel, void *p) { if (!s) { qemu_log_mask(LOG_UNIMP, "DPDMA client not attached to valid DPDMA" " instance\n"); return; } assert(channel <= 5); s->data[channel] = p; } void xlnx_dpdma_trigger_vsync_irq(XlnxDPDMAState *s) { s->registers[DPDMA_ISR] |= (1 << 27); xlnx_dpdma_update_irq(s); } type_init(xlnx_dpdma_register_types)