# HG changeset patch # User Paul Boddie # Date 1699381232 -3600 # Node ID 89d597f10dc6399695e27b3e4053bba0e32d7567 # Parent edb87791dfa75e20a355d742e7b5e1c2c4fbd2d1 Introduced support for the SPI peripheral on the JZ4780. Removed DMA buffer handling from the AIC/I2S peripheral support, changing the transfer operation to use an arbitrary physical address. Changed the hw_info test program to support use of the SPI peripheral and GPIO-driven SPI communication in the same session. Added a test picture in RGB16 format for use with the hw_info program. diff -r edb87791dfa7 -r 89d597f10dc6 conf/landfall-examples/P8021407-crop-240x240.data Binary file conf/landfall-examples/P8021407-crop-240x240.data has changed diff -r edb87791dfa7 -r 89d597f10dc6 conf/landfall-examples/mips-jz4780-info.io --- a/conf/landfall-examples/mips-jz4780-info.io Fri Nov 03 19:58:11 2023 +0100 +++ b/conf/landfall-examples/mips-jz4780-info.io Tue Nov 07 19:20:32 2023 +0100 @@ -10,6 +10,7 @@ DMA = wrap(hw:match("jz4780-dma")); GPIO = wrap(hw:match("jz4780-gpio")); I2C = wrap(hw:match("jz4780-i2c")); + SSI = wrap(hw:match("jz4780-ssi")); } Io.add_vbus("common", bus) diff -r edb87791dfa7 -r 89d597f10dc6 conf/landfall-examples/mips-jz4780-info.list --- a/conf/landfall-examples/mips-jz4780-info.list Fri Nov 03 19:58:11 2023 +0100 +++ b/conf/landfall-examples/mips-jz4780-info.list Tue Nov 07 19:20:32 2023 +0100 @@ -13,3 +13,4 @@ module ned module ex_jz4780_info module piano-3.raw +module P8021407-crop-240x240.data diff -r edb87791dfa7 -r 89d597f10dc6 conf/landfall-examples/mips-x1600-info.io --- a/conf/landfall-examples/mips-x1600-info.io Fri Nov 03 19:58:11 2023 +0100 +++ b/conf/landfall-examples/mips-x1600-info.io Tue Nov 07 19:20:32 2023 +0100 @@ -10,6 +10,7 @@ DMA = wrap(hw:match("x1600-dma")); GPIO = wrap(hw:match("x1600-gpio")); I2C = wrap(hw:match("x1600-i2c")); + SSI = wrap(hw:match("x1600-ssi")); } Io.add_vbus("common", bus) diff -r edb87791dfa7 -r 89d597f10dc6 pkg/devices/lib/Makefile --- a/pkg/devices/lib/Makefile Fri Nov 03 19:58:11 2023 +0100 +++ b/pkg/devices/lib/Makefile Tue Nov 07 19:20:32 2023 +0100 @@ -5,14 +5,14 @@ include $(L4DIR)/mk/subdir.mk -aic: cpm gpio +aic: cpm dma gpio cpm: common dma: common gpio: common hdmi: panel -i2c: cpm gpio +i2c: cpm dma gpio keypad: common lcd: common panel: lcd pwm: common -spi: gpio +spi: cpm dma gpio diff -r edb87791dfa7 -r 89d597f10dc6 pkg/devices/lib/aic/include/aic-x1600.h --- a/pkg/devices/lib/aic/include/aic-x1600.h Fri Nov 03 19:58:11 2023 +0100 +++ b/pkg/devices/lib/aic/include/aic-x1600.h Tue Nov 07 19:20:32 2023 +0100 @@ -44,13 +44,6 @@ Cpm_x1600_chip *_cpm; Dma_x1600_channel *_dma; - /* Buffer management. */ - - unsigned int _size = 0; - l4_addr_t _vaddr = 0; - l4re_dma_space_dma_addr_t _paddr = 0; - l4_cap_idx_t _mem = L4_INVALID_CAP; - public: explicit Aic_x1600_channel(l4_addr_t aic_start, l4_addr_t start, enum Clock_identifiers clock_rx, @@ -58,8 +51,8 @@ Cpm_x1600_chip *cpm, Dma_x1600_channel *dma); - long get_buffer(uint32_t count, l4_addr_t *addr); - uint32_t transfer(uint32_t count, uint32_t sample_rate, uint8_t sample_size); + uint32_t transfer(l4re_dma_space_dma_addr_t paddr, uint32_t count, + uint32_t sample_rate, uint8_t sample_size); private: void disable(); @@ -100,8 +93,8 @@ void *x1600_aic_get_channel(void *aic, uint8_t channel, void *dma); -long x1600_aic_get_buffer(void *channel, uint32_t count, l4_addr_t *addr); - -uint32_t x1600_aic_transfer(void *channel, uint32_t count, uint32_t sample_rate, uint8_t sample_size); +uint32_t x1600_aic_transfer(void *channel, l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint32_t sample_rate, + uint8_t sample_size); EXTERN_C_END diff -r edb87791dfa7 -r 89d597f10dc6 pkg/devices/lib/aic/src/x1600.cc --- a/pkg/devices/lib/aic/src/x1600.cc Fri Nov 03 19:58:11 2023 +0100 +++ b/pkg/devices/lib/aic/src/x1600.cc Tue Nov 07 19:20:32 2023 +0100 @@ -312,32 +312,13 @@ } } -/* Obtain a DMA-accessible buffer for sample transfers. */ - -long -Aic_x1600_channel::get_buffer(uint32_t count, l4_addr_t *addr) -{ - long err = get_dma_region(count, 8, &_vaddr, &_paddr, &_mem); - - if (err) - return err; - - // Set the region size as the requested size, not any allocated size. - - _size = count; - *addr = _vaddr; - return L4_EOK; -} - /* Transfer a sample using the given byte count (total sample size), sample rate (or frequency), sample unit size (or resolution, width). */ uint32_t -Aic_x1600_channel::transfer(uint32_t count, uint32_t sample_rate, uint8_t sample_size) +Aic_x1600_channel::transfer(l4re_dma_space_dma_addr_t paddr, uint32_t count, + uint32_t sample_rate, uint8_t sample_size) { - if (count > _size) - return 0; - /* To play a sample: - Configure the sample size using the Aic_output_size settings in Aic_control @@ -375,7 +356,7 @@ uint32_t sample_unit = (sample_size == 8) ? 1 : (sample_size == 16) ? 2 : 4; uint32_t unit_count = count / sample_unit; - uint32_t to_transfer = _dma->transfer(_paddr, + uint32_t to_transfer = _dma->transfer(paddr, _aic_start + Aic_fifo_data, unit_count, true, @@ -436,12 +417,10 @@ static_cast(dma)); } -long x1600_aic_get_buffer(void *channel, uint32_t count, l4_addr_t *addr) +uint32_t x1600_aic_transfer(void *channel, l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint32_t sample_rate, + uint8_t sample_size) { - return static_cast(channel)->get_buffer(count, addr); + return static_cast(channel)->transfer(paddr, count, + sample_rate, sample_size); } - -uint32_t x1600_aic_transfer(void *channel, uint32_t count, uint32_t sample_rate, uint8_t sample_size) -{ - return static_cast(channel)->transfer(count, sample_rate, sample_size); -} diff -r edb87791dfa7 -r 89d597f10dc6 pkg/devices/lib/spi/include/spi-gpio.h --- a/pkg/devices/lib/spi/include/spi-gpio.h Fri Nov 03 19:58:11 2023 +0100 +++ b/pkg/devices/lib/spi/include/spi-gpio.h Tue Nov 07 19:20:32 2023 +0100 @@ -19,11 +19,11 @@ * Boston, MA 02110-1301, USA */ +#pragma once + #include #include -#pragma once - #ifdef __cplusplus @@ -38,15 +38,13 @@ int _data_pin; Hw::Gpio_chip *_enable_device; int _enable_pin; - uint32_t _frequency; + uint64_t _frequency; public: - /* Associate the device with a particular memory region. */ - explicit Spi_gpio(Hw::Gpio_chip *clock_device, int clock_pin, Hw::Gpio_chip *data_device, int data_pin, Hw::Gpio_chip *enable_device, int enable_pin, - uint32_t frequency = 0); + uint64_t frequency = 0); void send(int bytes, const uint8_t data[]); }; @@ -62,7 +60,7 @@ void *spi_gpio_get_channel(void *clock_chip, int clock_pin, void *data_chip, int data_pin, void *enable_chip, int enable_pin, - uint32_t frequency); + uint64_t frequency); void spi_gpio_send(void *channel, int bytes, const uint8_t data[]); diff -r edb87791dfa7 -r 89d597f10dc6 pkg/devices/lib/spi/include/spi-jz4780.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/spi/include/spi-jz4780.h Tue Nov 07 19:20:32 2023 +0100 @@ -0,0 +1,105 @@ +/* + * Perform SPI communication using the JZ4780 SPI peripheral. + * + * Copyright (C) 2023 Paul Boddie + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include +#include + + + +#ifdef __cplusplus + +#include +#include +#include + +/* SPI peripheral channel. */ + +class Spi_jz4780_channel +{ + Hw::Register_block<32> _regs; + + /* Initialisation parameters. */ + + l4_addr_t _spi_start; + enum Clock_identifiers _clock; + Cpm_jz4780_chip *_cpm; + Dma_jz4780_channel *_dma; + enum Dma_jz4780_request_type _request_type; + uint64_t _frequency; + + void configure_transfer(uint8_t char_size); + +public: + explicit Spi_jz4780_channel(l4_addr_t spi_start, l4_addr_t start, + enum Clock_identifiers clock, + Cpm_jz4780_chip *cpm, + Dma_jz4780_channel *dma, + enum Dma_jz4780_request_type request_type, + uint64_t frequency); + + /* DMA operations. */ + + uint32_t transfer(l4re_dma_space_dma_addr_t paddr, uint32_t count, + uint8_t unit_size, uint8_t char_size); + + /* Convenience operations. */ + + uint32_t send(uint32_t bytes, const uint8_t data[], uint8_t unit_size, + uint8_t char_size); +}; + +/* SPI peripheral. */ + +class Spi_jz4780_chip +{ +private: + l4_addr_t _spi_start, _start, _end; + Cpm_jz4780_chip *_cpm; + +public: + explicit Spi_jz4780_chip(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end, Cpm_jz4780_chip *cpm); + + Spi_jz4780_channel *get_channel(uint8_t channel, Dma_jz4780_channel *dma, uint64_t frequency); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *jz4780_spi_init(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end, + void *cpm); + +void *jz4780_spi_get_channel(void *spi, uint8_t channel, void *dma, uint64_t frequency); + +uint32_t jz4780_spi_send(void *channel, uint32_t bytes, const uint8_t data[], + uint8_t unit_size, uint8_t char_size); + +uint32_t jz4780_spi_transfer(void *channel, l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint8_t unit_size, uint8_t char_size); + +EXTERN_C_END diff -r edb87791dfa7 -r 89d597f10dc6 pkg/devices/lib/spi/src/Makefile --- a/pkg/devices/lib/spi/src/Makefile Fri Nov 03 19:58:11 2023 +0100 +++ b/pkg/devices/lib/spi/src/Makefile Tue Nov 07 19:20:32 2023 +0100 @@ -4,10 +4,10 @@ TARGET = libspi.o.a libspi.o.so PC_FILENAME := libdrivers-spi -SRC_CC := gpio.cc +SRC_CC := gpio.cc jz4780.cc PRIVATE_INCDIR += $(PKGDIR)/lib/spi/include -REQUIRES_LIBS := l4re_c l4re_c-util libdrivers-common libdrivers-gpio +REQUIRES_LIBS := l4re_c l4re_c-util libdrivers-common libdrivers-cpm libdrivers-dma libdrivers-gpio include $(L4DIR)/mk/lib.mk diff -r edb87791dfa7 -r 89d597f10dc6 pkg/devices/lib/spi/src/gpio.cc --- a/pkg/devices/lib/spi/src/gpio.cc Fri Nov 03 19:58:11 2023 +0100 +++ b/pkg/devices/lib/spi/src/gpio.cc Tue Nov 07 19:20:32 2023 +0100 @@ -27,7 +27,7 @@ Spi_gpio::Spi_gpio(Hw::Gpio_chip *clock_device, int clock_pin, Hw::Gpio_chip *data_device, int data_pin, Hw::Gpio_chip *enable_device, int enable_pin, - uint32_t frequency) + uint64_t frequency) : _clock_device(clock_device), _clock_pin(clock_pin), _data_device(data_device), @@ -41,7 +41,7 @@ _enable_device->setup(_enable_pin, Hw::Gpio_chip::Output, 1); } -/* Send a SPI command. */ +/* Send a byte sequence. */ void Spi_gpio::send(int bytes, const uint8_t data[]) { @@ -102,7 +102,7 @@ void *spi_gpio_get_channel(void *clock_chip, int clock_pin, void *data_chip, int data_pin, void *enable_chip, int enable_pin, - uint32_t frequency) + uint64_t frequency) { return (void *) new Spi_gpio(reinterpret_cast(clock_chip), clock_pin, reinterpret_cast(data_chip), data_pin, diff -r edb87791dfa7 -r 89d597f10dc6 pkg/devices/lib/spi/src/jz4780.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/spi/src/jz4780.cc Tue Nov 07 19:20:32 2023 +0100 @@ -0,0 +1,387 @@ +/* + * Perform SPI communication using the JZ4780 SPI peripheral. + * + * Copyright (C) 2023 Paul Boddie + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include + + + +/* Register definitions. */ + +enum Regs +{ + Ssi_data = 0x00, // SSIDR + Ssi_control0 = 0x04, // SSICR0 + Ssi_control1 = 0x08, // SSICR1 + Ssi_status = 0x0c, // SSISR + Ssi_interval_time = 0x10, // SSIITR + Ssi_char_per_frame = 0x14, // SSIICR + Ssi_clock = 0x18, // SSICGR + Ssi_recv_counter = 0x1c, // SSIRCNT + + /* Register block offset. */ + + Ssi_block_offset = 0x1000, +}; + +enum Ssi_data_bits : unsigned +{ + Ssi_data_gpc = 0x1000, +}; + +enum Ssi_control0_bits : unsigned +{ + Ssi_trans_endian_mask = 0xc0000, + Ssi_trans_endian_msbyte_msbit = 0x00000, + Ssi_trans_endian_msbyte_lsbit = 0x40000, + Ssi_trans_endian_lsbyte_lsbit = 0x80000, + Ssi_trans_endian_lsbyte_msbit = 0xc0000, + + Ssi_recv_endian_mask = 0x30000, + Ssi_recv_endian_msbyte_msbit = 0x00000, + Ssi_recv_endian_msbyte_lsbit = 0x10000, + Ssi_recv_endian_lsbyte_lsbit = 0x20000, + Ssi_recv_endian_lsbyte_msbit = 0x30000, + + Ssi_enable = 0x08000, + Ssi_enable_trans_half_empty = 0x04000, + Ssi_enable_recv_half_full = 0x02000, + Ssi_enable_trans_error = 0x01000, + Ssi_enable_recv_error = 0x00800, + Ssi_loop = 0x00400, + Ssi_recv_finish_control = 0x00200, + Ssi_recv_finished = 0x00100, + Ssi_enable_auto_clear_underrun = 0x00080, + Ssi_select_pin_is_ce2 = 0x00040, + Ssi_use_recv_count = 0x00010, + Ssi_old_fifo_empty_mode = 0x00008, + Ssi_trans_flush = 0x00004, + Ssi_recv_flush = 0x00002, + Ssi_disable_recv = 0x00001, +}; + +enum Ssi_control1_bits : unsigned +{ + Ssi_active_mask = 0xc0000000, + Ssi_active_ce_low = 0x00000000, + Ssi_active_ce_high = 0x40000000, + Ssi_active_ce2_low = 0x00000000, + Ssi_active_ce2_high = 0x80000000, + + Ssi_clock_start_delay_mask = 0x30000000, + Ssi_clock_start_delay_default = 0x00000000, + Ssi_clock_start_delay_plus_1 = 0x10000000, + Ssi_clock_start_delay_plus_2 = 0x20000000, + Ssi_clock_start_delay_plus_3 = 0x30000000, + + Ssi_clock_stop_delay_mask = 0x0c000000, + Ssi_clock_stop_delay_default = 0x00000000, + Ssi_clock_stop_delay_plus_1 = 0x04000000, + Ssi_clock_stop_delay_plus_2 = 0x08000000, + Ssi_clock_stop_delay_plus_3 = 0x0c000000, + + Ssi_interval_assert_ce_or_ce2 = 0x01000000, + Ssi_trans_empty_unfinished = 0x00800000, + + Ssi_format_mask = 0x00300000, + Ssi_format_spi = 0x00000000, + Ssi_format_ssp = 0x00100000, + Ssi_format_microwire1 = 0x00200000, + Ssi_format_microwire2 = 0x00300000, + + Ssi_trans_threshold_mask = 0x000f0000, + Ssi_command_length_mask = 0x0000f000, + Ssi_recv_threshold_mask = 0x00000f00, + Ssi_char_length_mask = 0x000000f8, + + Spi_clock_assert_sample = 0x00000000, // phase #0 + Spi_clock_assert_drive = 0x00000002, // phase #1 + Spi_clock_idle_low_level = 0x00000000, // polarity #0 + Spi_clock_idle_high_level = 0x00000001, // polarity #1 +}; + +enum Ssi_control1_shifts : unsigned +{ + Ssi_trans_threshold_shift = 16, + Ssi_command_length_shift = 12, + Ssi_recv_threshold_shift = 8, + Ssi_char_length_shift = 3, +}; + +enum Ssi_control1_limits : unsigned +{ + Ssi_trans_threshold_limit = 15, + Ssi_command_length_limit = 15, + Ssi_recv_threshold_limit = 15, + Ssi_char_length_limit = 30, +}; + +enum Ssi_status_bits : unsigned +{ + Ssi_trans_char_count_mask = 0x00ff0000, + Ssi_recv_char_count_mask = 0x0000ff00, + Ssi_trans_ended = 0x00000080, + Ssi_trans_busy = 0x00000040, + Ssi_trans_fifo_full = 0x00000020, + Ssi_recv_fifo_empty = 0x00000010, + Ssi_trans_fifo_half_empty = 0x00000008, + Ssi_recv_fifo_half_full = 0x00000004, + Ssi_trans_underrun = 0x00000002, + Ssi_recv_overrun = 0x00000001, +}; + +enum Ssi_status_shifts : unsigned +{ + Ssi_trans_char_count_shift = 16, + Ssi_recv_char_count_shift = 8, +}; + +enum Ssi_status_limits : unsigned +{ + Ssi_trans_char_count_limit = 0xff, + Ssi_recv_char_count_limit = 0xff, +}; + +enum Ssi_interval_time_bits : unsigned +{ + Ssi_interval_clock_mask = 0x8000, + Ssi_interval_clock_bit_clock = 0x0000, + Ssi_interval_clock_32k_clock = 0x8000, + Ssi_interval_time_mask = 0x3fff, +}; + +enum Ssi_char_per_frame_bits : unsigned +{ + Ssi_char_per_frame_mask = 0x7, +}; + +enum Ssi_clock_bits : unsigned +{ + Ssi_clock_frequency_mask = 0xff, +}; + +enum Ssi_recv_counter_bits : unsigned +{ + Ssi_recv_counter_mask = 0xffff, +}; + + + +/* Initialise a channel. */ + +Spi_jz4780_channel::Spi_jz4780_channel(l4_addr_t spi_start, l4_addr_t start, + enum Clock_identifiers clock, + Cpm_jz4780_chip *cpm, + Dma_jz4780_channel *dma, + enum Dma_jz4780_request_type request_type, + uint64_t frequency) +: _spi_start(spi_start), _clock(clock), _cpm(cpm), _dma(dma), + _request_type(request_type), _frequency(frequency) +{ + _regs = new Hw::Mmio_register_block<32>(start); + _cpm->start_clock(clock); + + /* Disable the channel while configuring: send MSB first, big endian wire + representation. Disable reception. */ + + _regs[Ssi_control0] = Ssi_trans_endian_msbyte_msbit | + Ssi_select_pin_is_ce2 | + Ssi_disable_recv; + + /* Set default transfer properties. */ + + configure_transfer(8); + + /* Select "normal" mode. */ + + _regs[Ssi_interval_time] = 0; + + /* Limit the frequency to half that of the device clock. */ + + if (_frequency >= _cpm->get_frequency(_clock)) + _frequency = _cpm->get_frequency(_clock) / 2; + + /* SSI_CLK = DEV_CLK / (2 * (divider + 1)) */ + + uint32_t divider = _cpm->get_frequency(_clock) / (_frequency * 2) - 1; + + _regs[Ssi_clock] = divider < Ssi_clock_frequency_mask ? divider : Ssi_clock_frequency_mask; + + /* Enable the channel. */ + + _regs[Ssi_control0] = _regs[Ssi_control0] | Ssi_enable; +} + +/* NOTE: More transfer characteristics should be configurable. */ + +void Spi_jz4780_channel::configure_transfer(uint8_t char_size) +{ + uint32_t char_length; + + if (char_size < 2) + char_length = 0; + else + { + char_length = char_size - 2; + + if (char_size > Ssi_char_length_limit) + char_length = Ssi_char_length_limit; + } + + /* Clear the status. */ + + _regs[Ssi_control0] = _regs[Ssi_control0] | Ssi_trans_flush | Ssi_recv_flush; + _regs[Ssi_status] = 0; + + /* Indicate the desired character size. + + Use active low device selection, SPI format with active low clock, with + data driven on the falling (asserted) clock and sampled on the rising + clock + + The unfinished flag prevents the transaction from finishing if the FIFO is + empty. It is not used here since it appears to prevent any meaningful + testing of the busy and end flags. */ + + _regs[Ssi_control1] = (char_length << Ssi_char_length_shift) | + ((Ssi_trans_threshold_limit / 2) << Ssi_trans_threshold_shift) | + Ssi_format_spi | Ssi_active_ce2_low | + Spi_clock_assert_drive | Spi_clock_idle_high_level; +} + +/* Transfer the given number of bytes from a buffer using the given unit size in + bytes and character size in bits. */ + +uint32_t +Spi_jz4780_channel::send(uint32_t bytes, const uint8_t data[], uint8_t unit_size, + uint8_t char_size) +{ + configure_transfer(char_size); + + uint32_t transferred; + uint32_t char_mask = (1 << char_size) - 1; + + for (transferred = 0; transferred < bytes; transferred += unit_size) + { + uint32_t value = 0; + + for (uint8_t byte = 0; byte < unit_size; byte++) + value = (value << 8) | data[transferred + byte]; + + /* Relocate any command bit to bit 16 for byte characters. */ + + uint32_t command = (char_size < 16) && (value & (1 << char_size)) ? (1 << 16) : 0; + + /* Combine the significant portion of the character with the command. */ + + value = (value & char_mask) | command; + _regs[Ssi_data] = value; + } + + /* Wait for the busy condition to clear or for a limited period. */ + + for (unsigned int i = 0; i < (1 << 20) && (_regs[Ssi_status] & Ssi_trans_busy); i++); + + return transferred; +} + +/* Transfer the given number of bytes from a DMA region using the given + unit size in bytes and character size in bits. */ + +uint32_t Spi_jz4780_channel::transfer(l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint8_t unit_size, + uint8_t char_size) +{ + configure_transfer(char_size); + + uint32_t transferred = 0; + uint32_t unit_count = count / unit_size; + uint32_t to_transfer = _dma->transfer(paddr, _spi_start + Ssi_data, + unit_count, true, false, + unit_size, unit_size, unit_size, + _request_type); + + if (to_transfer) + transferred = to_transfer ? (unit_count - _dma->wait()) * unit_size : 0; + + /* Wait for the busy condition to clear or for a limited period. */ + + for (unsigned int i = 0; i < (1 << 20) && (_regs[Ssi_status] & Ssi_trans_busy); i++); + + return transferred; +} + + + +/* Initialise the peripheral abstraction. */ + +Spi_jz4780_chip::Spi_jz4780_chip(l4_addr_t spi_start, l4_addr_t start, + l4_addr_t end, Cpm_jz4780_chip *cpm) +: _spi_start(spi_start), _start(start), _end(end), _cpm(cpm) +{ +} + +Spi_jz4780_channel * +Spi_jz4780_chip::get_channel(uint8_t channel, Dma_jz4780_channel *dma, + uint64_t frequency) +{ + // NOTE: Only sending is supported. + + enum Dma_jz4780_request_type request_types[] = {Dma_request_ssi0_out, Dma_request_ssi1_out}; + enum Clock_identifiers clocks[] = {Clock_ssi0, Clock_ssi1}; + + if (channel < 2) + return new Spi_jz4780_channel(_spi_start + channel * Ssi_block_offset, + _start + channel * Ssi_block_offset, + clocks[channel], + _cpm, dma, request_types[channel], frequency); + else + throw -L4_EINVAL; +} + + + +/* C language interface. */ + +void *jz4780_spi_init(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end, void *cpm) +{ + return new Spi_jz4780_chip(spi_start, start, end, static_cast(cpm)); +} + +void *jz4780_spi_get_channel(void *spi, uint8_t channel, void *dma, uint64_t frequency) +{ + return static_cast(spi)->get_channel(channel, + static_cast(dma), frequency); +} + +uint32_t jz4780_spi_send(void *channel, uint32_t bytes, const uint8_t data[], + uint8_t unit_size, uint8_t char_size) +{ + return static_cast(channel)->send(bytes, data, unit_size, char_size); +} + +uint32_t jz4780_spi_transfer(void *channel, l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint8_t unit_size, uint8_t char_size) +{ + return static_cast(channel)->transfer(paddr, count, unit_size, char_size); +} diff -r edb87791dfa7 -r 89d597f10dc6 pkg/landfall-examples/hw_info/common.h --- a/pkg/landfall-examples/hw_info/common.h Fri Nov 03 19:58:11 2023 +0100 +++ b/pkg/landfall-examples/hw_info/common.h Tue Nov 07 19:20:32 2023 +0100 @@ -35,9 +35,8 @@ void *aic_get_channel(void *aic, int num, void *channel); -long aic_get_buffer(void *channel, uint32_t count, l4_addr_t *addr); - -unsigned int aic_transfer(void *channel, uint32_t count, uint32_t sample_rate, +unsigned int aic_transfer(void *channel, l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint32_t sample_rate, uint8_t sample_size); @@ -168,12 +167,22 @@ /* SPI adapter functions. */ -void *spi_get_channel(void *clock_chip, int clock_pin, - void *data_chip, int data_pin, - void *enable_chip, int enable_pin, - uint32_t frequency); +void *spi_init(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end, void *cpm); + +void *spi_get_channel(void *spi, uint8_t num, void *channel, uint64_t frequency); -void spi_send(void *channel, int bytes, const uint8_t data[]); +void *spi_get_channel_gpio(void *clock_chip, int clock_pin, + void *data_chip, int data_pin, + void *enable_chip, int enable_pin, + uint64_t frequency); + +void spi_send(void *channel, int bytes, const uint8_t data[], + uint8_t unit_size, uint8_t char_size); + +void spi_send_gpio(void *channel, int bytes, const uint8_t data[]); + +uint32_t spi_transfer(void *channel, l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint8_t unit_size, uint8_t char_size); @@ -181,7 +190,7 @@ enum memory_regions { - AIC, CPM, DMA, GPIO, I2C + AIC, CPM, DMA, GPIO, I2C, SSI }; diff -r edb87791dfa7 -r 89d597f10dc6 pkg/landfall-examples/hw_info/hw_info.c --- a/pkg/landfall-examples/hw_info/hw_info.c Fri Nov 03 19:58:11 2023 +0100 +++ b/pkg/landfall-examples/hw_info/hw_info.c Tue Nov 07 19:20:32 2023 +0100 @@ -218,17 +218,22 @@ } } +static struct dma_region *_get_dma_region(void) +{ + int num = get_region_number(num_dma_regions); + + if (num < 0) + return NULL; + + return &dma_regions[num]; +} -/* AIC/I2S configuration. */ - -static l4_uint32_t aic_irq_start = 0, aic_irq_end = 0; /* AIC/I2S operations. */ static void new_aic_channel(void *aic) { - l4_cap_idx_t irqcap; int num = get_channel_number(num_aic_channels); void *channel; @@ -240,64 +245,23 @@ if (channel == NULL) return; - irqcap = l4re_util_cap_alloc(); - - if (init_irq(num, irqcap, aic_irq_start, aic_irq_end)) - return; - aic_channels[num] = aic_get_channel(aic, num, channel); - aic_irqs[num] = irqcap; -} - -static void set_aic_buffer(void) -{ - char *token; - FILE *fp; - void *channel = get_channel(num_aic_channels, aic_channels, NULL); - unsigned int size; - l4_addr_t vaddr; - - if (channel == NULL) - return; - - if (!read_number("Buffer size", &size)) - return; - - if (aic_get_buffer(channel, size, &vaddr)) - { - printf("Could not get buffer.\n"); - return; - } - - memset((void *) vaddr, 0, size); - - if ((token = read_token("Filename")) == NULL) - return; - - /* Populate the region from the file. */ - - fp = fopen(token, "r"); - - if (fp == NULL) - { - printf("File not readable.\n"); - return; - } - - fread((char *) vaddr, sizeof(char), size, fp); - fclose(fp); - - l4_cache_flush_data(vaddr, vaddr + size); } static void aic_transfer_data(void) { void *channel = get_channel(num_aic_channels, aic_channels, NULL); - unsigned int count, rate, resolution, transferred; + struct dma_region *region; + uint32_t count, rate, resolution, transferred; if (channel == NULL) return; + region = _get_dma_region(); + + if (region == NULL) + return; + if (!read_number("Sample size", &count)) return; @@ -307,7 +271,7 @@ if (!read_number("Sample resolution", &resolution)) return; - transferred = aic_transfer(channel, count, rate, resolution); + transferred = aic_transfer(channel, region->paddr, count, rate, resolution); printf("Transferred: %d\n", transferred); } @@ -507,16 +471,6 @@ return init_irq(0, dma_irq, dma_irq_start, dma_irq_end); } -static struct dma_region *_get_dma_region(void) -{ - int num = get_region_number(num_dma_regions); - - if (num < 0) - return NULL; - - return &dma_regions[num]; -} - static void list_dma_regions(void) { unsigned int num; @@ -627,7 +581,7 @@ static void dma_transfer_data(void) { - unsigned int count, to_transfer, transferred; + uint32_t count, to_transfer, transferred; unsigned int source_address, destination_address, source_width, destination_width, transfer_unit_size, request_type; int source_increment, destination_increment; @@ -670,11 +624,11 @@ l4_cache_inv_data(dma_regions[i].vaddr, dma_regions[i].vaddr + dma_regions[i].size); to_transfer = dma_transfer(channel, source_address, destination_address, - count, - source_increment, destination_increment, - source_width, destination_width, - transfer_unit_size, - request_type); + count, + source_increment, destination_increment, + source_width, destination_width, + transfer_unit_size, + request_type); transferred = to_transfer ? count - dma_wait(channel) : 0; @@ -1043,7 +997,27 @@ /* SPI operations. */ -static void new_spi_channel(void *gpio[]) +static void new_spi_channel(void *spi) +{ + int num = get_channel_number(num_spi_channels); + void *channel; + uint32_t frequency; + + if (num < 0) + return; + + channel = get_channel(num_dma_channels, dma_channels, NULL); + + if (channel == NULL) + return; + + if (!read_number("Frequency", &frequency)) + return; + + spi_channels[num] = spi_get_channel(spi, num, channel, frequency); +} + +static void new_spi_channel_gpio(void *gpio[]) { unsigned int clock_port, clock_pin, data_port, data_pin, enable_port, enable_pin; int num = get_channel_number(num_spi_channels); @@ -1064,13 +1038,13 @@ if (!read_number("Frequency", &frequency)) return; - spi_channels[num] = spi_get_channel(gpio[clock_port], clock_pin, - gpio[data_port], data_pin, - gpio[enable_port], enable_pin, - frequency); + spi_channels[num] = spi_get_channel_gpio(gpio[clock_port], clock_pin, + gpio[data_port], data_pin, + gpio[enable_port], enable_pin, + frequency); } -static void spi_send_data(void) +static void spi_send_data_gpio(void) { void *channel = get_channel(num_spi_channels, spi_channels, NULL); int bytes; @@ -1085,7 +1059,59 @@ bytes++; } - spi_send(channel, bytes, buffer); + spi_send_gpio(channel, bytes, buffer); +} + +static void spi_send_data_units(void) +{ + void *channel = get_channel(num_spi_channels, spi_channels, NULL); + unsigned int char_size, unit_size, value; + uint8_t buffer[256]; + int byte = 0; + + if (!read_number("Unit size", &unit_size)) + return; + + if (!read_number("Character size", &char_size)) + return; + + /* Read hex digits for bytes. Multiple bytes make up each unit and are read + from most to least significant. Where the unit size exceeds the character + size, the last bit before the character indicates the GPC bit. */ + + while ((byte < 256) && read_encoded_number(NULL, "%2x", &value)) + buffer[byte++] = value; + + spi_send(channel, byte, (uint8_t *) buffer, unit_size, char_size); +} + +static void spi_transfer_data(void) +{ + void *channel = get_channel(num_spi_channels, spi_channels, NULL); + struct dma_region *region; + unsigned int char_size, unit_size; + uint32_t count, transferred; + + if (channel == NULL) + return; + + region = _get_dma_region(); + + if (region == NULL) + return; + + if (!read_number("Transfer size", &count)) + return; + + if (!read_number("Unit size", &unit_size)) + return; + + if (!read_number("Character size", &char_size)) + return; + + transferred = spi_transfer(channel, region->paddr, count, unit_size, char_size); + + printf("Transferred: %d\n", transferred); } @@ -1102,8 +1128,6 @@ list_channels(num_aic_channels, aic_channels); else if (!strcmp(token, "c") || !strcmp(token, "channel")) new_aic_channel(aic); - else if (!strcmp(token, "s") || !strcmp(token, "set")) - set_aic_buffer(); else if (!strcmp(token, "t") || !strcmp(token, "transfer")) aic_transfer_data(); else @@ -1203,7 +1227,7 @@ list_i2c_channels(); } -static void handle_spi(void *gpio[]) +static void handle_spi(void *spi, void *gpio[]) { char *token; @@ -1212,11 +1236,17 @@ if (!strcmp(token, "l") || !strcmp(token, "list")) list_channels(num_spi_channels, spi_channels); else if (!strcmp(token, "c") || !strcmp(token, "channel")) - new_spi_channel(gpio); + new_spi_channel(spi); + else if (!strcmp(token, "g") || !strcmp(token, "gpio") || !strcmp(token, "gpio-channel")) + new_spi_channel_gpio(gpio); else if (!strcmp(token, "s") || !strcmp(token, "send")) - spi_send_data(); + spi_send_data_gpio(); + else if (!strcmp(token, "S") || !strcmp(token, "send-units")) + spi_send_data_units(); + else if (!strcmp(token, "t") || !strcmp(token, "transfer")) + spi_transfer_data(); else - printf("spi channel | list | send\n"); + printf("spi channel | gpio-channel | list | send | send-units | transfer\n"); } else list_channels(num_spi_channels, spi_channels); @@ -1232,7 +1262,9 @@ l4_addr_t dma_base = 0, dma_base_end = 0; l4_addr_t gpio_base = 0, gpio_base_end = 0; l4_addr_t i2c_base = 0, i2c_base_end = 0; - void *aic, *cpm, *dma, *gpio[num_gpio_ports], *i2c; + l4_addr_t ssi_base = 0, ssi_base_end = 0; + l4_addr_t ssi_phys_base = 0, ssi_phys_base_end = 0; + void *aic, *cpm, *dma, *gpio[num_gpio_ports], *i2c, *spi; int result = 0; unsigned int port; @@ -1303,10 +1335,15 @@ aic = aic_init(aic_phys_base, aic_base, aic_base_end, cpm); - if (get_irq(memory_regions[AIC], &aic_irq_start, &aic_irq_end) < 0) + printf("Access SSI...\n"); + + if ((result = get_memory_complete(memory_regions[SSI], &ssi_base, &ssi_base_end, + &ssi_phys_base, &ssi_phys_base_end)) < 0) return 1; - printf("IRQ range at %d...%d.\n", aic_irq_start, aic_irq_end); + printf("SSI at 0x%lx...0x%lx.\n", ssi_base, ssi_base_end); + + spi = spi_init(ssi_phys_base, ssi_base, ssi_base_end, cpm); /* Start the interactive session. */ @@ -1318,7 +1355,7 @@ printf("> "); - token = fgets(cmdline, 64, stdin); + token = fgets(cmdline, 256, stdin); if (token == NULL) break; @@ -1354,9 +1391,11 @@ /* SPI commands. */ else if (!strcmp(token, "s") || !strcmp(token, "spi")) - handle_spi(gpio); + handle_spi(spi, gpio); - else + /* Comments and blank lines. */ + + else if (strncmp(token, "#", 1) && strlen(token)) printf("Command?\n"); } diff -r edb87791dfa7 -r 89d597f10dc6 pkg/landfall-examples/hw_info/jz4780.c --- a/pkg/landfall-examples/hw_info/jz4780.c Fri Nov 03 19:58:11 2023 +0100 +++ b/pkg/landfall-examples/hw_info/jz4780.c Tue Nov 07 19:20:32 2023 +0100 @@ -19,12 +19,21 @@ * Boston, MA 02110-1301, USA */ -#include /* NOTE: To be replaced. */ +/* NOTE: AIC support should be replaced. The CI20 should be able to send I2S + audio over HDMI or via its internal codec to the headphone socket. */ + +#include + #include #include #include #include + +/* GPIO-based SPI can use arbitrary pins, whereas only the secondary header + provides pins like GPC. */ + #include +#include #include "common.h" @@ -41,14 +50,11 @@ return x1600_aic_get_channel(aic, num, channel); } -long aic_get_buffer(void *channel, uint32_t count, l4_addr_t *addr) +unsigned int aic_transfer(void *channel, l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint32_t sample_rate, + uint8_t sample_size) { - return x1600_aic_get_buffer(channel, count, addr); -} - -unsigned int aic_transfer(void *channel, uint32_t count, uint32_t sample_rate, uint8_t sample_size) -{ - return x1600_aic_transfer(channel, count, sample_rate, sample_size); + return x1600_aic_transfer(channel, paddr, count, sample_rate, sample_size); } @@ -326,19 +332,42 @@ /* SPI adapter functions. */ -void *spi_get_channel(void *clock_chip, int clock_pin, - void *data_chip, int data_pin, - void *enable_chip, int enable_pin, - uint32_t frequency) +void *spi_init(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end, void *cpm) { - return spi_gpio_get_channel(clock_chip, clock_pin, data_chip, data_pin, enable_chip, enable_pin, frequency); + return jz4780_spi_init(spi_start, start, end, cpm); +} + +void *spi_get_channel(void *spi, uint8_t num, void *channel, uint64_t frequency) +{ + return jz4780_spi_get_channel(spi, num, channel, frequency); } -void spi_send(void *channel, int bytes, const uint8_t data[]) +void *spi_get_channel_gpio(void *clock_chip, int clock_pin, + void *data_chip, int data_pin, + void *enable_chip, int enable_pin, + uint64_t frequency) +{ + return spi_gpio_get_channel(clock_chip, clock_pin, data_chip, data_pin, + enable_chip, enable_pin, frequency); +} + +void spi_send(void *channel, int bytes, const uint8_t data[], uint8_t unit_size, + uint8_t char_size) +{ + jz4780_spi_send(channel, bytes, data, unit_size, char_size); +} + +void spi_send_gpio(void *channel, int bytes, const uint8_t data[]) { spi_gpio_send(channel, bytes, data); } +uint32_t spi_transfer(void *channel, l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint8_t unit_size, uint8_t char_size) +{ + return jz4780_spi_transfer(channel, paddr, count, unit_size, char_size); +} + /* Memory regions. */ @@ -349,6 +378,7 @@ [DMA] = "jz4780-dma", [GPIO] = "jz4780-gpio", [I2C] = "jz4780-i2c", + [SSI] = "jz4780-ssi", }; @@ -399,7 +429,6 @@ {"ssi", Clock_ssi, "SSI"}, {"ssi0", Clock_ssi0, "SSI0"}, {"ssi1", Clock_ssi1, "SSI1"}, - {"ssi2", Clock_ssi2, "SSI2"}, {"uart0", Clock_uart0, "UART0"}, {"uart1", Clock_uart1, "UART1"}, {"uart2", Clock_uart2, "UART2"}, @@ -416,9 +445,9 @@ const unsigned int num_dma_channels = 32; -struct dma_region dma_regions[2]; +struct dma_region dma_regions[8]; -const unsigned int num_dma_regions = 2; +const unsigned int num_dma_regions = 8; l4_cap_idx_t dma_irq = L4_INVALID_CAP; diff -r edb87791dfa7 -r 89d597f10dc6 pkg/landfall-examples/hw_info/x1600.c --- a/pkg/landfall-examples/hw_info/x1600.c Fri Nov 03 19:58:11 2023 +0100 +++ b/pkg/landfall-examples/hw_info/x1600.c Tue Nov 07 19:20:32 2023 +0100 @@ -41,14 +41,11 @@ return x1600_aic_get_channel(aic, num, channel); } -long aic_get_buffer(void *channel, uint32_t count, l4_addr_t *addr) +unsigned int aic_transfer(void *channel, l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint32_t sample_rate, + uint8_t sample_size) { - return x1600_aic_get_buffer(channel, count, addr); -} - -unsigned int aic_transfer(void *channel, uint32_t count, uint32_t sample_rate, uint8_t sample_size) -{ - return x1600_aic_transfer(channel, count, sample_rate, sample_size); + return x1600_aic_transfer(channel, paddr, count, sample_rate, sample_size); } @@ -326,19 +323,50 @@ /* SPI adapter functions. */ -void *spi_get_channel(void *clock_chip, int clock_pin, - void *data_chip, int data_pin, - void *enable_chip, int enable_pin, - uint32_t frequency) +void *spi_init(l4_addr_t spi_start, l4_addr_t start, l4_addr_t end, void *cpm) +{ + (void) spi_start; (void) start; (void) end; (void) cpm; + return NULL; +} + +void *spi_get_channel(void *spi, uint8_t num, void *channel, uint64_t frequency) +{ + (void) spi; (void) num; (void) channel; (void) frequency; + return NULL; +} + +void *spi_get_channel_gpio(void *clock_chip, int clock_pin, + void *data_chip, int data_pin, + void *enable_chip, int enable_pin, + uint64_t frequency) { return spi_gpio_get_channel(clock_chip, clock_pin, data_chip, data_pin, enable_chip, enable_pin, frequency); } -void spi_send(void *channel, int bytes, const uint8_t data[]) +void spi_send(void *channel, int bytes, const uint8_t data[], uint8_t unit_size, + uint8_t char_size) +{ + /* NOTE: Not yet supported. */ + + (void) channel; (void) bytes; (void) data; (void) unit_size; (void) char_size; + // x1600_spi_send(channel, bytes, data, unit_size, char_size); +} + +void spi_send_gpio(void *channel, int bytes, const uint8_t data[]) { spi_gpio_send(channel, bytes, data); } +uint32_t spi_transfer(void *channel, l4re_dma_space_dma_addr_t paddr, + uint32_t count, uint8_t unit_size, uint8_t char_size) +{ + /* NOTE: Not yet supported. */ + + (void) channel; (void) paddr; (void) count; (void) unit_size; (void) char_size; + // return x1600_spi_transfer(channel, paddr, count, unit_size, char_size); + return 0; +} + /* Memory regions. */ @@ -349,6 +377,7 @@ [DMA] = "x1600-dma", [GPIO] = "x1600-gpio", [I2C] = "x1600-i2c", + [SSI] = "x1600-ssi", }; @@ -377,7 +406,7 @@ {"apb", Clock_pclock, "APB"}, {"aic", Clock_aic, "AIC"}, {"dma", Clock_dma, "DMA"}, - {"lcd", Clock_lcd_pixel0, "LCD pixel"}, + {"lcd0", Clock_lcd_pixel0, "LCD pixel"}, {"msc0", Clock_msc0, "MSC0"}, {"msc1", Clock_msc1, "MSC1"}, {"otg", Clock_otg0, "USB OTG"}, @@ -387,7 +416,7 @@ {"i2s1", Clock_i2s1, "I2S1"}, {"i2s0r", Clock_i2s0_rx, "I2S0 RX"}, {"i2s0t", Clock_i2s0_tx, "I2S0 TX"}, - {"ssi", Clock_ssi0, "SSI"}, + {"ssi0", Clock_ssi0, "SSI"}, {"uart0", Clock_uart0, "UART0"}, {"uart1", Clock_uart1, "UART1"}, {"uart2", Clock_uart2, "UART2"}, @@ -403,9 +432,9 @@ const unsigned int num_dma_channels = 32; -struct dma_region dma_regions[2]; +struct dma_region dma_regions[8]; -const unsigned int num_dma_regions = 2; +const unsigned int num_dma_regions = 8; l4_cap_idx_t dma_irq = L4_INVALID_CAP;