1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/lib/aic/src/x1600.cc Tue Oct 24 17:34:51 2023 +0200
1.3 @@ -0,0 +1,466 @@
1.4 +/*
1.5 + * AIC support for the X1600.
1.6 + *
1.7 + * Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <l4/devices/aic-x1600.h>
1.26 +#include <l4/devices/dma.h>
1.27 +#include <l4/devices/hw_mmio_register_block.h>
1.28 +
1.29 +#include <l4/sys/icu.h>
1.30 +#include <l4/util/util.h>
1.31 +
1.32 +#include <stdio.h>
1.33 +
1.34 +enum Regs
1.35 +{
1.36 + Aic_config = 0x000, // AICFR
1.37 + Aic_control = 0x004, // AICCR
1.38 + Aic_i2s_msb_control = 0x010, // I2SCR
1.39 + Aic_fifo_status = 0x014, // AICSR
1.40 + Aic_i2s_msb_status = 0x01c, // I2SSR
1.41 + Aic_i2s_msb_divider = 0x030, // I2SDIV
1.42 + Aic_fifo_data = 0x034, // AICDR
1.43 + Aic_loop_data = 0x038, // AICLR
1.44 + Aic_tfifo_loop = 0x03c, // AICTFLR
1.45 +};
1.46 +
1.47 +enum Aic_config_bits : unsigned
1.48 +{
1.49 + Aic_enable_big_endian = 0x1000, // MSB
1.50 + Aic_independent_clock = 0x0100, // DMODE
1.51 + Aic_shared_clock = 0x0000, // DMODE
1.52 + Aic_last_sample_uflow = 0x0040, // LSMP
1.53 + Aic_select_i2s_msb = 0x0010, // AUSEL
1.54 + Aic_reset = 0x0008, // RST
1.55 + Aic_tmaster = 0x0004, // TMASTER
1.56 + Aic_rmaster = 0x0002, // RMASTER
1.57 + Aic_enable = 0x0001, // ENB
1.58 +
1.59 + Aic_rfifo_thold_bit = 24, // RFTH
1.60 + Aic_tfifo_thold_bit = 16, // TFTH
1.61 +};
1.62 +
1.63 +enum Aic_fifo_limits : unsigned
1.64 +{
1.65 + Aic_rfifo_max = 15,
1.66 + Aic_tfifo_max = 31,
1.67 +};
1.68 +
1.69 +enum Aic_control_bits : unsigned
1.70 +{
1.71 + Aic_enable_tfifo_loop_overrun = 0x80000000, // ETFLOR
1.72 + Aic_enable_tfifo_loop_request = 0x40000000, // ETFLS
1.73 + Aic_packed_data = 0x10000000, // PACK16
1.74 +
1.75 + Aic_channel_number_mask = 0x07000000, // CHANNEL
1.76 + Aic_channel_mono = 0x00000000,
1.77 + Aic_channel_stereo = 0x01000000,
1.78 +
1.79 + Aic_enable_tfifo_loop = 0x00800000, // ETFL
1.80 + Aic_enable_tfifo_loop_dma = 0x00400000, // TLDMS
1.81 +
1.82 + Aic_output_size_mask = 0x00380000, // OSS
1.83 + Aic_output_size_8bit = 0x00000000, // 0
1.84 + Aic_output_size_16bit = 0x00080000, // 1
1.85 + Aic_output_size_18bit = 0x00100000, // 2
1.86 + Aic_output_size_20bit = 0x00180000, // 3
1.87 + Aic_output_size_24bit = 0x00200000, // 4
1.88 +
1.89 + Aic_input_size_mask = 0x00070000, // ISS
1.90 + Aic_input_size_8bit = 0x00000000, // 0
1.91 + Aic_input_size_16bit = 0x00010000, // 1
1.92 + Aic_input_size_18bit = 0x00020000, // 2
1.93 + Aic_input_size_20bit = 0x00030000, // 3
1.94 + Aic_input_size_24bit = 0x00040000, // 4
1.95 +
1.96 + Aic_enable_recv_dma = 0x00008000, // RDMS
1.97 + Aic_enable_trans_dma = 0x00004000, // TDMS
1.98 +
1.99 + Aic_mono_control_mask = 0x00003000, // MONOCTR
1.100 + Aic_mono_control_both = 0x00000000, // 0
1.101 + Aic_mono_control_right = 0x00001000, // 1
1.102 + Aic_mono_control_left = 0x00002000, // 2
1.103 +
1.104 + Aic_trans_byte_swap = 0x00000400, // ENDSW
1.105 + Aic_tfifo_flush = 0x00000100, // TFLUSH
1.106 + Aic_rfifo_flush = 0x00000080, // RFLUSH
1.107 + Aic_enable_rfifo_overrun = 0x00000040, // EROR
1.108 + Aic_enable_tfifo_underrun = 0x00000020, // ETUR
1.109 + Aic_enable_rfifo_request = 0x00000010, // ERFS
1.110 + Aic_enable_tfifo_request = 0x00000008, // ETFS
1.111 + Aic_enable_loopback = 0x00000004, // ENLBF
1.112 + Aic_enable_playback = 0x00000002, // ERPL
1.113 + Aic_enable_record = 0x00000001, // EREC
1.114 +};
1.115 +
1.116 +enum Aic_i2s_msb_control_bits : unsigned
1.117 +{
1.118 + Aic_i2s_msb_right_first = 0x20000, // RFIRST
1.119 + Aic_i2s_msb_switch_lr = 0x10000, // SWLH
1.120 + Aic_select_msb_justified = 0x00001, // AMSL
1.121 + Aic_select_i2s = 0x00000, // AMSL
1.122 +};
1.123 +
1.124 +enum Aic_fifo_status_bits : unsigned
1.125 +{
1.126 + Aic_rfifo_overrun = 0x40, // ROR
1.127 + Aic_tfifo_underrun = 0x20, // TUR
1.128 + Aic_rfifo_request = 0x10, // RFS
1.129 + Aic_tfifo_request = 0x08, // TFS
1.130 + Aic_tfifo_loop_overrun = 0x04, // TFLOR
1.131 + Aic_tfifo_loop_request = 0x02, // TFLS
1.132 +
1.133 + Aic_rfifo_level_bit = 24, // RFL
1.134 + Aic_tfifo_loop_level_bit = 15, // TFLL
1.135 + Aic_tfifo_level_bit = 8, // TFL
1.136 +};
1.137 +
1.138 +enum Aic_i2s_msb_status_bits : unsigned
1.139 +{
1.140 + Aic_trans_channel_busy = 0x20, // CHBSY
1.141 + Aic_trans_busy = 0x10, // TBSY
1.142 + Aic_recv_busy = 0x08, // RBSY
1.143 + Aic_busy = 0x04, // BSY
1.144 +};
1.145 +
1.146 +enum Aic_i2s_msb_divider_bits : unsigned
1.147 +{
1.148 + Aic_recv_divider_mask = 0x1ff0000, // RDIV
1.149 + Aic_trans_divider_mask = 0x00001ff, // TDIV
1.150 +
1.151 + Aic_recv_divider_bit = 16, // RDIV
1.152 + Aic_trans_divider_bit = 0, // TDIV
1.153 +};
1.154 +
1.155 +enum Aic_i2s_msb_divider_limits : unsigned
1.156 +{
1.157 + Aic_recv_divider_limit = 0x1ff, // RDIV
1.158 + Aic_trans_divider_limit = 0x1ff, // TDIV
1.159 +};
1.160 +
1.161 +enum Aic_fifo_data_limits : unsigned
1.162 +{
1.163 + Aic_fifo_data_limit = 0xffffff, // DATA
1.164 +};
1.165 +
1.166 +enum Aic_loop_data_limits : unsigned
1.167 +{
1.168 + Aic_loop_data_limit = 0xffffff, // DATALP
1.169 +};
1.170 +
1.171 +enum Aic_tfifo_loop_limits : unsigned
1.172 +{
1.173 + Aic_tfifo_loop_limit = 0xf, // TFLTH
1.174 +};
1.175 +
1.176 +
1.177 +
1.178 +// Initialise a channel.
1.179 +
1.180 +Aic_x1600_channel::Aic_x1600_channel(l4_addr_t aic_start, l4_addr_t start,
1.181 + enum Clock_identifiers clock_rx,
1.182 + enum Clock_identifiers clock_tx,
1.183 + Cpm_x1600_chip *cpm,
1.184 + Dma_x1600_channel *dma)
1.185 +: _aic_start(aic_start), _clock_rx(clock_rx), _clock_tx(clock_tx), _cpm(cpm), _dma(dma)
1.186 +{
1.187 + _regs = new Hw::Mmio_register_block<32>(start);
1.188 + _cpm->start_clock(clock_rx);
1.189 + _cpm->start_clock(clock_tx);
1.190 + config();
1.191 +}
1.192 +
1.193 +/*
1.194 +I2S configuration.
1.195 +
1.196 +The system clock (SYS_CLK) for I2S in the X1600 is issued by the X1600 as the
1.197 +master/system clock (MCLK) externally to the DAC/codec.
1.198 +
1.199 +SYS_CLK is divided to obtain BIT_CLK which is issued by the X1600 as BCLK.
1.200 +This is dependent on the actual sample rate.
1.201 +
1.202 +LRCLK indicates the left/right channel for the issued data.
1.203 +
1.204 +Where clocks (MCLK, BCLK, LRCLK) are issued only by the X1600, share mode
1.205 +is used. Otherwise, independent (6-line) mode is used.
1.206 +
1.207 +To drive the MAX9835A, no MCLK is needed.
1.208 +
1.209 +Initialisation involves the following:
1.210 +
1.211 +- Configure MCLK, BCLK, LRCLK as outputs
1.212 +- Select I2S in Aic_i2s_msb_control (deselecting Aic_select_msb_justified)
1.213 +- Set master mode in Aic_config (Aic_tmaster, also Aic_rmaster if independent
1.214 + mode is used)
1.215 +- Set the dividers in Aic_i2s_msb_divider for BCLK using the configured I2S
1.216 + clock frequency
1.217 +- Perform a reset by writing to Aic_reset in Aic_config, if necessary
1.218 +- The MAX9835A may not need any configuration using I2C or some other bus
1.219 +*/
1.220 +
1.221 +void
1.222 +Aic_x1600_channel::config()
1.223 +{
1.224 + // NOTE: Setting transmit request threshold to 31 (15 * 2 + 1).
1.225 +
1.226 + printf("config = %08x\n", (uint32_t) _regs[Aic_config]);
1.227 +
1.228 + _regs[Aic_config] = Aic_shared_clock | Aic_select_i2s_msb | Aic_reset |
1.229 + Aic_tmaster | Aic_enable | (15 << Aic_tfifo_thold_bit);
1.230 +
1.231 + _regs[Aic_i2s_msb_control] = Aic_select_i2s;
1.232 +
1.233 + printf("config = %08x\n", (uint32_t) _regs[Aic_config]);
1.234 +}
1.235 +
1.236 +/*
1.237 +Clock configuration:
1.238 +
1.239 +- SYS_CLK (MCLK) is Clock_i2s0_tx (or Clock_i2s0_rx)
1.240 +- BIT_CLK (BCLK) is MCLK divided by the appropriate AIC/I2S divider
1.241 +- BCLK should be 64 times the sample rate according to the manual
1.242 +
1.243 +This suggests that the X1600 always produces data for two 32-bit channels
1.244 +regardless of the input data characteristics.
1.245 +*/
1.246 +
1.247 +void
1.248 +Aic_x1600_channel::set_divider(uint32_t divider, uint32_t mask, uint32_t limit,
1.249 + int bit)
1.250 +{
1.251 + if (divider <= limit)
1.252 + _regs[Aic_i2s_msb_divider] = (_regs[Aic_i2s_msb_divider] & ~mask) |
1.253 + (divider << bit);
1.254 +}
1.255 +
1.256 +void
1.257 +Aic_x1600_channel::set_recv_frequency(uint32_t sample_rate)
1.258 +{
1.259 + set_divider(_cpm->get_frequency(_clock_rx) / (sample_rate * 32 * 2),
1.260 + Aic_recv_divider_mask, Aic_recv_divider_limit, Aic_recv_divider_bit);
1.261 +}
1.262 +
1.263 +void
1.264 +Aic_x1600_channel::set_trans_frequency(uint32_t sample_rate)
1.265 +{
1.266 + printf("tx %d / (freq %d * %d * 2) = %d vs. %d\n", _cpm->get_frequency(_clock_tx),
1.267 + sample_rate, 32,
1.268 + _cpm->get_frequency(_clock_tx) / (sample_rate * 32 * 2),
1.269 + Aic_trans_divider_limit);
1.270 +
1.271 + set_divider(_cpm->get_frequency(_clock_tx) / (sample_rate * 32 * 2),
1.272 + Aic_trans_divider_mask, Aic_trans_divider_limit, Aic_trans_divider_bit);
1.273 +}
1.274 +
1.275 +uint32_t
1.276 +Aic_x1600_channel::encode_input_size(uint8_t sample_size)
1.277 +{
1.278 + switch (sample_size)
1.279 + {
1.280 + case 16:
1.281 + return Aic_input_size_16bit;
1.282 +
1.283 + case 18:
1.284 + return Aic_input_size_18bit;
1.285 +
1.286 + case 20:
1.287 + return Aic_input_size_20bit;
1.288 +
1.289 + case 24:
1.290 + return Aic_input_size_24bit;
1.291 +
1.292 + default:
1.293 + return Aic_input_size_8bit;
1.294 + }
1.295 +}
1.296 +
1.297 +uint32_t
1.298 +Aic_x1600_channel::encode_output_size(uint8_t sample_size)
1.299 +{
1.300 + switch (sample_size)
1.301 + {
1.302 + case 16:
1.303 + return Aic_output_size_16bit;
1.304 +
1.305 + case 18:
1.306 + return Aic_output_size_18bit;
1.307 +
1.308 + case 20:
1.309 + return Aic_output_size_20bit;
1.310 +
1.311 + case 24:
1.312 + return Aic_output_size_24bit;
1.313 +
1.314 + default:
1.315 + return Aic_output_size_8bit;
1.316 + }
1.317 +}
1.318 +
1.319 +// Obtain a DMA-accessible buffer for sample transfers.
1.320 +
1.321 +long
1.322 +Aic_x1600_channel::get_buffer(uint32_t count, l4_addr_t *addr)
1.323 +{
1.324 + printf("get_buffer(%d)\n", count);
1.325 + long err = get_dma_region(count, 8, &_vaddr, &_paddr, &_mem);
1.326 +
1.327 + if (err)
1.328 + return err;
1.329 +
1.330 + // Set the region size as the requested size, not any allocated size.
1.331 +
1.332 + _size = count;
1.333 + *addr = _vaddr;
1.334 + printf("size = %d\n", _size);
1.335 + return L4_EOK;
1.336 +}
1.337 +
1.338 +// Transfer a sample using the given byte count (total sample size), sample rate
1.339 +// (or frequency), sample unit size (or resolution, width).
1.340 +
1.341 +unsigned int
1.342 +Aic_x1600_channel::transfer(uint32_t count, uint32_t sample_rate, uint8_t sample_size)
1.343 +{
1.344 + printf("transfer(%d, %d, %d)\n", count, sample_rate, sample_size);
1.345 +
1.346 + if (count > _size)
1.347 + return 0;
1.348 +
1.349 + /* To play a sample:
1.350 +
1.351 + - Configure the sample size using the Aic_output_size settings in Aic_control
1.352 + - The sample size will be 16- or 24-bit for the MAX9835A, with the X1600 not
1.353 + supporting 32-bit samples
1.354 + - Configure the number of channels in Aic_control (Aic_channel_mono or
1.355 + Aic_channel_stereo)
1.356 + - For 16-bit samples, select Aic_packed_data in Aic_control if appropriate,
1.357 + possibly not for the MAX983A
1.358 + - If two channels are used, select Aic_i2s_msb_right_first in
1.359 + Aic_i2s_msb_control if appropriate, as well as Aic_i2s_msb_switch_lr if
1.360 + switching the channels
1.361 + - Reconfigure the dividers if appropriate
1.362 + - For DMA, set Aic_enable_trans_dma in Aic_control and the transmit FIFO
1.363 + threshold value
1.364 + */
1.365 +
1.366 + // NOTE: Introduce sample size.
1.367 +
1.368 + set_trans_frequency(sample_rate);
1.369 +
1.370 + /* NOTE: The MAX98357A might require stereo input data, but perhaps the
1.371 + peripheral can generate the appropriate LRCLK to make it accept mono
1.372 + data. */
1.373 +
1.374 + _regs[Aic_control] = encode_output_size(sample_size) |
1.375 + Aic_channel_mono |
1.376 + Aic_enable_trans_dma |
1.377 + Aic_enable_playback;
1.378 +
1.379 + printf("control = %08x\n", (uint32_t) _regs[Aic_control]);
1.380 + printf("config = %08x\n", (uint32_t) _regs[Aic_config]);
1.381 + printf("divider = %d\n", (uint32_t) _regs[Aic_i2s_msb_divider]);
1.382 + printf("status = %08x\n", (uint32_t) _regs[Aic_i2s_msb_status]);
1.383 + printf("fifo = %08x\n", (uint32_t) _regs[Aic_fifo_status]);
1.384 +
1.385 + // Transfer from the allocated region to the FIFO. Use an incrementing source
1.386 + // address with source width, destination width and transfer unit reflecting
1.387 + // the sample size, and with transfers initiated by an empty AIC transmit
1.388 + // FIFO.
1.389 +
1.390 + printf("transfer from %llx to %lx\n", _paddr, _aic_start + Aic_fifo_data);
1.391 +
1.392 + unsigned int sample_unit = (sample_size == 8) ? 1 :
1.393 + (sample_size == 16) ? 2 :
1.394 + 4;
1.395 + unsigned int unit_count = count / sample_unit;
1.396 +
1.397 + unsigned int to_transfer = _dma->transfer(_paddr,
1.398 + _aic_start + Aic_fifo_data,
1.399 + unit_count,
1.400 + true,
1.401 + false,
1.402 + sample_unit,
1.403 + sample_unit,
1.404 + sample_unit,
1.405 + Dma_request_aic_out);
1.406 +
1.407 + printf("status = %08x\n", (uint32_t) _regs[Aic_i2s_msb_status]);
1.408 + printf("fifo = %08x\n", (uint32_t) _regs[Aic_fifo_status]);
1.409 +
1.410 + unsigned int transferred = 0;
1.411 +
1.412 + if (to_transfer)
1.413 + transferred = to_transfer ? (unit_count - _dma->wait()) * sample_unit : 0;
1.414 +
1.415 +#if 0
1.416 + if (transferred)
1.417 + while (_regs[Aic_fifo_status]);
1.418 +
1.419 + _regs[Aic_control] = _regs[Aic_control] & ~Aic_enable_playback;
1.420 +#endif
1.421 +
1.422 + return transferred;
1.423 +}
1.424 +
1.425 +
1.426 +
1.427 +// Initialise the AIC/I2S controller.
1.428 +
1.429 +Aic_x1600_chip::Aic_x1600_chip(l4_addr_t aic_start, l4_addr_t start, l4_addr_t end,
1.430 + Cpm_x1600_chip *cpm)
1.431 +: _aic_start(aic_start), _start(start), _end(end), _cpm(cpm)
1.432 +{
1.433 +}
1.434 +
1.435 +// Obtain a channel object.
1.436 +
1.437 +Aic_x1600_channel *
1.438 +Aic_x1600_chip::get_channel(uint8_t channel, Dma_x1600_channel *dma)
1.439 +{
1.440 + if (channel < 1)
1.441 + return new Aic_x1600_channel(_aic_start, _start, Clock_i2s0_rx, Clock_i2s0_tx, _cpm, dma);
1.442 + else
1.443 + throw -L4_EINVAL;
1.444 +}
1.445 +
1.446 +
1.447 +
1.448 +// C language interface functions.
1.449 +
1.450 +void *x1600_aic_init(l4_addr_t aic_start, l4_addr_t start, l4_addr_t end, void *cpm)
1.451 +{
1.452 + return (void *) new Aic_x1600_chip(aic_start, start, end, static_cast<Cpm_x1600_chip *>(cpm));
1.453 +}
1.454 +
1.455 +void *x1600_aic_get_channel(void *aic, uint8_t channel, void *dma)
1.456 +{
1.457 + return static_cast<Aic_x1600_chip *>(aic)->get_channel(channel,
1.458 + static_cast<Dma_x1600_channel *>(dma));
1.459 +}
1.460 +
1.461 +long x1600_aic_get_buffer(void *channel, uint32_t count, l4_addr_t *addr)
1.462 +{
1.463 + return static_cast<Aic_x1600_channel *>(channel)->get_buffer(count, addr);
1.464 +}
1.465 +
1.466 +unsigned int x1600_aic_transfer(void *channel, uint32_t count, uint32_t sample_rate, uint8_t sample_size)
1.467 +{
1.468 + return static_cast<Aic_x1600_channel *>(channel)->transfer(count, sample_rate, sample_size);
1.469 +}