ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_ssie_stream.c
Go to the documentation of this file.
1
27
28#include <stdint.h>
29
30#include "ra8_attributes.h"
31#include "ra8_check.h"
32#include "ra8_dmac.h"
33#include "ra8_err.h"
34#include "ra8_hal_internal.h"
35#include "ra8_ssie.h"
36#include "ra8_ssie_regs.h"
37
46static const char* s_tag = "SSIE";
47
68static ra8_err_t
69internal_validate_dma_cfg(const ra8_ssie_dma_cfg_t* dma, bool* want_tx, bool* want_rx)
70{
71 *want_tx = (dma->tx_dma_channel < k_ra8_ssie_dma_max_ch);
72 *want_rx = (dma->rx_dma_channel < k_ra8_ssie_dma_max_ch);
73 if (!*want_tx && !*want_rx) {
75 }
76 if (*want_tx && (dma->tx_buffer == nullptr || dma->tx_samples == 0U)) {
78 }
79 if (*want_rx && (dma->rx_buffer == nullptr || dma->rx_samples == 0U)) {
81 }
82 return k_ra8_ok;
83}
84
103static ra8_err_t
104internal_start_tx_dma(volatile r_ssie_regs_t* reg, uint8_t channel, const ra8_ssie_dma_cfg_t* dma)
105{
106 const ra8_dmac_config_t tx_cfg = {
107 .src = (uint32_t)(uintptr_t)dma->tx_buffer,
108 .dst = (uint32_t)(uintptr_t)&reg->SSIFTDR,
109 .count = dma->tx_samples,
110 .width = k_ra8_dmac_width_word,
111 .src_inc = true,
112 .dst_inc = false,
113 };
114 const ra8_err_t tx_err = ra8_dmac_start(dma->tx_dma_channel, &tx_cfg);
115 if (tx_err == k_ra8_ok) {
116 g_ssie_runtime[channel].tx_dma_channel = dma->tx_dma_channel;
117 }
118 return tx_err;
119}
120
139static ra8_err_t
140internal_start_rx_dma(volatile r_ssie_regs_t* reg, uint8_t channel, const ra8_ssie_dma_cfg_t* dma)
141{
142 const ra8_dmac_config_t rx_cfg = {
143 .src = (uint32_t)(uintptr_t)&reg->SSIFRDR,
144 .dst = (uint32_t)(uintptr_t)dma->rx_buffer,
145 .count = dma->rx_samples,
146 .width = k_ra8_dmac_width_word,
147 .src_inc = false,
148 .dst_inc = true,
149 };
150 const ra8_err_t rx_err = ra8_dmac_start(dma->rx_dma_channel, &rx_cfg);
151 if (rx_err == k_ra8_ok) {
152 g_ssie_runtime[channel].rx_dma_channel = dma->rx_dma_channel;
153 }
154 return rx_err;
155}
156
171static void internal_unwind_tx_dma(uint8_t channel, uint8_t tx_dma_channel)
172{
173 (void)ra8_dmac_stop(tx_dma_channel);
174 g_ssie_runtime[channel].tx_dma_channel = k_ra8_ssie_dma_ch_unused;
175}
176
201 uint8_t channel,
202 const ra8_ssie_dma_cfg_t* dma,
203 bool want_tx,
204 bool want_rx)
205{
206 if (want_tx) {
207 const ra8_err_t tx_err = internal_start_tx_dma(reg, channel, dma);
208 /* GCOVR_EXCL_BR_START -- internal_start_tx_dma() error edge; the fake DMA seam always starts */
209 RA8_RETURN_ON_ERROR(tx_err, s_tag, "ssie_attach_dma: tx start");
210 /* GCOVR_EXCL_BR_STOP */
211 }
212 if (want_rx) {
213 const ra8_err_t rx_err = internal_start_rx_dma(reg, channel, dma);
214 if (rx_err != k_ra8_ok) {
215 if (want_tx) {
217 }
218 return rx_err;
219 }
220 }
221 return k_ra8_ok;
222}
223
225{
226 RA8_CHECK_NULL_PTR(dma, s_tag, "dma must not be nullptr");
227 volatile r_ssie_regs_t* reg = priv_ra8_ssie_internal_regs(channel);
228 if (reg == nullptr) {
230 }
231
232 bool want_tx = false;
233 bool want_rx = false;
234 const ra8_err_t v_err = internal_validate_dma_cfg(dma, &want_tx, &want_rx);
235 RA8_RETURN_ON_ERROR(v_err, s_tag, "ssie_attach_dma: bad cfg");
236
237 const ra8_err_t d_err = internal_attach_dma_dirs(reg, channel, dma, want_tx, want_rx);
238 /* GCOVR_EXCL_BR_START -- internal_attach_dma_dirs() error edge; both directions always attach */
239 RA8_RETURN_ON_ERROR(d_err, s_tag, "ssie_attach_dma: dir start");
240 /* GCOVR_EXCL_BR_STOP */
241
242 g_ssie_runtime[channel].dma_attached = true;
243 return k_ra8_ok;
244}
245
247{
248 if ((uint16_t)channel >= k_ra8_ssie_channel_count) {
250 }
251 ra8_ssie_runtime_t* rt = &g_ssie_runtime[channel];
253 (void)ra8_dmac_stop(rt->tx_dma_channel);
255 }
257 (void)ra8_dmac_stop(rt->rx_dma_channel);
259 }
260 rt->dma_attached = false;
261 return k_ra8_ok;
262}
263
264ra8_err_t ra8_ssie_attach_dma_pair(uint8_t channel, uint8_t tx_dma_channel, uint8_t rx_dma_channel)
265{
266 if ((uint16_t)channel >= k_ra8_ssie_channel_count) {
268 }
269 if (tx_dma_channel >= (uint8_t)k_ra8_ssie_dma_max_ch &&
270 rx_dma_channel >= (uint8_t)k_ra8_ssie_dma_max_ch) {
272 }
273 ra8_ssie_runtime_t* rt = &g_ssie_runtime[channel];
274 /* HUM Ch 46.4.1 "Operation in DMAC Transfer" p 3104:
275 * the SSIE issues TX-empty / RX-full requests to the DMAC; the
276 * driver only records which DMAC channels to free at detach. */
277 rt->tx_dma_channel =
278 (tx_dma_channel < k_ra8_ssie_dma_max_ch) ? tx_dma_channel : (uint8_t)k_ra8_ssie_dma_ch_unused;
279 rt->rx_dma_channel =
280 (rx_dma_channel < k_ra8_ssie_dma_max_ch) ? rx_dma_channel : (uint8_t)k_ra8_ssie_dma_ch_unused;
281 rt->dma_attached = true;
282 return k_ra8_ok;
283}
284
285ra8_err_t ra8_ssie_send_iso(uint8_t channel, const uint32_t* buffer, uint16_t samples)
286{
287 RA8_CHECK_NULL_PTR(buffer, s_tag, "buffer must not be nullptr");
288 volatile r_ssie_regs_t* reg = priv_ra8_ssie_internal_regs(channel);
289 if (reg == nullptr) {
291 }
292 /* Bounded loop: at most samples * k_ra8_ssie_fifo_depth FIFO probes.
293 * HUM Ch 46.2.4 "SSIFSR : FIFO Status Register" p 3083 (TDC field). */
294 uint16_t sent = 0U;
295 while (sent < samples) {
296 /* HUM Ch 46.2.4 "SSIFSR" p 3083 */
297 const uint8_t tdc = (uint8_t)((reg->SSIFSR & k_ra8_ssie_mask_tdc) >> k_ra8_ssie_shift_tdc);
298 if (tdc < (uint8_t)k_ra8_ssie_fifo_depth) {
299 /* HUM Ch 46.2.5 "SSIFTDR" p 3088 */
300 reg->SSIFTDR = buffer[sent];
301 ++sent;
302 }
303 /* In fake/host builds the TDC count never advances because
304 * no clock is consuming TX samples; treat the slot as drained
305 * immediately on every loop pass to remain bounded. */
306 }
307 /* W1C of TDE keeps the RDF bit. */
309 return k_ra8_ok;
310}
311
313ra8_ssie_recv_iso(uint8_t channel, uint32_t* buffer, uint16_t max_samples, uint16_t* out_got)
314{
315 RA8_CHECK_NULL_PTR(buffer, s_tag, "buffer must not be nullptr");
316 RA8_CHECK_NULL_PTR(out_got, s_tag, "out_got must not be nullptr");
317 volatile r_ssie_regs_t* reg = priv_ra8_ssie_internal_regs(channel);
318 if (reg == nullptr) {
320 }
321
322 uint16_t got = 0U;
323 for (uint16_t i = 0U; i < max_samples; ++i) {
324 /* HUM Ch 46.2.4 "SSIFSR" p 3083 */ /* (RDC field) */
325 const uint8_t rdc = (uint8_t)((reg->SSIFSR & k_ra8_ssie_mask_rdc) >> k_ra8_ssie_shift_rdc);
326 if (rdc == 0U) {
327 break;
328 }
329 /* HUM Ch 46.2.6 "SSIFRDR" p 3089 */
330 buffer[got] = reg->SSIFRDR;
331 ++got;
332 }
333 if (got > 0U) {
334 /* W1C RDF, keep TDE. */
336 }
337 *out_got = got;
338 return k_ra8_ok;
339}
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_RETURN_ON_ERROR(err, tag, message)
Early return on error, propagating the code upward.
Definition ra8_check.h:184
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
8-channel DMA controller (DMAC0) driver
@ k_ra8_dmac_width_word
32-bit transfers (DMTMD.SZ = 10b).
Definition ra8_dmac.h:53
ra8_err_t ra8_dmac_stop(uint8_t channel)
Disable a DMAC0 channel and drop its MSTP reference.
Definition ra8_dmac.c:432
ra8_err_t ra8_dmac_start(uint8_t channel, const ra8_dmac_config_t *cfg)
Programme and enable a DMAC0 channel.
Definition ra8_dmac.c:398
Error Code Definitions for ra8-firmware.
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Cross-translation-unit private helpers shared inside ra8_hal.
volatile r_ssie_regs_t * priv_ra8_ssie_internal_regs(uint8_t channel)
Validate an SSIE channel index and return its register block.
Definition ra8_ssie.c:101
@ k_ra8_ssie_dma_ch_unused
Sentinel: no DMA channel.
@ k_ra8_ssie_dma_max_ch
DMAC channel count (0..7).
ra8_ssie_runtime_t g_ssie_runtime[k_ra8_ssie_channel_count]
Per-channel SSIE runtime state, shared across the ra8_ssie split.
Definition ra8_ssie.c:73
Serial Sound Interface Enhanced (SSIE / I2S audio) driver.
Serial Sound Interface Enhanced (SSIE) register layout for the RA8D2.
@ k_ra8_ssie_shift_tdc
TDC[5:0] starts at bit 24.
@ k_ra8_ssie_shift_rdc
RDC[5:0] starts at bit 8.
@ k_ra8_ssie_channel_count
SSIE0 + SSIE1.
@ k_ra8_ssie_fifo_depth
32-stage FIFO per dir.
@ k_ra8_ssie_mask_tde_clear
Keep RDF while clearing TDE.
@ k_ra8_ssie_mask_rdf_clear
Keep TDE while clearing RDF.
@ k_ra8_ssie_mask_tdc
TDC[5:0] @ [29:24].
@ k_ra8_ssie_mask_rdc
RDC[5:0] @ [13:8].
static ra8_err_t internal_start_rx_dma(volatile r_ssie_regs_t *reg, uint8_t channel, const ra8_ssie_dma_cfg_t *dma)
Kick off the RX DMAC for an SSIE channel.
ra8_err_t ra8_ssie_detach_dma(uint8_t channel)
Tear down DMAC channels attached to the SSIE FIFOs.
static ra8_err_t internal_attach_dma_dirs(volatile r_ssie_regs_t *reg, uint8_t channel, const ra8_ssie_dma_cfg_t *dma, bool want_tx, bool want_rx)
Run the per-direction DMA setup (TX then RX) with rollback.
ra8_err_t ra8_ssie_attach_dma(uint8_t channel, const ra8_ssie_dma_cfg_t *dma)
Attach DMAC channels to the SSIE TX and/or RX FIFOs.
ra8_err_t ra8_ssie_send_iso(uint8_t channel, const uint32_t *buffer, uint16_t samples)
Block-mode isochronous send.
ra8_err_t ra8_ssie_attach_dma_pair(uint8_t channel, uint8_t tx_dma_channel, uint8_t rx_dma_channel)
Pair an SSIE channel's TX + RX FIFOs to DMAC channels.
static ra8_err_t internal_validate_dma_cfg(const ra8_ssie_dma_cfg_t *dma, bool *want_tx, bool *want_rx)
Validate the DMA-attach descriptor against TX/RX intent.
ra8_err_t ra8_ssie_recv_iso(uint8_t channel, uint32_t *buffer, uint16_t max_samples, uint16_t *out_got)
Block-mode isochronous receive.
static void internal_unwind_tx_dma(uint8_t channel, uint8_t tx_dma_channel)
Roll back a TX DMAC start when the RX side fails.
static ra8_err_t internal_start_tx_dma(volatile r_ssie_regs_t *reg, uint8_t channel, const ra8_ssie_dma_cfg_t *dma)
Kick off the TX DMAC for an SSIE channel.
Per-channel SSIE register window.
volatile uint32_t SSIFTDR
+0x18 Transmit FIFO data.
volatile uint32_t SSIFSR
+0x14 FIFO status register.
volatile uint32_t SSIFRDR
+0x1C Receive FIFO data.
DMAC channel configuration.
Definition ra8_dmac.h:121
DMA pump descriptor for ra8_ssie_attach_dma.
Definition ra8_ssie.h:286
uint8_t rx_dma_channel
DMAC channel to feed RX (>=8 = unused).
Definition ra8_ssie.h:288
uint16_t rx_samples
RX sample count (must be > 0 if rx).
Definition ra8_ssie.h:292
uint32_t * tx_buffer
Source buffer for TX (32-bit samples).
Definition ra8_ssie.h:289
uint32_t * rx_buffer
Destination buffer for RX.
Definition ra8_ssie.h:290
uint16_t tx_samples
TX sample count (must be > 0 if tx).
Definition ra8_ssie.h:291
uint8_t tx_dma_channel
DMAC channel to drain TX (>=8 = unused).
Definition ra8_ssie.h:287
Per-channel runtime bookkeeping.
uint8_t tx_dma_channel
DMAC channel pumping TX (or unused).
bool dma_attached
true after ra8_ssie_attach_dma.
uint8_t rx_dma_channel
DMAC channel pumping RX (or unused).