1#!/usr/bin/env python3
2#
3# Copyright (c) 2019 Intel Corporation
4#
5# SPDX-License-Identifier: Apache-2.0
6import math
7import logging
8from time import sleep
9from ctypes import c_uint16, POINTER, cast, c_uint8, c_uint64
10
11from lib.driver import Register
12import lib.registers as regs_def
13import lib.platforms as plat_def
14
15
16class DmaBuf:
17    """ Class for DMA buffer """
18
19    def __init__(self, drv, size):
20        self.drv = drv
21        self.size = size
22
23        # Allocated DMA buffer should be a multiplication of page size
24        self.page_count = math.ceil(self.size / plat_def.DMA_PAGE_SIZE)
25        logging.debug("Page Count: %d" % self.page_count)
26
27        self.alloc_size = self.page_count * plat_def.DMA_PAGE_SIZE
28        logging.debug("Allocate DMA Buffer: size=0x%08X alloc_size=0x%08X"
29                      % (self.size, self.alloc_size))
30        self.mem = self.drv.alloc_mem(self.alloc_size)
31        self.addr_p = self.mem.dma_addr_p
32        self.buf = cast(self.mem.dma_addr_v,
33                        POINTER(c_uint8 * self.alloc_size)).contents
34
35    def copy(self, data, size):
36        """ Copying data to allocated DMA buffer """
37        if size > self.alloc_size:
38            raise ValueError("Not enough buffer. allocated: %d requested: %d"
39                             % (self.alloc_size, size))
40        logging.debug("Copying Data to DMA buffer")
41        self.buf[:size] = data[:size]
42
43    def free(self):
44        if self.mem:
45            self.drv.free_mem(self.mem)
46            self.mem = None
47
48
49class DmaBufDescList:
50    """ Class DMA Buffer Descriptor List """
51
52    def __init__(self, drv, fw_buf):
53        self.drv = drv
54        self.bd_count = fw_buf.page_count
55
56        # Single Page for Buffer Descriptor List
57        self.buf = DmaBuf(drv, plat_def.DMA_PAGE_SIZE)
58
59        curr_ptr = 0
60        # Map BDLE with data buffer
61        logging.debug("Update Buffer Descriptor List:")
62        for i in range(self.bd_count):
63            bdle_addr = Register(self.buf.mem.memmap, (i * 16) + 0x00, c_uint64)
64            bdle_len = Register(self.buf.mem.memmap, (i * 16) + 0x08)
65            bdle_ioc = Register(self.buf.mem.memmap, (i * 16) + 0x0C)
66
67            if fw_buf.alloc_size - curr_ptr > plat_def.DMA_PAGE_SIZE:
68                bdle_len.value = plat_def.DMA_PAGE_SIZE
69                bdle_ioc.value = 0
70                bdle_addr.value = fw_buf.addr_p + curr_ptr
71            else:
72                bdle_len.value = fw_buf.alloc_size - curr_ptr
73                bdle_ioc.value = 1
74                bdle_addr.value = fw_buf.addr_p + curr_ptr
75
76                logging.debug("   BDLE#%02d:  ADDR: %s  LEN: %s  IOC: %s"
77                              % (i, bdle_addr, bdle_len, bdle_ioc))
78                break
79            curr_ptr += plat_def.DMA_PAGE_SIZE
80            logging.debug("   BDLE#%02d:  ADDR: %s  LEN: %s  IOC: %s"
81                          % (i, bdle_addr, bdle_len, bdle_ioc))
82
83    def free(self):
84        if self.buf:
85            self.drv.free_mem(self.buf.mem)
86            self.buf = None
87
88
89class StreamDesc:
90    """ Class for Stream Descriptor """
91
92    def __init__(self, idx, dev):
93        self.idx = idx
94        self.dev = dev
95        self.used = False
96        self.buf = None
97        self.bdl = None
98
99        offset = regs_def.HDA_SD_BASE + (regs_def.HDA_SD_SIZE * (idx))
100        # Registers in SD
101        self.ctl_sts = Register(self.dev.hda_bar_mmap,
102                                offset + regs_def.HDA_SD_CS)
103        self.lpib = Register(self.dev.hda_bar_mmap,
104                             offset + regs_def.HDA_SD_LPIB)
105        self.cbl = Register(self.dev.hda_bar_mmap,
106                            offset + regs_def.HDA_SD_CBL)
107        self.lvi = Register(self.dev.hda_bar_mmap,
108                            offset + regs_def.HDA_SD_LVI, c_uint16)
109        self.fifow = Register(self.dev.hda_bar_mmap,
110                              offset + regs_def.HDA_SD_FIFOW, c_uint16)
111        self.fifos = Register(self.dev.hda_bar_mmap,
112                              offset + regs_def.HDA_SD_FIFOS, c_uint16)
113        self.fmt = Register(self.dev.hda_bar_mmap,
114                            offset + regs_def.HDA_SD_FMT, c_uint16)
115        self.fifol = Register(self.dev.hda_bar_mmap,
116                              offset + regs_def.HDA_SD_FIFOL)
117        self.bdlplba = Register(self.dev.hda_bar_mmap,
118                                offset + regs_def.HDA_SD_BDLPLBA)
119        self.bdlpuba = Register(self.dev.hda_bar_mmap,
120                                offset + regs_def.HDA_SD_BDLPUBA)
121
122        # Register for SPIB
123        offset = regs_def.HDA_SPBF_SD_BASE + (regs_def.HDA_SPBF_SD_SIZE * (idx))
124        self.sdspib = Register(self.dev.hda_bar_mmap,
125                               offset + regs_def.HDA_SPBF_SDSPIB)
126
127    def free_memory(self):
128        if self.buf is not None:
129            self.buf.free()
130            self.buf = None
131        if self.bdl is not None:
132            self.bdl.free()
133            self.bdl = None
134
135    def alloc_memory(self, size):
136        self.free_memory()
137        self.buf = DmaBuf(self.dev.drv, size)
138        self.bdl = DmaBufDescList(self.dev.drv, self.buf)
139
140    def config(self):
141        self.bdlplba.value = self.bdl.buf.addr_p & 0xFFFFFFFF
142        self.bdlpuba.value = self.bdl.buf.addr_p >> 32
143        self.cbl.value = self.buf.size
144        self.lvi.value = self.bdl.bd_count - 1
145        self.sdspib.value = self.cbl.value
146
147    def set_bitrate(self, bit_rate):
148        logging.debug("SD#%02d: Set Bitrate: 0x%04X" % (self.idx, bit_rate))
149        sdfmt = self.fmt.value
150        sdfmt |= (bit_rate << 4)
151        self.fmt.value = sdfmt
152        logging.debug("SD#%02d: SD_FMT=%s" % (self.idx, self.fmt))
153
154    def set_stream_id(self, stream_id):
155        logging.debug("SD#%02d: Set Stream ID: 0x%04X" % (self.idx, stream_id))
156        sd_ctl = self.ctl_sts.value
157        sd_ctl &= ~(0xF << 20)
158        sd_ctl |= (stream_id << 20)
159        self.ctl_sts.value = sd_ctl
160        logging.debug("SD#%02d: SD_CTL_STS=%s" % (self.idx, self.ctl_sts))
161
162    def set_traffic_priority(self, value):
163        logging.debug("SD#%02d: Set Traffic Priority(0x%02X)" % (self.idx, value))
164        sd_ctl = self.ctl_sts.value
165        sd_ctl |= (value << 18)
166        self.ctl_sts.value = sd_ctl
167        logging.debug("SD#%02d: SD_CTL_STS=%s" % (self.idx, self.ctl_sts))
168
169    def start(self):
170        """ Start DMA transfer """
171        logging.debug("SD#%02d: Start DMA stream" % self.idx)
172        self.ctl_sts.value = self.ctl_sts.value | (1 << 1)
173        logging.debug("SD#%02d: SD_CTL_STS=%s" % (self.idx, self.ctl_sts))
174        while self.ctl_sts.value & (1 << 1) == 0:
175            sleep(0.001)
176
177    def pause(self):
178        logging.debug("SD#%02d: Pause DMA stream" % self.idx)
179        self.ctl_sts.value = self.ctl_sts.value & ~(1 << 1)
180        while self.ctl_sts.value & (1 << 1):
181            sleep(0.001)
182
183    def reset(self):
184        logging.debug("SD#%02d: Reset DAM stream" % self.idx)
185        self.pause()
186        self.ctl_sts.value = self.ctl_sts.value | 1
187        sleep(0.01)
188        self.ctl_sts.value = self.ctl_sts.value & ~1
189        sleep(0.01)
190
191
192class StreamDescList:
193    """ Class for DMA Stream Descriptor List """
194
195    def __init__(self, dev):
196        self.dev = dev
197        self.sdl = []
198        for i in range(plat_def.NUM_STREAMS):
199            sd = StreamDesc(i, self.dev)
200            self.sdl.append(sd)
201
202    def close(self):
203        for sd in self.sdl:
204            if sd.used:
205                self.release(sd)
206
207    def reset_all(self):
208        for sd in self.sdl:
209            sd.reset()
210
211    def get_sd(self, idx):
212        sd = self.sdl[idx]
213        if sd.used:
214            raise ResourceWarning("IOB #%d already in use!" % idx)
215        sd.used = True
216        return sd
217
218    def release(self, sd):
219        return self.release_sd(sd.idx)
220
221    def release_sd(self, idx, reset_hw=True):
222        if not self.sdl[idx].used:
223            logging.warning("SD#%d: Not used!!!" % idx)
224        self.sdl[idx].used = False
225        if reset_hw:
226            self.sdl[idx].pause()
227            self.sdl[idx].reset()
228        self.sdl[idx].free_memory()
229