├── .gitignore ├── CMakeLists.txt ├── LICENSE.TXT ├── README.md ├── external └── pico_extras_import.cmake ├── pico_sdk_import.cmake ├── post_init.cmake ├── src ├── CMakeLists.txt ├── common │ ├── CMakeLists.txt │ ├── pico_audio │ │ ├── CMakeLists.txt │ │ ├── audio.cpp │ │ ├── audio_utils.S │ │ └── include │ │ │ └── pico │ │ │ ├── audio.h │ │ │ └── sample_conversion.h │ ├── pico_scanvideo │ │ ├── CMakeLists.txt │ │ ├── README.adoc │ │ ├── include │ │ │ └── pico │ │ │ │ └── scanvideo │ │ │ │ ├── composable_scanline.h │ │ │ │ └── scanvideo_base.h │ │ ├── scanvideo.pio │ │ └── vga_modes.c │ ├── pico_sd_card │ │ ├── CMakeLists.txt │ │ └── include │ │ │ └── pico │ │ │ └── sd_card.h │ ├── pico_util_buffer │ │ ├── CMakeLists.txt │ │ ├── buffer.c │ │ └── include │ │ │ └── pico │ │ │ └── util │ │ │ └── buffer.h │ └── platypus │ │ ├── CMakeLists.txt │ │ ├── decompress_row.S │ │ ├── decompress_row_33.S │ │ ├── platypus.c │ │ └── platypus.h └── rp2_common │ ├── CMakeLists.txt │ ├── hardware_rosc │ ├── CMakeLists.txt │ ├── include │ │ └── hardware │ │ │ └── rosc.h │ └── rosc.c │ ├── pico_audio_i2s │ ├── CMakeLists.txt │ ├── audio_i2s.c │ ├── audio_i2s.pio │ └── include │ │ └── pico │ │ └── audio_i2s.h │ ├── pico_audio_pwm │ ├── CMakeLists.txt │ ├── audio_pwm.c │ ├── audio_pwm.pio │ ├── include │ │ └── pico │ │ │ ├── audio_pwm.h │ │ │ └── audio_pwm │ │ │ └── sample_encoding.h │ └── sample_encoding.cpp │ ├── pico_audio_spdif │ ├── CMakeLists.txt │ ├── audio_spdif.c │ ├── audio_spdif.pio │ ├── include │ │ └── pico │ │ │ ├── audio_spdif.h │ │ │ └── audio_spdif │ │ │ └── sample_encoding.h │ └── sample_encoding.cpp │ ├── pico_scanvideo_dbi │ ├── CMakeLists.txt │ ├── README.md │ ├── control.pio │ ├── tft_driver.c │ ├── tft_driver.h │ ├── vga_modes.c │ ├── video.h │ └── video_dbi.c │ ├── pico_scanvideo_dpi │ ├── CMakeLists.txt │ ├── include │ │ └── pico │ │ │ └── scanvideo.h │ ├── scanvideo.c │ └── timing.pio │ ├── pico_sd_card │ ├── CMakeLists.txt │ ├── README.md │ ├── crc-itu-t.h │ ├── crc7.h │ ├── sd_card.c │ └── sd_card.pio │ ├── pico_sleep │ ├── CMakeLists.txt │ ├── include │ │ └── pico │ │ │ └── sleep.h │ └── sleep.c │ ├── usb_common │ ├── CMakeLists.txt │ └── include │ │ └── usb │ │ └── usb_common.h │ ├── usb_device │ ├── CMakeLists.txt │ ├── include │ │ └── pico │ │ │ ├── usb_device.h │ │ │ ├── usb_device_private.h │ │ │ └── usb_stream_helper.h │ ├── usb_device.c │ └── usb_stream_helper.c │ └── usb_device_msc │ ├── CMakeLists.txt │ ├── include │ └── pico │ │ ├── scsi.h │ │ ├── scsi_ir.h │ │ ├── usb_device_msc.h │ │ └── virtual_disk.h │ └── usb_device_msc.c └── test ├── CMakeLists.txt ├── sample_conversion_test ├── CMakeLists.txt └── sample_conversion_test.cpp └── sd_test ├── CMakeLists.txt └── sd_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | cmake-* 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | if (NOT TARGET _pico_extras_inclusion_marker) 4 | add_library(_pico_extras_inclusion_marker INTERFACE) 5 | 6 | # Pull in PICO SDK (must be before project) 7 | include(pico_sdk_import.cmake) 8 | if (PICO_SDK_VERSION_STRING VERSION_LESS "2.0.0") 9 | message(FATAL_ERROR "Require at least Raspberry Pi Pico SDK version 2.0.0") 10 | endif() 11 | 12 | project(pico_extras C CXX) 13 | 14 | set(CMAKE_C_STANDARD 11) 15 | set(CMAKE_CXX_STANDARD 17) 16 | 17 | pico_is_top_level_project(PICO_EXTRAS_TOP_LEVEL_PROJECT) 18 | 19 | if (NOT PICO_EXTRAS_PATH) 20 | set(PICO_EXTRAS_PATH ${CMAKE_CURRENT_LIST_DIR}) 21 | endif() 22 | set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to Pico Extras") 23 | 24 | # The real work gets done in post_init which is called at the end of pico_sdk_init 25 | list(APPEND PICO_SDK_POST_LIST_FILES ${CMAKE_CURRENT_LIST_DIR}/post_init.cmake) 26 | if (PICO_EXTRAS_TOP_LEVEL_PROJECT) 27 | message("pico_extras: initialize SDK since we're the top-level") 28 | # Initialize the SDK 29 | pico_sdk_init() 30 | else() 31 | set(PICO_SDK_POST_LIST_FILES ${PICO_SDK_POST_LIST_FILES} PARENT_SCOPE) 32 | endif() 33 | endif() -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 4 | following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 7 | disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 10 | disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 21 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo has additional libraries that are not yet ready for inclusion the Pico SDK proper, 2 | or are just useful but don't necessarily belong in the Pico SDK. 3 | 4 | Note that any API here is a work in progress and subject to change. 5 | 6 | See [pico-playground](https://github.com/raspberrypi/pico-playground) for buildable example code using these extra libraries. 7 | 8 | 9 | Library|Description 10 | ---|--- 11 | [hardware_rosc](src/rp2_common/hardware_rosc)| API for the ring oscillator 12 | `lwip`| Deprecated as of SDK 1.5.0; use `pico_lwip` and `pico_lwip_arch` from the SDK instead. 13 | [pico_audio](src/common/pico_audio)|Audio output support; this is highly functional, but the API is subject to change 14 |    [pico_audio_i2s](src/rp2_common/pico_audio_i2s)|Audio output via I2S on 3 GPIOs using PIO. Arbitrary frequency 15 |    [pico_audio_pwm](src/rp2_common/pico_audio_pwm)|Audio output via (PIO) PWM. Currently a bit limited in frequency support (it was developed on FPGA to do 22050Hz at 48Mhz system clock). It does however support error diffusion dithering and noise shaping with 16x oversampling to give surprisingly good audio quality. This code will be split to provide both a fixed frequencie(s) version and a slightly slower but rather better arbitrary frequency version supporting ever higher carrier frequencies 16 |    [pico_audio_spdif](src/rp2_common/pico_audio_spdif)|Audio output in S/PDIF on a GPIO using PIO. Supports up to 192khz stereo. Consumed OK in test, haven't tried it with real hardware 17 | [pico_sd_card](src/rp2_common/pico_sd_card)|1 and 4 bit SDIO support using PIO. This is functional (currently writing is only 1 bit), but the code is very much a prototype and the API is just a placeholder - the command set needs to be separated from the SDIO and shared with SPI. A reference design for an SD card interface can be found in the [Hardware design with RP2040](https://datasheets.raspberrypi.com/rp2040/hardware-design-with-rp2040.pdf) datasheet. 18 | [pico_sleep](src/rp2_common/pico_sleep)|Low power related APIs, WIP because they are not sufficiently generic and also only handle core 0 19 | [pico_scanvideo](src/common/pico_scanvideo)|Support for video output where every pixel is _scanned out_ every frame. VGA/DPI support is highly functional and stable, but the API is subject to change. A reference design for a VGA board can be found in the [Hardware design with RP2040](https://datasheets.raspberrypi.com/rp2040/hardware-design-with-rp2040.pdf) datasheet. 20 |    [pico_scanvideo_dbi](src/rp2_common/pico_scanvideo_dbi)| currently non-compiling... placeholder for adding scanvideo over MIPI DBI support. 21 |    [pico_scanvideo_dpi](src/rp2_common/pico_scanvideo_dpi)| Highly functional and stable support for parallel RGB output and VSYNC/HSYNC/DEN/CLOCK for VGA/DPI. 22 | [pico_util_buffer](src/common/pico_util_buffer)|Rather incomplete buffer abstraction, used by pico_audio and pico_scanvideo 23 | [platypus](src/common/platypus)| Decoder for a custom image compression format suitable for dithered images (good for RGB555) and suitable for decoding on RP2040 at scanline speeds ... i.e you can easily decode a 320x240 image 60x per second to avoid storing the uncompressed image for scanout video. It gets about 50% compression (but is designed only for 4x4 fixed dithered RGB555 images, so is somewhat specific!). TODO add the encoder here :-) 24 | [usb_device](src/rp2_common/usb_device), [usb_common](src/rp2_common/usb_common)| The custom and somewhat minimal USB device stack used in the bootrom. We now use TinyUSB in the Pico SDK but kept here for posterity 25 | [usb_device_msc](src/rp2_common/usb_device_msc)| USB Mass Storage Class implementation using _usb_device_ 26 | 27 | You can add Pico Extras to your project similarly to the SDK (copying [external/pico_extras_import.cmake](external/pico_extras_import.cmake) into your project) 28 | having set the `PICO_EXTRAS_PATH` variable in your environment or via cmake variable. 29 | 30 | ```cmake 31 | cmake_minimum_required(VERSION 3.12) 32 | 33 | # Pull in PICO SDK (must be before project) 34 | include(pico_sdk_import.cmake) 35 | 36 | # We also need PICO EXTRAS 37 | include(pico_extras_import.cmake) 38 | 39 | project(pico_playground C CXX) 40 | set(CMAKE_C_STANDARD 11) 41 | set(CMAKE_CXX_STANDARD 17) 42 | ``` 43 | 44 | Alternative you can inject it into an existing project without modifying it via `PICO_CMAKE_POST_LIST_DIRS` 45 | by passing `-DPICO_SDK_POST_LIST_DIRS=/path/to/pico_extras` to cmake 46 | -------------------------------------------------------------------------------- /external/pico_extras_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_extras_import.cmake 2 | 3 | # This can be dropped into an external project to help locate pico-extras 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH)) 7 | set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH}) 8 | message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT)) 12 | set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT}) 13 | message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH)) 17 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | if (NOT PICO_EXTRAS_PATH) 22 | if (PICO_EXTRAS_FETCH_FROM_GIT) 23 | include(FetchContent) 24 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 25 | if (PICO_EXTRAS_FETCH_FROM_GIT_PATH) 26 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 27 | endif () 28 | FetchContent_Declare( 29 | pico_extras 30 | GIT_REPOSITORY https://github.com/raspberrypi/pico-extras 31 | GIT_TAG master 32 | ) 33 | if (NOT pico_extras) 34 | message("Downloading Raspberry Pi Pico Extras") 35 | FetchContent_Populate(pico_extras) 36 | set(PICO_EXTRAS_PATH ${pico_extras_SOURCE_DIR}) 37 | endif () 38 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 39 | else () 40 | if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras") 41 | set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras) 42 | message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}") 43 | else() 44 | message(FATAL_ERROR 45 | "PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif() 48 | endif () 49 | endif () 50 | 51 | set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS") 52 | set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable") 53 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS") 54 | 55 | get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 56 | if (NOT EXISTS ${PICO_EXTRAS_PATH}) 57 | message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found") 58 | endif () 59 | 60 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE) 61 | 62 | add_subdirectory(${PICO_EXTRAS_PATH} pico_extras) 63 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | # GIT_SUBMODULES_RECURSE was added in 3.17 33 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 34 | FetchContent_Declare( 35 | pico_sdk 36 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 37 | GIT_TAG master 38 | GIT_SUBMODULES_RECURSE FALSE 39 | ) 40 | else () 41 | FetchContent_Declare( 42 | pico_sdk 43 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 44 | GIT_TAG master 45 | ) 46 | endif () 47 | 48 | if (NOT pico_sdk) 49 | message("Downloading Raspberry Pi Pico SDK") 50 | FetchContent_Populate(pico_sdk) 51 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 52 | endif () 53 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 54 | else () 55 | message(FATAL_ERROR 56 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 57 | ) 58 | endif () 59 | endif () 60 | 61 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 62 | if (NOT EXISTS ${PICO_SDK_PATH}) 63 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 64 | endif () 65 | 66 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 67 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 68 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 69 | endif () 70 | 71 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 72 | 73 | include(${PICO_SDK_INIT_CMAKE_FILE}) 74 | -------------------------------------------------------------------------------- /post_init.cmake: -------------------------------------------------------------------------------- 1 | add_library(pico_extras_included INTERFACE) 2 | target_compile_definitions(pico_extras_included INTERFACE 3 | -DPICO_EXTRAS=1 4 | ) 5 | 6 | pico_add_platform_library(pico_extras_included) 7 | 8 | # note as we're a .cmake included by the SDK, we're relative to the pico-sdk build 9 | add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src ${CMAKE_BINARY_DIR}/pico_extras/src) 10 | 11 | if (PICO_EXTRAS_TESTS_ENABLED OR PICO_EXTRAS_TOP_LEVEL_PROJECT) 12 | add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/test ${CMAKE_BINARY_DIR}/pico_extras/test) 13 | endif () 14 | 15 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(common) 2 | if (PICO_ON_DEVICE) 3 | add_subdirectory(rp2_common) 4 | endif() 5 | -------------------------------------------------------------------------------- /src/common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(pico_audio) 2 | add_subdirectory(pico_scanvideo) 3 | add_subdirectory(pico_sd_card) 4 | add_subdirectory(pico_util_buffer) 5 | add_subdirectory(platypus) 6 | -------------------------------------------------------------------------------- /src/common/pico_audio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT TARGET pico_audio_headers) 2 | add_library(pico_audio_headers INTERFACE) 3 | target_include_directories(pico_audio_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 4 | target_link_libraries(pico_audio_headers INTERFACE pico_util_buffer) 5 | endif() 6 | 7 | if (NOT TARGET pico_audio) 8 | add_library(pico_audio INTERFACE) 9 | 10 | target_sources(pico_audio INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR}/audio.cpp 12 | ) 13 | if (NOT PICO_NO_HARDWARE AND NOT PICO_RISCV) 14 | target_sources(pico_audio INTERFACE 15 | ${CMAKE_CURRENT_LIST_DIR}/audio_utils.S 16 | ) 17 | endif() 18 | 19 | target_link_libraries(pico_audio INTERFACE pico_audio_headers pico_sync) 20 | endif() 21 | -------------------------------------------------------------------------------- /src/common/pico_audio/audio.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include "pico/audio.h" 9 | #include "pico/sample_conversion.h" 10 | 11 | // ====================== 12 | // == DEBUGGING ========= 13 | 14 | #define ENABLE_AUDIO_ASSERTIONS 15 | 16 | #ifdef ENABLE_AUDIO_ASSERTIONS 17 | #define audio_assert(x) assert(x) 18 | #else 19 | #define audio_assert(x) (void)0 20 | #endif 21 | 22 | inline static audio_buffer_t *list_remove_head(audio_buffer_t **phead) { 23 | audio_buffer_t *ab = *phead; 24 | 25 | if (ab) { 26 | *phead = ab->next; 27 | ab->next = NULL; 28 | } 29 | 30 | return ab; 31 | } 32 | 33 | inline static audio_buffer_t *list_remove_head_with_tail(audio_buffer_t **phead, 34 | audio_buffer_t **ptail) { 35 | audio_buffer_t *ab = *phead; 36 | 37 | if (ab) { 38 | *phead = ab->next; 39 | 40 | if (!ab->next) { 41 | audio_assert(*ptail == ab); 42 | *ptail = NULL; 43 | } else { 44 | ab->next = NULL; 45 | } 46 | } 47 | 48 | return ab; 49 | } 50 | 51 | inline static void list_prepend(audio_buffer_t **phead, audio_buffer_t *ab) { 52 | audio_assert(ab->next == NULL); 53 | audio_assert(ab != *phead); 54 | ab->next = *phead; 55 | *phead = ab; 56 | } 57 | 58 | // todo add a tail for these already sorted lists as we generally insert on the end 59 | inline static void list_append_with_tail(audio_buffer_t **phead, audio_buffer_t **ptail, 60 | audio_buffer_t *ab) { 61 | audio_assert(ab->next == NULL); 62 | audio_assert(ab != *phead); 63 | audio_assert(ab != *ptail); 64 | 65 | if (!*phead) { 66 | audio_assert(!*ptail); 67 | *ptail = ab; 68 | // insert at the beginning 69 | list_prepend(phead, ab); 70 | } else { 71 | // insert at end 72 | (*ptail)->next = ab; 73 | *ptail = ab; 74 | } 75 | } 76 | 77 | audio_buffer_t *get_free_audio_buffer(audio_buffer_pool_t *context, bool block) { 78 | audio_buffer_t *ab; 79 | 80 | do { 81 | uint32_t save = spin_lock_blocking(context->free_list_spin_lock); 82 | ab = list_remove_head(&context->free_list); 83 | spin_unlock(context->free_list_spin_lock, save); 84 | if (ab || !block) break; 85 | __wfe(); 86 | } while (true); 87 | return ab; 88 | } 89 | 90 | void queue_free_audio_buffer(audio_buffer_pool_t *context, audio_buffer_t *ab) { 91 | assert(!ab->next); 92 | uint32_t save = spin_lock_blocking(context->free_list_spin_lock); 93 | list_prepend(&context->free_list, ab); 94 | spin_unlock(context->free_list_spin_lock, save); 95 | __sev(); 96 | } 97 | 98 | audio_buffer_t *get_full_audio_buffer(audio_buffer_pool_t *context, bool block) { 99 | audio_buffer_t *ab; 100 | 101 | do { 102 | uint32_t save = spin_lock_blocking(context->prepared_list_spin_lock); 103 | ab = list_remove_head_with_tail(&context->prepared_list, &context->prepared_list_tail); 104 | spin_unlock(context->prepared_list_spin_lock, save); 105 | if (ab || !block) break; 106 | __wfe(); 107 | } while (true); 108 | return ab; 109 | } 110 | 111 | void queue_full_audio_buffer(audio_buffer_pool_t *context, audio_buffer_t *ab) { 112 | assert(!ab->next); 113 | uint32_t save = spin_lock_blocking(context->prepared_list_spin_lock); 114 | list_append_with_tail(&context->prepared_list, &context->prepared_list_tail, ab); 115 | spin_unlock(context->prepared_list_spin_lock, save); 116 | __sev(); 117 | } 118 | 119 | void producer_pool_give_buffer_default(audio_connection_t *connection, audio_buffer_t *buffer) { 120 | queue_full_audio_buffer(connection->producer_pool, buffer); 121 | } 122 | 123 | audio_buffer_t *producer_pool_take_buffer_default(audio_connection_t *connection, bool block) { 124 | return get_free_audio_buffer(connection->producer_pool, block); 125 | } 126 | 127 | void consumer_pool_give_buffer_default(audio_connection_t *connection, audio_buffer_t *buffer) { 128 | queue_free_audio_buffer(connection->consumer_pool, buffer); 129 | } 130 | 131 | audio_buffer_t *consumer_pool_take_buffer_default(audio_connection_t *connection, bool block) { 132 | return get_full_audio_buffer(connection->consumer_pool, block); 133 | } 134 | 135 | static audio_connection_t connection_default = { 136 | .producer_pool_take = producer_pool_take_buffer_default, 137 | .producer_pool_give = producer_pool_give_buffer_default, 138 | .consumer_pool_take = consumer_pool_take_buffer_default, 139 | .consumer_pool_give = consumer_pool_give_buffer_default, 140 | }; 141 | 142 | audio_buffer_t *audio_new_buffer(audio_buffer_format_t *format, int buffer_sample_count) { 143 | audio_buffer_t *buffer = (audio_buffer_t *) calloc(1, sizeof(audio_buffer_t)); 144 | audio_init_buffer(buffer, format, buffer_sample_count); 145 | return buffer; 146 | } 147 | 148 | void audio_init_buffer(audio_buffer_t *audio_buffer, audio_buffer_format_t *format, int buffer_sample_count) { 149 | audio_buffer->format = format; 150 | audio_buffer->buffer = pico_buffer_alloc(buffer_sample_count * format->sample_stride); 151 | audio_buffer->max_sample_count = buffer_sample_count; 152 | audio_buffer->sample_count = 0; 153 | } 154 | 155 | audio_buffer_pool_t * 156 | audio_new_buffer_pool(audio_buffer_format_t *format, int buffer_count, int buffer_sample_count) { 157 | audio_buffer_pool_t *ac = (audio_buffer_pool_t *) calloc(1, sizeof(audio_buffer_pool_t)); 158 | audio_buffer_t *audio_buffers = buffer_count ? (audio_buffer_t *) calloc(buffer_count, 159 | sizeof(audio_buffer_t)) : 0; 160 | ac->format = format->format; 161 | for (int i = 0; i < buffer_count; i++) { 162 | audio_init_buffer(audio_buffers + i, format, buffer_sample_count); 163 | audio_buffers[i].next = i != buffer_count - 1 ? &audio_buffers[i + 1] : NULL; 164 | } 165 | // todo one per channel? 166 | ac->free_list_spin_lock = spin_lock_init(SPINLOCK_ID_AUDIO_FREE_LIST_LOCK); 167 | ac->free_list = audio_buffers; 168 | ac->prepared_list_spin_lock = spin_lock_init(SPINLOCK_ID_AUDIO_PREPARED_LISTS_LOCK); 169 | ac->prepared_list = NULL; 170 | ac->prepared_list_tail = NULL; 171 | ac->connection = &connection_default; 172 | return ac; 173 | } 174 | 175 | audio_buffer_t *audio_new_wrapping_buffer(audio_buffer_format_t *format, mem_buffer_t *buffer) { 176 | audio_buffer_t *audio_buffer = (audio_buffer_t *) calloc(1, sizeof(audio_buffer_t)); 177 | if (audio_buffer) { 178 | audio_buffer->format = format; 179 | audio_buffer->buffer = buffer; 180 | audio_buffer->max_sample_count = buffer->size / format->sample_stride; 181 | audio_buffer->sample_count = 0; 182 | audio_buffer->next = 0; 183 | } 184 | return audio_buffer; 185 | 186 | } 187 | 188 | audio_buffer_pool_t * 189 | audio_new_producer_pool(audio_buffer_format_t *format, int buffer_count, int buffer_sample_count) { 190 | audio_buffer_pool_t *ac = audio_new_buffer_pool(format, buffer_count, buffer_sample_count); 191 | ac->type = audio_buffer_pool::ac_producer; 192 | return ac; 193 | } 194 | 195 | audio_buffer_pool_t * 196 | audio_new_consumer_pool(audio_buffer_format_t *format, int buffer_count, int buffer_sample_count) { 197 | audio_buffer_pool_t *ac = audio_new_buffer_pool(format, buffer_count, buffer_sample_count); 198 | ac->type = audio_buffer_pool::ac_consumer; 199 | return ac; 200 | } 201 | 202 | void audio_complete_connection(audio_connection_t *connection, audio_buffer_pool_t *producer_pool, 203 | audio_buffer_pool_t *consumer_pool) { 204 | assert(producer_pool->type == audio_buffer_pool::ac_producer); 205 | assert(consumer_pool->type == audio_buffer_pool::ac_consumer); 206 | producer_pool->connection = connection; 207 | consumer_pool->connection = connection; 208 | connection->producer_pool = producer_pool; 209 | connection->consumer_pool = consumer_pool; 210 | } 211 | 212 | void give_audio_buffer(audio_buffer_pool_t *ac, audio_buffer_t *buffer) { 213 | buffer->user_data = 0; 214 | assert(ac->connection); 215 | if (ac->type == audio_buffer_pool::ac_producer) 216 | ac->connection->producer_pool_give(ac->connection, buffer); 217 | else 218 | ac->connection->consumer_pool_give(ac->connection, buffer); 219 | } 220 | 221 | audio_buffer_t *take_audio_buffer(audio_buffer_pool_t *ac, bool block) { 222 | assert(ac->connection); 223 | if (ac->type == audio_buffer_pool::ac_producer) 224 | return ac->connection->producer_pool_take(ac->connection, block); 225 | else 226 | return ac->connection->consumer_pool_take(ac->connection, block); 227 | } 228 | 229 | // todo rename this - this is s16 to s16 230 | audio_buffer_t *mono_to_mono_consumer_take(audio_connection_t *connection, bool block) { 231 | return consumer_pool_take, Mono>(connection, block); 232 | } 233 | 234 | // todo rename this - this is s16 to s16 235 | audio_buffer_t *stereo_to_stereo_consumer_take(audio_connection_t *connection, bool block) { 236 | return consumer_pool_take, Stereo>(connection, block); 237 | } 238 | 239 | // todo rename this - this is s16 to s16 240 | audio_buffer_t *mono_to_stereo_consumer_take(audio_connection_t *connection, bool block) { 241 | return consumer_pool_take, Mono>(connection, block); 242 | } 243 | 244 | audio_buffer_t *mono_s8_to_mono_consumer_take(audio_connection_t *connection, bool block) { 245 | return consumer_pool_take, Mono>(connection, block); 246 | } 247 | 248 | audio_buffer_t *mono_s8_to_stereo_consumer_take(audio_connection_t *connection, bool block) { 249 | return consumer_pool_take, Mono>(connection, block); 250 | } 251 | 252 | void stereo_to_stereo_producer_give(audio_connection_t *connection, audio_buffer_t *buffer) { 253 | return producer_pool_blocking_give, Stereo>(connection, buffer); 254 | } 255 | -------------------------------------------------------------------------------- /src/common/pico_audio/audio_utils.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "hardware/regs/addressmap.h" 8 | #include "hardware/regs/sio.h" 9 | 10 | .syntax unified 11 | .cpu cortex-m0plus 12 | .thumb 13 | 14 | #define AUDIO_UPSAMPLE_SCALE_BITS 12 15 | .align 2 16 | .section .time_critical.audio_upsample 17 | .global audio_upsample 18 | .type audio_upsample,%function 19 | // step is fraction of an input sample per output sample * (1 << AUDIO_UPSAMPLE_SCALE_BITS) and should be < (1 << AUDIO_UPSAMPLE_SCALE_BITS) ... i.e. we we are upsampling (otherwise results are undefined) 20 | // void audio_upsample(int16_t *input, int16_t *output, int count, uint32_t step) 21 | .thumb_func 22 | audio_upsample: 23 | push {r4, r5, r6, r7, lr} 24 | lsls r2, #1 25 | mov ip, r1 26 | add ip, r2 27 | ldr r6, =SIO_BASE + SIO_INTERP0_ACCUM0_OFFSET 28 | // interp_configure_with_signed_and_blend 29 | ldr r4, =((AUDIO_UPSAMPLE_SCALE_BITS - 1) << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB) | (1 << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) | ((24 - AUDIO_UPSAMPLE_SCALE_BITS) << SIO_INTERP0_CTRL_LANE0_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE0_BLEND_BITS 30 | str r4, [r6, #SIO_INTERP0_CTRL_LANE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 31 | // interp_configure_with_signed_and_cross_input 32 | ldr r4, =((AUDIO_UPSAMPLE_SCALE_BITS - 8) << SIO_INTERP0_CTRL_LANE1_SHIFT_LSB) | (0 << SIO_INTERP0_CTRL_LANE1_MASK_LSB_LSB) | (7 << SIO_INTERP0_CTRL_LANE1_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE1_SIGNED_BITS | SIO_INTERP0_CTRL_LANE1_CROSS_INPUT_BITS 33 | str r4, [r6, #SIO_INTERP0_CTRL_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 34 | str r0, [r6, #SIO_INTERP0_BASE2_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 35 | movs r0, #0 36 | str r0, [r6, #SIO_INTERP0_ACCUM0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 37 | mov r7, r0 // last_offset = 0 (invalid) 38 | movs r2, #2 39 | 40 | // r0 0 41 | // r1 output 42 | // r2 2 43 | // r3 step 44 | // r4 temp 45 | // r5 temp 46 | // r6 interp_hw 47 | // r7 last_offset 48 | // ip end 49 | b 4f 50 | 51 | 1: // aligned 52 | ldr r5, [r4] 53 | str r5, [r6, #SIO_INTERP0_BASE_1AND0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 54 | 2: // unchanged sample ptr 55 | ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 56 | strh r4, [r1] 57 | str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 58 | add r1, r2 59 | cmp r1, ip 60 | beq 5f 61 | 3: // next sample 62 | ldr r4, [r6, #SIO_INTERP0_PEEK_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 63 | cmp r4, r7 64 | beq 2b 65 | mov r7, r4 66 | tst r4, r2 67 | beq 1b 68 | ldrsh r5, [r4, r0] 69 | str r5, [r6, #SIO_INTERP0_BASE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 70 | ldrsh r4, [r4, r2] 71 | str r4, [r6, #SIO_INTERP0_BASE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 72 | ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 73 | strh r4, [r1] 74 | str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 75 | add r1, r2 76 | 4: 77 | cmp r1, ip 78 | bne 3b 79 | 5: 80 | pop {r4, r5, r6, r7, pc} 81 | 82 | .align 2 83 | .section .time_critical.audio_upsample_words 84 | .global audio_upsample_words 85 | .type audio_upsample_words,%function 86 | // step is fraction of an input sample per output sample * (1 << AUDIO_UPSAMPLE_SCALE_BITS) and should be < (1 << AUDIO_UPSAMPLE_SCALE_BITS) ... i.e. we we are upsampling (otherwise results are undefined) 87 | // void audio_upsample(int16_t *input, int16_t *output_aligned, int output_word_count, uint32_t step) 88 | .thumb_func 89 | audio_upsample_words: 90 | push {r4, r5, r6, r7, lr} 91 | lsls r2, #2 92 | mov ip, r1 93 | add ip, r2 94 | ldr r6, =SIO_BASE + SIO_INTERP0_ACCUM0_OFFSET 95 | // interp_configure_with_blend 96 | ldr r4, =((AUDIO_UPSAMPLE_SCALE_BITS - 1) << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB) | (1 << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) | ((24 -AUDIO_UPSAMPLE_SCALE_BITS) << SIO_INTERP0_CTRL_LANE0_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE0_BLEND_BITS 97 | str r4, [r6, #SIO_INTERP0_CTRL_LANE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 98 | // interp_configure_with_signed_and_cross_input 99 | ldr r4, =((AUDIO_UPSAMPLE_SCALE_BITS - 8) << SIO_INTERP0_CTRL_LANE1_SHIFT_LSB) | (0 << SIO_INTERP0_CTRL_LANE1_MASK_LSB_LSB) | (7 << SIO_INTERP0_CTRL_LANE1_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE1_SIGNED_BITS | SIO_INTERP0_CTRL_LANE1_CROSS_INPUT_BITS 100 | str r4, [r6, #SIO_INTERP0_CTRL_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 101 | str r0, [r6, #SIO_INTERP0_BASE2_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 102 | movs r0, #0 103 | str r0, [r6, #SIO_INTERP0_ACCUM0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 104 | mov r7, r0 // last_offset = 0 (invalid) 105 | movs r2, #2 106 | 107 | // r0 0 108 | // r1 output 109 | // r2 2 110 | // r3 step 111 | // r4 temp 112 | // r5 temp 113 | // r6 interp_hw 114 | // r7 last_offset 115 | // ip end 116 | b 4f 117 | 118 | 1: // aligned A 119 | ldr r5, [r4] 120 | str r5, [r6, #SIO_INTERP0_BASE_1AND0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 121 | 2: // unchanged sample ptr A 122 | ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 123 | // output A 124 | strh r4, [r1] 125 | str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 126 | 127 | // next sample B 128 | ldr r4, [r6, #SIO_INTERP0_PEEK_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 129 | cmp r4, r7 130 | beq 6f 131 | 132 | mov r7, r4 133 | tst r4, r2 134 | bne 7f 135 | 136 | 8: 137 | // aligned B 138 | ldr r5, [r4] 139 | str r5, [r6, #SIO_INTERP0_BASE_1AND0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 140 | 141 | 6: // unchanged sample ptr B 142 | ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 143 | strh r4, [r1, r2] 144 | str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 145 | adds r1, #4 146 | cmp r1, ip 147 | beq 5f 148 | 149 | 3: // next sample A 150 | ldr r4, [r6, #SIO_INTERP0_PEEK_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 151 | cmp r4, r7 152 | beq 2b 153 | mov r7, r4 154 | tst r4, r2 155 | beq 1b 156 | ldrsh r5, [r4, r0] 157 | str r5, [r6, #SIO_INTERP0_BASE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 158 | ldrsh r4, [r4, r2] 159 | str r4, [r6, #SIO_INTERP0_BASE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 160 | ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 161 | strh r4, [r1] 162 | 163 | str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 164 | 165 | // next sample B 166 | ldr r4, [r6, #SIO_INTERP0_PEEK_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 167 | cmp r4, r7 168 | beq 6b 169 | mov r7, r4 170 | tst r4, r2 171 | beq 8b 172 | 7: // unalignedb 173 | ldrsh r5, [r4, r0] 174 | str r5, [r6, #SIO_INTERP0_BASE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 175 | ldrsh r4, [r4, r2] 176 | str r4, [r6, #SIO_INTERP0_BASE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 177 | ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 178 | strh r4, [r1, r2] 179 | str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 180 | adds r1, #4 181 | 182 | 4: 183 | cmp r1, ip 184 | bne 3b 185 | 186 | 5: 187 | pop {r4, r5, r6, r7, pc} 188 | 189 | .global audio_upsample_double 190 | .type audio_upsample_double,%function 191 | // step is fraction of an input sample per output sample * (1 << AUDIO_UPSAMPLE_SCALE_BITS) and should be < (1 << AUDIO_UPSAMPLE_SCALE_BITS) ... i.e. we we are upsampling (otherwise results are undefined) 192 | // void audio_upsample(int16_t *input, int16_t *output, int count, uint32_t step) 193 | .thumb_func 194 | audio_upsample_double: 195 | push {r4, r5, r6, r7, lr} 196 | lsls r2, #2 197 | mov ip, r1 198 | add ip, r2 199 | ldr r6, =SIO_BASE + SIO_INTERP0_ACCUM0_OFFSET 200 | // interp_configure_with_signed_and_blend 201 | ldr r4, =((AUDIO_UPSAMPLE_SCALE_BITS - 1) << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB) | (1 << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) | ((24 - AUDIO_UPSAMPLE_SCALE_BITS) << SIO_INTERP0_CTRL_LANE0_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE0_BLEND_BITS 202 | str r4, [r6, #SIO_INTERP0_CTRL_LANE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 203 | // interp_configure_with_signed_and_cross_input 204 | ldr r4, =((AUDIO_UPSAMPLE_SCALE_BITS - 8) << SIO_INTERP0_CTRL_LANE1_SHIFT_LSB) | (0 << SIO_INTERP0_CTRL_LANE1_MASK_LSB_LSB) | (7 << SIO_INTERP0_CTRL_LANE1_MASK_MSB_LSB) | SIO_INTERP0_CTRL_LANE1_SIGNED_BITS | SIO_INTERP0_CTRL_LANE1_CROSS_INPUT_BITS 205 | str r4, [r6, #SIO_INTERP0_CTRL_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 206 | str r0, [r6, #SIO_INTERP0_BASE2_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 207 | movs r0, #0 208 | str r0, [r6, #SIO_INTERP0_ACCUM0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 209 | mov r7, r0 // last_offset = 0 (invalid) 210 | movs r2, #2 211 | 212 | // r0 0 213 | // r1 output 214 | // r2 2 215 | // r3 step 216 | // r4 temp 217 | // r5 temp 218 | // r6 interp_hw 219 | // r7 last_offset 220 | // ip end 221 | b 4f 222 | 223 | 1: // aligned 224 | ldr r5, [r4] 225 | str r5, [r6, #SIO_INTERP0_BASE_1AND0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 226 | 2: // unchanged sample ptr 227 | ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 228 | strh r4, [r1] 229 | str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 230 | strh r4, [r1, #2] 231 | add r1, r2 232 | add r1, r2 233 | cmp r1, ip 234 | beq 5f 235 | 3: // next sample 236 | ldr r4, [r6, #SIO_INTERP0_PEEK_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 237 | cmp r4, r7 238 | beq 2b 239 | mov r7, r4 240 | tst r4, r2 241 | beq 1b 242 | ldrsh r5, [r4, r0] 243 | str r5, [r6, #SIO_INTERP0_BASE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 244 | ldrsh r4, [r4, r2] 245 | str r4, [r6, #SIO_INTERP0_BASE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 246 | ldr r4, [r6, #SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 247 | strh r4, [r1] 248 | str r3, [r6, #SIO_INTERP0_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET] 249 | strh r4, [r1, #2] 250 | add r1, r2 251 | add r1, r2 252 | 4: 253 | cmp r1, ip 254 | bne 3b 255 | 5: 256 | pop {r4, r5, r6, r7, pc} 257 | -------------------------------------------------------------------------------- /src/common/pico_audio/include/pico/audio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _PICO_AUDIO_H 8 | #define _PICO_AUDIO_H 9 | 10 | #include "pico.h" 11 | #include "pico/util/buffer.h" 12 | #include "hardware/sync.h" 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | /** \file audio.h 19 | * \defgroup pico_audio pico_audio 20 | * 21 | * Common API for audio output 22 | * 23 | */ 24 | 25 | // PICO_CONFIG: SPINLOCK_ID_AUDIO_FREE_LIST_LOCK, Spinlock number for the audio free list, min=0, max=31, default=6, group=audio 26 | #ifndef SPINLOCK_ID_AUDIO_FREE_LIST_LOCK 27 | #define SPINLOCK_ID_AUDIO_FREE_LIST_LOCK 6 28 | #endif 29 | 30 | // PICO_CONFIG: SPINLOCK_ID_AUDIO_PREPARED_LISTS_LOCK, Spinlock number for the audio prepared list, min=0, max=31, default=7, group=audio 31 | #ifndef SPINLOCK_ID_AUDIO_PREPARED_LISTS_LOCK 32 | #define SPINLOCK_ID_AUDIO_PREPARED_LISTS_LOCK 7 33 | #endif 34 | 35 | // PICO_CONFIG: PICO_AUDIO_NOOP, Enable/disable audio by forcing NOOPS, type=bool, default=0, group=audio 36 | #ifndef PICO_AUDIO_NOOP 37 | #define PICO_AUDIO_NOOP 0 38 | #endif 39 | 40 | 41 | #define AUDIO_BUFFER_FORMAT_PCM_S16 1 ///< signed 16bit PCM 42 | #define AUDIO_BUFFER_FORMAT_PCM_S8 2 ///< signed 8bit PCM 43 | #define AUDIO_BUFFER_FORMAT_PCM_U16 3 ///< unsigned 16bit PCM 44 | #define AUDIO_BUFFER_FORMAT_PCM_U8 4 ///< unsigned 8bit PCM 45 | 46 | /** \brief Audio format definition 47 | */ 48 | typedef struct audio_format { 49 | uint32_t sample_freq; ///< Sample frequency in Hz 50 | uint16_t format; ///< Audio format \ref audio_formats 51 | uint16_t channel_count; ///< Number of channels 52 | } audio_format_t; 53 | 54 | /** \brief Audio buffer format definition 55 | */ 56 | typedef struct audio_buffer_format { 57 | const audio_format_t *format; ///< Audio format 58 | uint16_t sample_stride; ///< Sample stride 59 | } audio_buffer_format_t; 60 | 61 | /** \brief Audio buffer definition 62 | */ 63 | typedef struct audio_buffer { 64 | mem_buffer_t *buffer; 65 | const audio_buffer_format_t *format; 66 | uint32_t sample_count; 67 | uint32_t max_sample_count; 68 | uint32_t user_data; // only valid while the user has the buffer 69 | // private - todo make an internal version 70 | struct audio_buffer *next; 71 | } audio_buffer_t; 72 | 73 | typedef struct audio_connection audio_connection_t; 74 | 75 | typedef struct audio_buffer_pool { 76 | enum { 77 | ac_producer, ac_consumer 78 | } type; 79 | const audio_format_t *format; 80 | // private 81 | audio_connection_t *connection; 82 | spin_lock_t *free_list_spin_lock; 83 | // ----- begin protected by free_list_spin_lock ----- 84 | audio_buffer_t *free_list; 85 | spin_lock_t *prepared_list_spin_lock; 86 | audio_buffer_t *prepared_list; 87 | audio_buffer_t *prepared_list_tail; 88 | } audio_buffer_pool_t; 89 | 90 | typedef struct audio_connection audio_connection_t; 91 | 92 | struct audio_connection { 93 | audio_buffer_t *(*producer_pool_take)(audio_connection_t *connection, bool block); 94 | 95 | void (*producer_pool_give)(audio_connection_t *connection, audio_buffer_t *buffer); 96 | 97 | audio_buffer_t *(*consumer_pool_take)(audio_connection_t *connection, bool block); 98 | 99 | void (*consumer_pool_give)(audio_connection_t *connection, audio_buffer_t *buffer); 100 | 101 | audio_buffer_pool_t *producer_pool; 102 | audio_buffer_pool_t *consumer_pool; 103 | }; 104 | 105 | /*! \brief Allocate and initialise an audio producer pool 106 | * \ingroup pico_audio 107 | * 108 | * \param format Format of the audio buffer 109 | * \param buffer_count \todo 110 | * \param buffer_sample_count \todo 111 | * \return Pointer to an audio_buffer_pool 112 | */ 113 | audio_buffer_pool_t *audio_new_producer_pool(audio_buffer_format_t *format, int buffer_count, 114 | int buffer_sample_count); 115 | 116 | /*! \brief Allocate and initialise an audio consumer pool 117 | * \ingroup pico_audio 118 | * 119 | * \param format Format of the audio buffer 120 | * \param buffer_count 121 | * \param buffer_sample_count 122 | * \return Pointer to an audio_buffer_pool 123 | */ 124 | audio_buffer_pool_t *audio_new_consumer_pool(audio_buffer_format_t *format, int buffer_count, 125 | int buffer_sample_count); 126 | 127 | /*! \brief Allocate and initialise an audio wrapping buffer 128 | * \ingroup pico_audio 129 | * 130 | * \param format Format of the audio buffer 131 | * \param buffer \todo 132 | * \return Pointer to an audio_buffer 133 | */ 134 | audio_buffer_t *audio_new_wrapping_buffer(audio_buffer_format_t *format, mem_buffer_t *buffer); 135 | 136 | /*! \brief Allocate and initialise an new audio buffer 137 | * \ingroup pico_audio 138 | * 139 | * \param format Format of the audio buffer 140 | * \param buffer_sample_count \todo 141 | * \return Pointer to an audio_buffer 142 | */ 143 | audio_buffer_t *audio_new_buffer(audio_buffer_format_t *format, int buffer_sample_count); 144 | 145 | /*! \brief Initialise an audio buffer 146 | * \ingroup pico_audio 147 | * 148 | * \param audio_buffer Pointer to an audio_buffer 149 | * \param format Format of the audio buffer 150 | * \param buffer_sample_count \todo 151 | */ 152 | void audio_init_buffer(audio_buffer_t *audio_buffer, audio_buffer_format_t *format, int buffer_sample_count); 153 | 154 | /*! \brief \todo 155 | * \ingroup pico_audio 156 | * 157 | * \param ac \todo 158 | * \param buffer \todo 159 | * \return Pointer to an audio_buffer 160 | */ 161 | void give_audio_buffer(audio_buffer_pool_t *ac, audio_buffer_t *buffer); 162 | 163 | /*! \brief \todo 164 | * \ingroup pico_audio 165 | * 166 | * \return Pointer to an audio_buffer 167 | */ 168 | audio_buffer_t *take_audio_buffer(audio_buffer_pool_t *ac, bool block); 169 | 170 | /*! \brief \todo 171 | * \ingroup pico_audio 172 | * 173 | */ 174 | static inline void release_audio_buffer(audio_buffer_pool_t *ac, audio_buffer_t *buffer) { 175 | buffer->sample_count = 0; 176 | give_audio_buffer(ac, buffer); 177 | } 178 | 179 | /*! \brief \todo 180 | * \ingroup pico_audio 181 | * 182 | * todo we are currently limited to 4095+1 input samples 183 | * step is fraction of an input sample per output sample * 0x1000 and should be < 0x1000 i.e. we we are up-sampling (otherwise results are undefined) 184 | */ 185 | void audio_upsample(int16_t *input, int16_t *output, uint output_count, uint32_t step); 186 | 187 | /*! \brief \todo 188 | * \ingroup pico_audio 189 | * similar but the output buffer is word aligned, and we output an even number of samples.. this is slightly faster than the above 190 | * todo we are currently limited to 4095+1 input samples 191 | * step is fraction of an input sample per output sample * 0x1000 and should be < 0x1000 i.e. we we are up-sampling (otherwise results are undefined) 192 | */ 193 | void audio_upsample_words(int16_t *input, int16_t *output_aligned, uint output_word_count, uint32_t step); 194 | 195 | /*! \brief \todo 196 | * \ingroup pico_audio 197 | */ 198 | void audio_upsample_double(int16_t *input, int16_t *output, uint output_count, uint32_t step); 199 | 200 | /*! \brief \todo 201 | * \ingroup pico_audio 202 | */ 203 | void audio_complete_connection(audio_connection_t *connection, audio_buffer_pool_t *producer, 204 | audio_buffer_pool_t *consumer); 205 | 206 | /*! \brief \todo 207 | * \ingroup pico_audio 208 | */ 209 | audio_buffer_t *get_free_audio_buffer(audio_buffer_pool_t *context, bool block); 210 | 211 | /*! \brief \todo 212 | * \ingroup pico_audio 213 | */ 214 | void queue_free_audio_buffer(audio_buffer_pool_t *context, audio_buffer_t *ab); 215 | 216 | /*! \brief \todo 217 | * \ingroup pico_audio 218 | */ 219 | audio_buffer_t *get_full_audio_buffer(audio_buffer_pool_t *context, bool block); 220 | 221 | /*! \brief \todo 222 | * \ingroup pico_audio 223 | */ 224 | void queue_full_audio_buffer(audio_buffer_pool_t *context, audio_buffer_t *ab); 225 | 226 | /*! \brief \todo 227 | * \ingroup pico_audio 228 | * 229 | * generally an pico_audio connection uses 3 of the defaults and does the hard work in one of them 230 | */ 231 | void consumer_pool_give_buffer_default(audio_connection_t *connection, audio_buffer_t *buffer); 232 | 233 | /*! \brief \todo 234 | * \ingroup pico_audio 235 | */ 236 | audio_buffer_t *consumer_pool_take_buffer_default(audio_connection_t *connection, bool block); 237 | 238 | /*! \brief \todo 239 | * \ingroup pico_audio 240 | */ 241 | void producer_pool_give_buffer_default(audio_connection_t *connection, audio_buffer_t *buffer); 242 | 243 | /*! \brief \todo 244 | * \ingroup pico_audio 245 | */ 246 | audio_buffer_t *producer_pool_take_buffer_default(audio_connection_t *connection, bool block); 247 | 248 | enum audio_correction_mode { 249 | none, 250 | fixed_dither, 251 | dither, 252 | noise_shaped_dither, 253 | }; 254 | 255 | struct buffer_copying_on_consumer_take_connection { 256 | struct audio_connection core; 257 | audio_buffer_t *current_producer_buffer; 258 | uint32_t current_producer_buffer_pos; 259 | }; 260 | 261 | struct producer_pool_blocking_give_connection { 262 | audio_connection_t core; 263 | audio_buffer_t *current_consumer_buffer; 264 | uint32_t current_consumer_buffer_pos; 265 | }; 266 | 267 | /*! \brief \todo 268 | * \ingroup pico_audio 269 | */ 270 | audio_buffer_t *mono_to_mono_consumer_take(audio_connection_t *connection, bool block); 271 | 272 | /*! \brief \todo 273 | * \ingroup pico_audio 274 | */ 275 | audio_buffer_t *mono_s8_to_mono_consumer_take(audio_connection_t *connection, bool block); 276 | 277 | /*! \brief \todo 278 | * \ingroup pico_audio 279 | */ 280 | audio_buffer_t *stereo_to_stereo_consumer_take(audio_connection_t *connection, bool block); 281 | 282 | /*! \brief \todo 283 | * \ingroup pico_audio 284 | */ 285 | audio_buffer_t *mono_to_stereo_consumer_take(audio_connection_t *connection, bool block); 286 | 287 | /*! \brief \todo 288 | * \ingroup pico_audio 289 | */ 290 | audio_buffer_t *mono_s8_to_stereo_consumer_take(audio_connection_t *connection, bool block); 291 | 292 | /*! \brief \todo 293 | * \ingroup pico_audio 294 | */ 295 | void stereo_to_stereo_producer_give(audio_connection_t *connection, audio_buffer_t *buffer); 296 | 297 | // not worth a separate header for now 298 | typedef struct __packed pio_audio_channel_config { 299 | uint8_t base_pin; 300 | uint8_t dma_channel; 301 | uint8_t pio_sm; 302 | } pio_audio_channel_config_t; 303 | 304 | #ifdef __cplusplus 305 | } 306 | #endif 307 | 308 | #endif //_AUDIO_H 309 | -------------------------------------------------------------------------------- /src/common/pico_audio/include/pico/sample_conversion.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef SOFTWARE_SAMPLE_CONVERSION_H 8 | #define SOFTWARE_SAMPLE_CONVERSION_H 9 | 10 | #include 11 | #include 12 | #include "pico/audio.h" 13 | #include "pico/util/buffer.h" 14 | 15 | template 16 | struct FmtDetails { 17 | public: 18 | static const uint channel_count = 1; 19 | static const uint frame_stride = channel_count * sizeof(_sample_t); 20 | typedef _sample_t sample_t; 21 | }; 22 | 23 | struct FmtU8 : public FmtDetails { 24 | }; 25 | 26 | struct FmtS8 : public FmtDetails { 27 | }; 28 | 29 | struct FmtU16 : public FmtDetails { 30 | }; 31 | 32 | struct FmtS16 : public FmtDetails { 33 | }; 34 | 35 | // Multi-channel is just N samples back to back 36 | template 37 | struct MultiChannelFmt { 38 | static const uint channel_count = ChannelCount; 39 | static const uint frame_stride = ChannelCount * Fmt::frame_stride; 40 | typedef typename Fmt::sample_t sample_t; 41 | }; 42 | 43 | // define Mono details as one channel 44 | template using Mono = MultiChannelFmt; 45 | 46 | // define Stereo details as two channels 47 | template using Stereo = MultiChannelFmt; 48 | 49 | template 50 | struct sample_converter { 51 | static typename ToFmt::sample_t convert_sample(const typename FromFmt::sample_t &sample); 52 | }; 53 | 54 | // noop conversion 55 | 56 | template 57 | struct sample_converter { 58 | static typename Fmt::sample_t convert_sample(const typename Fmt::sample_t &sample) { 59 | return sample; 60 | } 61 | }; 62 | 63 | // converters to S16 64 | template<> 65 | struct sample_converter { 66 | static int16_t convert_sample(const uint16_t &sample) { 67 | return sample ^ 0x8000u; 68 | } 69 | }; 70 | 71 | template<> 72 | struct sample_converter { 73 | static int16_t convert_sample(const int8_t &sample) { 74 | return sample << 8u; 75 | } 76 | }; 77 | 78 | template<> 79 | struct sample_converter { 80 | static int16_t convert_sample(const uint8_t &sample) { 81 | return (sample << 8u) ^ 0x8000u; 82 | } 83 | }; 84 | 85 | // converters to U16 86 | 87 | template<> 88 | struct sample_converter { 89 | static uint16_t convert_sample(const int8_t &sample) { 90 | return (sample << 8u) ^ 0x8000u; 91 | } 92 | }; 93 | 94 | template<> 95 | struct sample_converter { 96 | static uint16_t convert_sample(const uint8_t &sample) { 97 | return sample << 8u; 98 | } 99 | }; 100 | 101 | template<> 102 | struct sample_converter { 103 | static uint16_t convert_sample(const int16_t &sample) { 104 | return sample ^ 0x8000u; 105 | } 106 | }; 107 | 108 | // converters to S8 109 | 110 | template<> 111 | struct sample_converter { 112 | static int8_t convert_sample(const uint16_t &sample) { 113 | return (sample ^ 0x8000u) >> 8u; 114 | } 115 | }; 116 | 117 | template<> 118 | struct sample_converter { 119 | static int8_t convert_sample(const uint8_t &sample) { 120 | return sample ^ 0x80; 121 | } 122 | }; 123 | 124 | template<> 125 | struct sample_converter { 126 | static int8_t convert_sample(const int16_t &sample) { 127 | return sample >> 8u; 128 | } 129 | }; 130 | 131 | // converters to U8 132 | 133 | template<> 134 | struct sample_converter { 135 | static uint8_t convert_sample(const uint16_t &sample) { 136 | return sample >> 8u; 137 | } 138 | }; 139 | 140 | template<> 141 | struct sample_converter { 142 | static uint8_t convert_sample(const int8_t &sample) { 143 | return sample ^ 0x80; 144 | } 145 | }; 146 | 147 | template<> 148 | struct sample_converter { 149 | static uint8_t convert_sample(const int16_t &sample) { 150 | return (sample ^ 0x8000u) >> 8u; 151 | } 152 | }; 153 | 154 | // template type for doing sample conversion 155 | template 156 | struct converting_copy { 157 | static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count); 158 | }; 159 | 160 | // Efficient copies of same sample type 161 | 162 | template 163 | struct converting_copy, MultiChannelFmt> { 164 | static void copy(typename MultiChannelFmt::sample_t *dest, 165 | const typename MultiChannelFmt::sample_t *src, 166 | uint sample_count) { 167 | memcpy((void *) dest, (const void *) src, sample_count * MultiChannelFmt::frame_stride); 168 | } 169 | }; 170 | 171 | // N channel to N channel 172 | template 173 | struct converting_copy, MultiChannelFmt> { 174 | static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) { 175 | for (uint i = 0; i < sample_count * NumChannels; i++) { 176 | *dest++ = sample_converter::convert_sample(*src++); 177 | } 178 | } 179 | }; 180 | 181 | 182 | // mono->stereo conversion 183 | template 184 | struct converting_copy, Mono> { 185 | static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) { 186 | for (; sample_count; sample_count--) { 187 | typename ToFmt::sample_t mono_sample = sample_converter::convert_sample(*src++); 188 | *dest++ = mono_sample; 189 | *dest++ = mono_sample; 190 | } 191 | } 192 | }; 193 | 194 | // stereo->mono conversion 195 | template 196 | struct converting_copy, Stereo> { 197 | static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) { 198 | for (; sample_count; sample_count--) { 199 | // average first in case precision is better in source 200 | typename FromFmt::sample_t averaged_sample = (src[0] + src[1]) / 2; 201 | src += 2; 202 | *dest++ = sample_converter::convert_sample(averaged_sample); 203 | } 204 | } 205 | }; 206 | 207 | template 208 | audio_buffer_t *consumer_pool_take(audio_connection_t *connection, bool block) { 209 | struct buffer_copying_on_consumer_take_connection *cc = (struct buffer_copying_on_consumer_take_connection *) connection; 210 | // for now we block until we have all the data in consumer buffers 211 | audio_buffer_t *buffer = get_free_audio_buffer(cc->core.consumer_pool, block); 212 | if (!buffer) return NULL; 213 | assert(buffer->format->sample_stride == ToFmt::frame_stride); 214 | 215 | uint32_t pos = 0; 216 | while (pos < buffer->max_sample_count) { 217 | if (!cc->current_producer_buffer) { 218 | cc->current_producer_buffer = get_full_audio_buffer(cc->core.producer_pool, block); 219 | if (!cc->current_producer_buffer) { 220 | assert(!block); 221 | if (!pos) { 222 | queue_free_audio_buffer(cc->core.consumer_pool, buffer); 223 | return NULL; 224 | } 225 | break; 226 | } 227 | assert(cc->current_producer_buffer->format->format->channel_count == FromFmt::channel_count); 228 | assert(cc->current_producer_buffer->format->sample_stride == FromFmt::frame_stride); 229 | cc->current_producer_buffer_pos = 0; 230 | } 231 | uint sample_count = std::min(buffer->max_sample_count - pos, 232 | cc->current_producer_buffer->sample_count - cc->current_producer_buffer_pos); 233 | converting_copy::copy( 234 | ((typename ToFmt::sample_t *) buffer->buffer->bytes) + pos * ToFmt::channel_count, 235 | ((typename FromFmt::sample_t *) cc->current_producer_buffer->buffer->bytes) + 236 | cc->current_producer_buffer_pos * FromFmt::channel_count, 237 | sample_count); 238 | pos += sample_count; 239 | cc->current_producer_buffer_pos += sample_count; 240 | if (cc->current_producer_buffer_pos == cc->current_producer_buffer->sample_count) { 241 | queue_free_audio_buffer(cc->core.producer_pool, cc->current_producer_buffer); 242 | cc->current_producer_buffer = NULL; 243 | } 244 | } 245 | buffer->sample_count = pos; 246 | return buffer; 247 | } 248 | 249 | template 250 | void producer_pool_blocking_give(audio_connection_t *connection, audio_buffer_t *buffer) { 251 | struct producer_pool_blocking_give_connection *pbc = (struct producer_pool_blocking_give_connection *) connection; 252 | // for now we block until we have all the data in consumer buffers 253 | uint32_t pos = 0; 254 | while (pos < buffer->sample_count) { 255 | if (!pbc->current_consumer_buffer) { 256 | pbc->current_consumer_buffer = get_free_audio_buffer(pbc->core.consumer_pool, true); 257 | pbc->current_consumer_buffer_pos = 0; 258 | } 259 | uint sample_count = std::min(buffer->sample_count - pos, 260 | pbc->current_consumer_buffer->max_sample_count - pbc->current_consumer_buffer_pos); 261 | assert(buffer->format->sample_stride == FromFmt::frame_stride); 262 | assert(buffer->format->format->channel_count == FromFmt::channel_count); 263 | converting_copy::copy( 264 | ((typename ToFmt::sample_t *) pbc->current_consumer_buffer->buffer->bytes) + 265 | pbc->current_consumer_buffer_pos * ToFmt::channel_count, 266 | ((typename FromFmt::sample_t *) buffer->buffer->bytes) + pos * FromFmt::channel_count, sample_count); 267 | pos += sample_count; 268 | pbc->current_consumer_buffer_pos += sample_count; 269 | if (pbc->current_consumer_buffer_pos == pbc->current_consumer_buffer->max_sample_count) { 270 | pbc->current_consumer_buffer->sample_count = pbc->current_consumer_buffer->max_sample_count; 271 | queue_full_audio_buffer(pbc->core.consumer_pool, pbc->current_consumer_buffer); 272 | pbc->current_consumer_buffer = NULL; 273 | } 274 | } 275 | // todo this should be a connection configuration (or a seaparate connection type) 276 | #ifdef BLOCKING_GIVE_SYNCHRONIZE_BUFFERS 277 | if (pbc->current_consumer_buffer) { 278 | pbc->current_consumer_buffer->sample_count = pbc->current_consumer_buffer_pos; 279 | queue_full_audio_buffer(pbc->core.consumer_pool, pbc->current_consumer_buffer); 280 | pbc->current_consumer_buffer = NULL; 281 | } 282 | #endif 283 | assert(pos == buffer->sample_count); 284 | queue_free_audio_buffer(pbc->core.producer_pool, buffer); 285 | } 286 | 287 | #endif //SOFTWARE_SAMPLE_CONVERSION_H 288 | -------------------------------------------------------------------------------- /src/common/pico_scanvideo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (PICO_ON_DEVICE AND "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" AND PICO_DEOPTIMIZED_DEBUG) 2 | message("scanvideo is disabled for 'Debug' builds when PICO_DEOPTIMIZED_DEBUG=1") 3 | else() 4 | add_library(pico_scanvideo INTERFACE) 5 | 6 | pico_generate_pio_header(pico_scanvideo ${CMAKE_CURRENT_LIST_DIR}/scanvideo.pio PATH include/pico/scanvideo) 7 | 8 | target_sources(pico_scanvideo INTERFACE 9 | ${CMAKE_CURRENT_LIST_DIR}/vga_modes.c 10 | ) 11 | 12 | target_include_directories(pico_scanvideo INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 13 | target_link_libraries(pico_scanvideo INTERFACE pico_base_headers pico_util_buffer) 14 | endif() 15 | -------------------------------------------------------------------------------- /src/common/pico_scanvideo/include/pico/scanvideo/composable_scanline.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef SCANVIDEO_COMPOSABLE_SCANLINE_H_ 8 | #define SCANVIDEO_COMPOSABLE_SCANLINE_H_ 9 | 10 | #include "pico/types.h" 11 | #include "scanvideo.pio.h" 12 | 13 | // PICO_CONFIG: PICO_SCANVIDEO_USE_RAW1P_2CYCLE, Enable/disable SVideo use raw 1P 2 cycle, type=bool, default=0, group=video+- 14 | #ifndef PICO_SCANVIDEO_USE_RAW1P_2CYCLE 15 | #define PICO_SCANVIDEO_USE_RAW1P_2CYCLE 0 16 | #endif 17 | 18 | #if !PICO_SCANVIDEO_USE_RAW1P_2CYCLE 19 | #define video_24mhz_composable_prefix video_24mhz_composable_default 20 | #else 21 | #define video_24mhz_composable_prefix video_24mhz_composable_raw1p_2cycle 22 | #endif 23 | 24 | // seems needed on some platforms 25 | #define __EXTRA_CONCAT(x, y) __CONCAT(x,y) 26 | #define video_24mhz_composable_program_extern(x) __EXTRA_CONCAT( __EXTRA_CONCAT(video_24mhz_composable_prefix, _offset_), x) 27 | #define __DVP_JMP(x) ((unsigned)video_24mhz_composable_program_extern(x)) 28 | #define COMPOSABLE_COLOR_RUN __DVP_JMP(color_run) 29 | #define COMPOSABLE_EOL_ALIGN __DVP_JMP(end_of_scanline_ALIGN) 30 | #define COMPOSABLE_EOL_SKIP_ALIGN __DVP_JMP(end_of_scanline_skip_ALIGN) 31 | #define COMPOSABLE_RAW_RUN __DVP_JMP(raw_run) 32 | #define COMPOSABLE_RAW_1P __DVP_JMP(raw_1p) 33 | #define COMPOSABLE_RAW_2P __DVP_JMP(raw_2p) 34 | #if !PICO_SCANVIDEO_USE_RAW1P_2CYCLE 35 | #define COMPOSABLE_RAW_1P_SKIP_ALIGN __DVP_JMP(raw_1p_skip_ALIGN) 36 | #else 37 | #define COMPOSABLE_RAW_1P_2CYCLE __DVP_JMP(raw_1p_2cycle) 38 | #endif 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/common/pico_scanvideo/include/pico/scanvideo/scanvideo_base.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef SCANVIDEO_scanvideo_H_ 8 | #define SCANVIDEO_scanvideo_H_ 9 | 10 | #include "pico/types.h" 11 | 12 | #if !PICO_NO_HARDWARE 13 | 14 | #include "hardware/pio.h" 15 | 16 | #endif 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | /** \file scanvideo_base.h 23 | * \defgroup pico_scanvideo pico_scanvideo 24 | * 25 | * Common Scan-out Video API 26 | */ 27 | // == CONFIG ============ 28 | #ifndef PICO_SCANVIDEO_PLANE_COUNT 29 | #define PICO_SCANVIDEO_PLANE_COUNT 1 30 | #endif 31 | 32 | #ifndef PICO_SCANVIDEO_SCANLINE_BUFFER_COUNT 33 | #define PICO_SCANVIDEO_SCANLINE_BUFFER_COUNT 8 34 | #endif 35 | 36 | #ifndef PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS 37 | #define PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS 0 38 | #endif 39 | 40 | #ifndef PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA 41 | #define PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA 0 42 | #endif 43 | 44 | #ifndef PICO_SCANVIDEO_PLANE1_FIXED_FRAGMENT_DMA 45 | #define PICO_SCANVIDEO_PLANE1_FIXED_FRAGMENT_DMA 0 46 | #endif 47 | 48 | #if PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA || PICO_SCANVIDEO_PLANE1_FIXED_FRAGMENT_DMA 49 | #define PICO_SCANVIDEO_PLANE1_FRAGMENT_DMA 1 50 | #endif 51 | 52 | #ifndef PICO_SCANVIDEO_PLANE2_VARIABLE_FRAGMENT_DMA 53 | #define PICO_SCANVIDEO_PLANE2_VARIABLE_FRAGMENT_DMA 0 54 | #endif 55 | 56 | #ifndef PICO_SCANVIDEO_PLANE2_FIXED_FRAGMENT_DMA 57 | #define PICO_SCANVIDEO_PLANE2_FIXED_FRAGMENT_DMA 0 58 | #endif 59 | 60 | #if PICO_SCANVIDEO_PLANE2_VARIABLE_FRAGMENT_DMA || PICO_SCANVIDEO_PLANE2_FIXED_FRAGMENT_DMA 61 | #define PICO_SCANVIDEO_PLANE2_FRAGMENT_DMA 1 62 | #endif 63 | 64 | #ifndef PICO_SCANVIDEO_PLANE3_VARIABLE_FRAGMENT_DMA 65 | #define PICO_SCANVIDEO_PLANE3_VARIABLE_FRAGMENT_DMA 0 66 | #endif 67 | 68 | #ifndef PICO_SCANVIDEO_PLANE3_FIXED_FRAGMENT_DMA 69 | #define PICO_SCANVIDEO_PLANE3_FIXED_FRAGMENT_DMA 0 70 | #endif 71 | 72 | #if PICO_SCANVIDEO_PLANE3_VARIABLE_FRAGMENT_DMA || PICO_SCANVIDEO_PLANE3_FIXED_FRAGMENT_DMA 73 | #define PICO_SCANVIDEO_PLANE3_FRAGMENT_DMA 1 74 | #endif 75 | 76 | #ifndef PICO_SCANVIDEO_ENABLE_CLOCK_PIN 77 | #define PICO_SCANVIDEO_ENABLE_CLOCK_PIN 0 78 | #endif 79 | 80 | #ifndef PICO_SCANVIDEO_ENABLE_DEN_PIN 81 | #define PICO_SCANVIDEO_ENABLE_DEN_PIN 0 82 | #endif 83 | 84 | #ifndef PICO_SCANVIDEO_COLOR_PIN_BASE 85 | #define PICO_SCANVIDEO_COLOR_PIN_BASE 0 86 | #endif 87 | 88 | #ifndef PICO_SCANVIDEO_COLOR_PIN_COUNT 89 | #define PICO_SCANVIDEO_COLOR_PIN_COUNT 16 90 | #endif 91 | 92 | #ifndef PICO_SCANVIDEO_SYNC_PIN_BASE 93 | #define PICO_SCANVIDEO_SYNC_PIN_BASE (PICO_SCANVIDEO_COLOR_PIN_BASE + PICO_SCANVIDEO_COLOR_PIN_COUNT) 94 | #endif 95 | 96 | #ifndef PICO_SCANVIDEO_ENABLE_VIDEO_CLOCK_DOWN 97 | #define PICO_SCANVIDEO_ENABLE_VIDEO_CLOCK_DOWN 0 98 | #endif 99 | 100 | // todo make multi plane play nicely with mode swapping; 101 | // today we have hard coded blank/empty lines 102 | 103 | //#define PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA 1 104 | //#define PICO_SCANVIDEO_PLANE2_VARIABLE_FRAGMENT_DMA 1 105 | 106 | #ifndef PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS 107 | #define PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS 180 108 | #endif 109 | #ifndef PICO_SCANVIDEO_MAX_SCANLINE_BUFFER2_WORDS 110 | #define PICO_SCANVIDEO_MAX_SCANLINE_BUFFER2_WORDS PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS 111 | #endif 112 | #ifndef PICO_SCANVIDEO_MAX_SCANLINE_BUFFER3_WORDS 113 | #define PICO_SCANVIDEO_MAX_SCANLINE_BUFFER3_WORDS PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS 114 | #endif 115 | 116 | //extern struct semaphore vmode_updated; 117 | 118 | 119 | // ====================== 120 | 121 | #define BPP 16 122 | 123 | // most likely 24000000 124 | extern const uint32_t video_clock_freq; 125 | 126 | // todo pragma pack? 127 | typedef struct scanvideo_timing { 128 | uint32_t clock_freq; 129 | 130 | uint16_t h_active; 131 | uint16_t v_active; 132 | 133 | uint16_t h_front_porch; 134 | uint16_t h_pulse; 135 | uint16_t h_total; 136 | uint8_t h_sync_polarity; 137 | 138 | uint16_t v_front_porch; 139 | uint16_t v_pulse; 140 | uint16_t v_total; 141 | uint8_t v_sync_polarity; 142 | 143 | uint8_t enable_clock; 144 | uint8_t clock_polarity; 145 | 146 | uint8_t enable_den; 147 | } scanvideo_timing_t; 148 | 149 | typedef struct scanvideo_pio_program scanvideo_pio_program_t; 150 | 151 | // todo we need to handle blank data correctly (perhaps DMA should just not start for that scanline, 152 | // though obviously this is slightly more complicated with multiple playfields, or perhaps worse with 153 | // just one 154 | typedef struct scanvideo_mode { 155 | const scanvideo_timing_t *default_timing; 156 | const scanvideo_pio_program_t *pio_program; 157 | 158 | uint16_t width; 159 | uint16_t height; 160 | uint8_t xscale; // 1 == normal, 2 == double wide etc. up to what pio timing allows (not sure I have an assert based on delays) 161 | uint16_t yscale; // same for y scale (except any yscale is possible) 162 | // if > 1 then yscale is divided by this to provide the effective yscale; 163 | // note that yscale must be > yscale_denominator; i.e. only stretching is supported 164 | uint16_t yscale_denominator; 165 | } scanvideo_mode_t; 166 | 167 | extern bool scanvideo_setup(const scanvideo_mode_t *mode); 168 | extern bool scanvideo_setup_with_timing(const scanvideo_mode_t *mode, const scanvideo_timing_t *timing); 169 | extern void scanvideo_timing_enable(bool enable); 170 | // these take effect after the next vsync 171 | extern void scanvideo_display_enable(bool enable); 172 | // doesn't exist yet! 173 | // extern void video_set_display_mode(const struct scanvideo_mode *mode); 174 | 175 | // --- scanline management --- 176 | 177 | typedef struct scanvideo_scanline_buffer { 178 | uint32_t scanline_id; 179 | uint32_t *data; 180 | uint16_t data_used; 181 | uint16_t data_max; 182 | #if PICO_SCANVIDEO_PLANE1_FIXED_FRAGMENT_DMA 183 | uint16_t fragment_words; 184 | #endif 185 | #if PICO_SCANVIDEO_PLANE_COUNT > 1 186 | uint32_t *data2; 187 | uint16_t data2_used; 188 | uint16_t data2_max; 189 | #if PICO_SCANVIDEO_PLANE_COUNT > 2 190 | uint32_t *data3; 191 | uint16_t data3_used; 192 | uint16_t data3_max; 193 | #endif 194 | #endif 195 | void *user_data; 196 | #if PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS 197 | struct scanvideo_scanline_buffer *link; 198 | uint8_t link_after; 199 | #endif 200 | uint8_t status; 201 | } scanvideo_scanline_buffer_t; 202 | 203 | enum { 204 | SCANLINE_OK = 1, 205 | SCANLINE_ERROR, 206 | SCANLINE_SKIPPED 207 | }; 208 | 209 | // note frame numbers wrap 210 | static inline uint16_t scanvideo_frame_number(uint32_t scanline_id) { 211 | return (uint16_t) (scanline_id >> 16u); 212 | } 213 | 214 | static inline uint16_t scanvideo_scanline_number(uint32_t scanline_id) { 215 | return (uint16_t) scanline_id; 216 | } 217 | 218 | /** 219 | * @return the current vga mode (if there is one) 220 | */ 221 | extern scanvideo_mode_t scanvideo_get_mode(); 222 | 223 | /** 224 | * @return the next scanline_id to be displayed (may be from the next frame) 225 | */ 226 | extern uint32_t scanvideo_get_next_scanline_id(); 227 | 228 | /** 229 | * @return true if in the vblank interval 230 | */ 231 | extern bool scanvideo_in_vblank(); 232 | /** 233 | * @return true if in the hblank interval (or more accurately scanline data is not currently being sent to the PIO, which roughly corresponds, but is not exact). Note also that in 234 | * yscale-d modes, there are height * yscale hblank intervals per frame. 235 | */ 236 | extern bool scanvideo_in_hblank(); 237 | 238 | extern void scanvideo_wait_for_vblank(); 239 | 240 | extern uint32_t scanvideo_wait_for_scanline_complete(uint32_t scanline_id); 241 | /** 242 | * Acquire a scanline that needs generating. The scanline_id field indicates which scanline is required. 243 | * 244 | * This method may be called concurrently 245 | * 246 | * @param block true to block if the vga system is not ready to generate a new scanline 247 | * @return the scanline_buffer or NULL if block is false, and the vga system is not ready 248 | */ 249 | scanvideo_scanline_buffer_t *scanvideo_begin_scanline_generation(bool block); 250 | scanvideo_scanline_buffer_t *scanvideo_begin_scanline_generation2(scanvideo_scanline_buffer_t **second, bool block); 251 | #if PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS 252 | scanvideo_scanline_buffer_t *scanvideo_begin_scanline_generation_linked(uint n, bool block); 253 | #endif 254 | 255 | /** 256 | * Return a scanline that has been generated / or at least the client is done with. 257 | * 258 | * The status field indicates whether the scanline was actually generated OK 259 | * 260 | * This method may be called concurrently (for different buffers) 261 | * 262 | * @param scanline_buffer \todo 263 | */ 264 | void scanvideo_end_scanline_generation(scanvideo_scanline_buffer_t *scanline_buffer); 265 | 266 | typedef uint (*scanvideo_scanline_repeat_count_fn)(uint32_t scanline_id); 267 | void scanvideo_set_scanline_repeat_fn(scanvideo_scanline_repeat_count_fn fn); 268 | #if PICO_SCANVIDEO_SCANLINE_RELEASE_FUNCTION 269 | typedef void (*scanvideo_scanline_release_fn)(); 270 | void scanvideo_set_scanline_release_fn(scanvideo_scanline_release_fn fn); 271 | #endif 272 | 273 | extern const scanvideo_timing_t vga_timing_640x480_60_default; 274 | extern const scanvideo_timing_t vga_timing_1280x1024_60_default; 275 | extern const scanvideo_timing_t vga_timing_wide_480_50; 276 | extern const scanvideo_timing_t vga_timing_648x480_60_alt1; 277 | 278 | extern const scanvideo_mode_t vga_mode_160x120_60; // 3d monster maze anyone :-) 279 | extern const scanvideo_mode_t vga_mode_213x160_60; 280 | extern const scanvideo_mode_t vga_mode_320x240_60; 281 | extern const scanvideo_mode_t vga_mode_640x480_60; 282 | extern const scanvideo_mode_t vga_mode_800x600_54; 283 | extern const scanvideo_mode_t vga_mode_800x600_60; 284 | extern const scanvideo_mode_t vga_mode_1024x768_63; 285 | extern const scanvideo_mode_t vga_mode_1280x1024_40; 286 | extern const scanvideo_mode_t vga_mode_1024x768_60; 287 | extern const scanvideo_mode_t vga_mode_1280x1024_60; 288 | extern const scanvideo_mode_t vga_mode_720p_60; 289 | extern const scanvideo_mode_t vga_mode_1080p_60; 290 | extern const scanvideo_mode_t vga_mode_1440p_60; 291 | 292 | extern const scanvideo_mode_t vga_mode_tft_800x480_50; 293 | extern const scanvideo_mode_t vga_mode_tft_400x240_50; 294 | 295 | #ifndef NDEBUG 296 | // todo this is only for vga composable 24... should exist behind mode impl 297 | extern void validate_scanline(const uint32_t *dma_data, uint dma_data_size, uint max_pixels, uint expected_width); 298 | #endif 299 | 300 | // mode implementation 301 | 302 | struct scanvideo_pio_program { 303 | #if !PICO_NO_HARDWARE 304 | const pio_program_t *program; 305 | const uint8_t entry_point; 306 | // modifiable_instructions is of size program->length 307 | bool (*adapt_for_mode)(const scanvideo_pio_program_t *program, const scanvideo_mode_t *mode, 308 | scanvideo_scanline_buffer_t *missing_scanline_buffer, uint16_t *modifiable_instructions); 309 | pio_sm_config (*configure_pio)(pio_hw_t *pio, uint sm, uint offset); 310 | #else 311 | const char *id; 312 | #endif 313 | }; 314 | 315 | extern const scanvideo_pio_program_t video_24mhz_composable; 316 | 317 | #if !PICO_NO_HARDWARE 318 | extern void scanvideo_default_configure_pio(pio_hw_t *pio, uint sm, uint offset, pio_sm_config *config, bool overlay); 319 | #endif 320 | 321 | #ifndef PICO_SPINLOCK_ID_VIDEO_SCANLINE_LOCK 322 | #define PICO_SPINLOCK_ID_VIDEO_SCANLINE_LOCK 2 323 | #endif 324 | 325 | #ifndef PICO_SPINLOCK_ID_VIDEO_FREE_LIST_LOCK 326 | #define PICO_SPINLOCK_ID_VIDEO_FREE_LIST_LOCK 3 327 | #endif 328 | 329 | #ifndef PICO_SPINLOCK_ID_VIDEO_DMA_LOCK 330 | #define PICO_SPINLOCK_ID_VIDEO_DMA_LOCK 4 331 | #endif 332 | 333 | #ifndef PICO_SPINLOCK_ID_VIDEO_IN_USE_LOCK 334 | #define PICO_SPINLOCK_ID_VIDEO_IN_USE_LOCK 5 335 | #endif 336 | 337 | // note this is not necessarily an absolute gpio pin mask, it is still shifted by PICO_SCANVIDEO_COLOR_PIN_BASE 338 | #define PICO_SCANVIDEO_ALPHA_MASK (1u << PICO_SCANVIDEO_ALPHA_PIN) 339 | 340 | #ifndef PICO_SCANVIDEO_PIXEL_FROM_RGB8 341 | #define PICO_SCANVIDEO_PIXEL_FROM_RGB8(r, g, b) ((((b)>>3u)<>3u)<>3u)<>PICO_SCANVIDEO_PIXEL_RSHIFT)&0x1f) 350 | #endif 351 | 352 | #ifndef PICO_SCANVIDEO_G5_FROM_PIXEL 353 | #define PICO_SCANVIDEO_G5_FROM_PIXEL(p) (((p)>>PICO_SCANVIDEO_PIXEL_GSHIFT)&0x1f) 354 | #endif 355 | 356 | #ifndef PICO_SCANVIDEO_B5_FROM_PIXEL 357 | #define PICO_SCANVIDEO_B5_FROM_PIXEL(p) (((p)>>PICO_SCANVIDEO_PIXEL_BSHIFT)&0x1f) 358 | #endif 359 | 360 | #ifdef __cplusplus 361 | } 362 | #endif 363 | 364 | #endif //_VIDEO_H 365 | -------------------------------------------------------------------------------- /src/common/pico_scanvideo/scanvideo.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | ; Default scanline program (|| means aligned word boundary, | means hword boundary) 8 | .program video_24mhz_composable_default 9 | .origin 0 ; must load at zero (offsets are hardcoded in instruction stream) 10 | .define extra0 0 ; set later by code based on xscale 11 | .define extra1 0 ; set later by code (1 more than extra0) 12 | 13 | ; note bpp must be a factor of 32 14 | .define bpp 16 15 | ;.define bpp 8 16 | 17 | public end_of_scanline_skip_ALIGN: ; || jmp end_of_scanline_skip_ALIGN | ignored || 18 | ; was 16 but we just discard the reset of the OSR 19 | ; so as to also support 8 bit grayscale 20 | out null, 32; 21 | 22 | public end_of_scanline_ALIGN: ; | jmp end_of_scanline_ALIGN || 23 | 24 | public entry_point: 25 | wait irq, 4 ; todo perhaps change this to out exec, 16... so that we can do multiple things (including setting black pixel) 26 | public nop_raw: 27 | out pc, bpp 28 | 29 | public delay_h_0: 30 | public color_run: ; | jmp color_run | color | count-3 | 31 | out pins, bpp 32 | out x, bpp 33 | color_loop: 34 | public delay_a_1: 35 | jmp x-- color_loop [extra1] 36 | public nop_extra1: 37 | public delay_b_1: 38 | out pc, bpp [extra1] 39 | 40 | public raw_run: ; | jmp raw_run | color | n | | 41 | public delay_c_0: 42 | out pins, bpp [extra0] 43 | out x, bpp 44 | pixel_loop: 45 | public delay_d_0: 46 | out pins, bpp [extra0] 47 | jmp x-- pixel_loop 48 | .wrap_target 49 | public raw_1p: ; | jmp raw_1p | color | 50 | public delay_e_0: 51 | out pins, bpp [extra0] 52 | out pc, bpp 53 | 54 | public raw_2p: ; | jmp raw_2p | color | color | 55 | public delay_f_1: 56 | out pins, bpp [extra1] 57 | .wrap 58 | 59 | public raw_1p_skip_ALIGN: ; | jmp raw_1p_skip_ALIGN | color | ignored || 60 | out pins, 32 ; requires correct out mask 61 | public nop_extra0: 62 | public delay_g_0: 63 | out pc, bpp [extra0] ; note moved extra0 from above line, so we can use this instruction for 64 | 65 | ; Variant that replaces raw_1p_skip_work_ALIGN with raw1p_2cycle 66 | .program video_24mhz_composable_raw1p_2cycle 67 | .origin 0 ; must load at zero (offsets are hardcoded in instruction stream) 68 | .define extra0 0 ; set later by code based on xscale 69 | .define extra1 0 ; set later by code (1 more than extra0) 70 | 71 | ; note bpp must be a factor of 32 72 | .define bpp 16 73 | ;.define bpp 8 74 | 75 | public end_of_scanline_skip_ALIGN: ; || jmp end_of_scanline_skip_ALIGN | ignored || 76 | ; was 16 but we just discard the reset of the OSR 77 | ; so as to also support 8 bit grayscale 78 | out null, 32; 79 | 80 | public end_of_scanline_ALIGN: ; | jmp end_of_scanline_ALIGN || 81 | 82 | public entry_point: 83 | wait irq, 4 ; todo perhaps change this to out exec, 16... so that we can do multiple things (including setting black pixel) 84 | public nop_raw: 85 | out pc, bpp 86 | 87 | public color_run: ; | jmp color_run | color | count-3 | 88 | public delay_h_0: 89 | out pins, bpp 90 | out x, bpp 91 | color_loop: 92 | public delay_a_1: 93 | jmp x-- color_loop [extra1] 94 | public nop_extra1: 95 | public delay_b_1: 96 | out pc, bpp [extra1] 97 | 98 | public raw_run: ; | jmp raw_run | color | n | | 99 | public delay_c_0: 100 | out pins, bpp [extra0] 101 | out x, bpp 102 | pixel_loop: 103 | public delay_d_0: 104 | out pins, bpp [extra0] 105 | jmp x-- pixel_loop 106 | .wrap_target 107 | public raw_1p: ; | jmp raw_1p | color | 108 | public delay_e_0: 109 | out pins, bpp [extra0] 110 | out pc, bpp 111 | 112 | public raw_2p: ; | jmp raw_2p | color | color | 113 | public delay_f_1: 114 | out pins, bpp [extra1] 115 | .wrap 116 | public raw_1p_2cycle: 117 | public delay_g_0: 118 | out pins, bpp 119 | out pc, bpp 120 | -------------------------------------------------------------------------------- /src/common/pico_sd_card/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT TARGET pico_sd_card_headers) 2 | add_library(pico_sd_card_headers INTERFACE) 3 | target_include_directories(pico_sd_card_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 4 | endif() -------------------------------------------------------------------------------- /src/common/pico_sd_card/include/pico/sd_card.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _PICO_SD_CARD_H 8 | #define _PICO_SD_CARD_H 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #include "pico.h" 15 | 16 | #define SD_OK (0) 17 | #define SD_ERR_STUCK (-1) 18 | #define SD_ERR_BAD_RESPONSE (-2) 19 | #define SD_ERR_CRC (-3) 20 | #define SD_ERR_BAD_PARAM (-4) 21 | 22 | #ifndef PICO_SD_CLK_PIN 23 | #define PICO_SD_CLK_PIN 23 24 | #endif 25 | 26 | #ifndef PICO_SD_CMD_PIN 27 | #define PICO_SD_CMD_PIN 24 28 | #endif 29 | 30 | #ifndef PICO_SD_DAT0_PIN 31 | #define PICO_SD_DAT0_PIN 19 32 | #endif 33 | 34 | // todo for now 35 | #define PICO_SD_MAX_BLOCK_COUNT 32 36 | // todo buffer pool 37 | int sd_init_4pins(); 38 | int sd_init_1pin(); 39 | #define SD_SECTOR_SIZE 512 40 | int sd_readblocks_sync(uint32_t *buf, uint32_t block, uint block_count); 41 | int sd_readblocks_async(uint32_t *buf, uint32_t block, uint block_count); 42 | int sd_readblocks_scatter_async(uint32_t *control_words, uint32_t block, uint block_count); 43 | void sd_set_byteswap_on_read(bool swap); 44 | bool sd_scatter_read_complete(int *status); 45 | int sd_writeblocks_async(const uint32_t *data, uint32_t sector_num, uint sector_count); 46 | bool sd_write_complete(int *status); 47 | int sd_read_sectors_1bit_crc_async(uint32_t *sector_buf, uint32_t sector, uint sector_count); 48 | int sd_set_wide_bus(bool wide); 49 | int sd_set_clock_divider(uint div); 50 | 51 | #endif 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif -------------------------------------------------------------------------------- /src/common/pico_util_buffer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(pico_util_buffer INTERFACE) 2 | 3 | target_include_directories(pico_util_buffer INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 4 | 5 | target_sources(pico_util_buffer INTERFACE 6 | ${CMAKE_CURRENT_LIST_DIR}/buffer.c 7 | ) -------------------------------------------------------------------------------- /src/common/pico_util_buffer/buffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "pico/util/buffer.h" 8 | 9 | #ifdef PICO_BUFFER_USB_ALLOC_HACK 10 | #include 11 | 12 | uint8_t *usb_ram_alloc_ptr = (uint8_t *)(USBCTRL_DPRAM_BASE + USB_DPRAM_MAX); 13 | 14 | static void __attribute__((constructor)) _clear_usb_ram() { 15 | memset(usb_ram_alloc_ptr, 0, USB_DPRAM_SIZE - USB_DPRAM_MAX); 16 | } 17 | #endif 18 | -------------------------------------------------------------------------------- /src/common/pico_util_buffer/include/pico/util/buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _PICO_UTIL_BUFFER_H 8 | #define _PICO_UTIL_BUFFER_H 9 | 10 | #include "pico/types.h" 11 | 12 | /** \file buffer.h 13 | * \defgroup util_buffer buffer 14 | * \brief Buffer management 15 | * \ingroup pico_util 16 | */ 17 | 18 | #ifdef PICO_BUFFER_USB_ALLOC_HACK 19 | #include "hardware/address_mapped.h" 20 | #endif 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #ifdef DEBUG_MALLOC 27 | #include 28 | #endif 29 | 30 | #include 31 | 32 | /** \struct mem_buffer 33 | * \ingroup util_buffer 34 | * \brief Wrapper structure around a memory buffer 35 | * 36 | * Wrapper could be around static or allocated memory 37 | * 38 | * \todo This module needs to be checked - think there are issues with the free function 39 | */ 40 | typedef struct mem_buffer { 41 | size_t size; 42 | uint8_t *bytes; 43 | uint8_t flags; 44 | } mem_buffer_t; 45 | 46 | #ifdef PICO_BUFFER_USB_ALLOC_HACK 47 | #if !defined(USB_DPRAM_MAX) || (USB_DPRAM_MAX > 0) 48 | #include "hardware/structs/usb.h" 49 | #else 50 | #define USB_DPRAM_SIZE 4096 51 | #endif 52 | #endif 53 | 54 | inline static bool pico_buffer_alloc_in_place(mem_buffer_t *buffer, size_t size) { 55 | #ifdef PICO_BUFFER_USB_ALLOC_HACK 56 | extern uint8_t *usb_ram_alloc_ptr; 57 | if ((usb_ram_alloc_ptr + size) <= (uint8_t *)USBCTRL_DPRAM_BASE + USB_DPRAM_SIZE) { 58 | buffer->bytes = usb_ram_alloc_ptr; 59 | buffer->size = size; 60 | #ifdef DEBUG_MALLOC 61 | printf("balloc %d %p->%p\n", size, buffer->bytes, ((uint8_t *)buffer->bytes) + size); 62 | #endif 63 | usb_ram_alloc_ptr += size; 64 | return true; 65 | } 66 | #endif // inline for now 67 | buffer->bytes = (uint8_t *) calloc(1, size); 68 | if (buffer->bytes) { 69 | buffer->size = size; 70 | return true; 71 | } 72 | buffer->size = 0; 73 | return false; 74 | } 75 | 76 | inline static mem_buffer_t *pico_buffer_wrap(uint8_t *bytes, size_t size) { 77 | mem_buffer_t *buffer = (mem_buffer_t *) malloc(sizeof(mem_buffer_t)); 78 | if (buffer) { 79 | buffer->bytes = bytes; 80 | buffer->size = size; 81 | } 82 | return buffer; 83 | } 84 | 85 | inline static mem_buffer_t *pico_buffer_alloc(size_t size) { 86 | mem_buffer_t *b = (mem_buffer_t *) malloc(sizeof(mem_buffer_t)); 87 | if (b) { 88 | if (!pico_buffer_alloc_in_place(b, size)) { 89 | free(b); 90 | b = NULL; 91 | } 92 | } 93 | return b; 94 | } 95 | 96 | #ifdef __cplusplus 97 | } 98 | #endif 99 | #endif 100 | -------------------------------------------------------------------------------- /src/common/platypus/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(platypus INTERFACE) 2 | 3 | target_sources(platypus INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/platypus.c 5 | $<$:${CMAKE_CURRENT_LIST_DIR}/decompress_row.S> 6 | $<$:${CMAKE_CURRENT_LIST_DIR}/decompress_row_33.S> 7 | ) 8 | 9 | target_include_directories(platypus INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 10 | 11 | if (PICO_ON_DEVICE) 12 | target_link_libraries(platypus INTERFACE hardware_interp) 13 | endif() -------------------------------------------------------------------------------- /src/common/platypus/platypus.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "pico.h" 4 | #include "platypus.h" 5 | #include "pico/scanvideo.h" 6 | #if PICO_ON_DEVICE 7 | #include "hardware/interp.h" 8 | #endif 9 | 10 | #ifdef PLATYPUS_565 11 | #define PIXEL_RSHIFT 0 12 | #define PIXEL_GSHIFT 6 13 | #define PIXEL_BSHIFT 11 14 | #else 15 | #define PIXEL_RSHIFT 0 16 | #define PIXEL_GSHIFT 5 17 | #define PIXEL_BSHIFT 10 18 | #endif 19 | #if PICO_NO_HARDWARE 20 | 21 | const static uint32_t row_5_table[] = { 22 | 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00010000, 0x00010001, 0x00010000, 0x00000001, 23 | 0x00010001, 0x00000001, 0x00010000, 0x00000000, 0x00000000, 0x00010001, 0x00010000, 0x00010002, 24 | 0x00000000, 0x00010002, 0x00020000, 0x00010001, 0x00010000, 0x00020002, 0x00010000, 0x00010000, 25 | 0x00000001, 0x00000001, 0x00010001, 0x00000002, 0x00010001, 0x00000000, 0x00020001, 0x00000001, 26 | 0x00020000, 0x00020001, 0x00000001, 0x00000002, 0x00020002, 0x00000001, 0x00020001, 0x00000000, 27 | 0x00000000, 0x00000002, 0x00000001, 0x00010002, 0x00010000, 0x00020001, 0x00020000, 0x00010000, 28 | 0x00020000, 0x00020002, 0x00000000, 0x00020002, 0x00010002, 0x00000002, 0x00010000, 0x00020003, 29 | 0x00000000, 0x00020003, 0x00010000, 0x00030003, 0x00010000, 0x00000002, 0x00000001, 0x00010001, 30 | }; 31 | 32 | #ifndef PLATYPUS_565 33 | const static uint32_t row_222_table[] = { 34 | 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00010000, 0x00010001, 0x00010000, 0x00000001, 35 | 0x00000000, 0x00000020, 0x00000000, 0x00000021, 0x00010000, 0x00010021, 0x00010000, 0x00000021, 36 | 0x00200000, 0x00200020, 0x00200000, 0x00200021, 0x00210000, 0x00210021, 0x00210000, 0x00200021, 37 | 0x00200000, 0x00000020, 0x00200000, 0x00000021, 0x00210000, 0x00010021, 0x00210000, 0x00000021, 38 | 0x00000000, 0x00000400, 0x00000000, 0x00000401, 0x00010000, 0x00010401, 0x00010000, 0x00000401, 39 | 0x00000000, 0x00000420, 0x00000000, 0x00000421, 0x00010000, 0x00010421, 0x00010000, 0x00000421, 40 | 0x00200000, 0x00200420, 0x00200000, 0x00200421, 0x00210000, 0x00210421, 0x00210000, 0x00200421, 41 | 0x00200000, 0x00000420, 0x00200000, 0x00000421, 0x00210000, 0x00010421, 0x00210000, 0x00000421, 42 | 0x04000000, 0x04000400, 0x04000000, 0x04000401, 0x04010000, 0x04010401, 0x04010000, 0x04000401, 43 | 0x04000000, 0x04000420, 0x04000000, 0x04000421, 0x04010000, 0x04010421, 0x04010000, 0x04000421, 44 | 0x04200000, 0x04200420, 0x04200000, 0x04200421, 0x04210000, 0x04210421, 0x04210000, 0x04200421, 45 | 0x04200000, 0x04000420, 0x04200000, 0x04000421, 0x04210000, 0x04010421, 0x04210000, 0x04000421, 46 | 0x04000000, 0x00000400, 0x04000000, 0x00000401, 0x04010000, 0x00010401, 0x04010000, 0x00000401, 47 | 0x04000000, 0x00000420, 0x04000000, 0x00000421, 0x04010000, 0x00010421, 0x04010000, 0x00000421, 48 | 0x04200000, 0x00200420, 0x04200000, 0x00200421, 0x04210000, 0x00210421, 0x04210000, 0x00200421, 49 | 0x04200000, 0x00000420, 0x04200000, 0x00000421, 0x04210000, 0x00010421, 0x04210000, 0x00000421, 50 | }; 51 | #else 52 | const static uint32_t row_222_table[] = { 53 | 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00010000, 0x00010001, 0x00010000, 0x00000001, 54 | 0x00000000, 0x00000040, 0x00000000, 0x00000041, 0x00010000, 0x00010041, 0x00010000, 0x00000041, 55 | 0x00400000, 0x00400040, 0x00400000, 0x00400041, 0x00410000, 0x00410041, 0x00410000, 0x00400041, 56 | 0x00400000, 0x00000040, 0x00400000, 0x00000041, 0x00410000, 0x00010041, 0x00410000, 0x00000041, 57 | 0x00000000, 0x00000800, 0x00000000, 0x00000801, 0x00010000, 0x00010801, 0x00010000, 0x00000801, 58 | 0x00000000, 0x00000840, 0x00000000, 0x00000841, 0x00010000, 0x00010841, 0x00010000, 0x00000841, 59 | 0x00400000, 0x00400840, 0x00400000, 0x00400841, 0x00410000, 0x00410841, 0x00410000, 0x00400841, 60 | 0x00400000, 0x00000840, 0x00400000, 0x00000841, 0x00410000, 0x00010841, 0x00410000, 0x00000841, 61 | 0x08000000, 0x08000800, 0x08000000, 0x08000801, 0x08010000, 0x08010801, 0x08010000, 0x08000801, 62 | 0x08000000, 0x08000840, 0x08000000, 0x08000841, 0x08010000, 0x08010841, 0x08010000, 0x08000841, 63 | 0x08400000, 0x08400840, 0x08400000, 0x08400841, 0x08410000, 0x08410841, 0x08410000, 0x08400841, 64 | 0x08400000, 0x08000840, 0x08400000, 0x08000841, 0x08410000, 0x08010841, 0x08410000, 0x08000841, 65 | 0x08000000, 0x00000800, 0x08000000, 0x00000801, 0x08010000, 0x00010801, 0x08010000, 0x00000801, 66 | 0x08000000, 0x00000840, 0x08000000, 0x00000841, 0x08010000, 0x00010841, 0x08010000, 0x00000841, 67 | 0x08400000, 0x00400840, 0x08400000, 0x00400841, 0x08410000, 0x00410841, 0x08410000, 0x00400841, 68 | 0x08400000, 0x00000840, 0x08400000, 0x00000841, 0x08410000, 0x00010841, 0x08410000, 0x00000841, 69 | }; 70 | 71 | #endif 72 | 73 | const uint32_t* platypus_decompress_row(uint32_t *d0, uint32_t *d1, const uint32_t *s4, uint32_t w) { 74 | const uint8_t *s = (const uint8_t *)s4; 75 | for(uint x = 0; x < w; x += 2) 76 | { 77 | uint32_t row0, row1; 78 | #ifndef PLATYPUS_565 79 | if (s[1] & 0x80) { 80 | #else 81 | if (s[0] & 0x20) { 82 | #endif 83 | uint c0 = s[0] | (s[1] << 8); 84 | uint c1 = s[2] | (s[3] << 8); 85 | #ifndef PLATYPUS_565 86 | if (s[3] & 0x80) { 87 | #else 88 | if (s[2] & 0x20) { 89 | #endif 90 | // todo this is unused right now 91 | row0 = (c1 << 16) | c0; 92 | row1 = (s[7] << 24) | (s[6] << 16) | (s[5] << 8) | s[4]; 93 | s+=8; 94 | } else { 95 | row0 = (c1 << 16) | c0; 96 | row1 = (s[6] << 16) | (s[5] << 8) | s[4]; 97 | #ifndef PLATYPUS_565 98 | uint32_t mask = 0xff018401; // note ff at top saves us a mask of 0x00ffffff first for row1 99 | uint32_t hi_bits = row1 & mask; 100 | row1 ^= hi_bits; 101 | uint32_t lo_bits = row0 & (mask << 16u); 102 | row0 ^= lo_bits; 103 | hi_bits |= lo_bits >> 15u; 104 | row1 |= (hi_bits >> 10u) << 24u; 105 | row1 |= hi_bits << 27u; 106 | #else 107 | uint32_t mask = 0xff210821; 108 | uint32_t hi_bits = row1 & mask; 109 | row1 ^= hi_bits; 110 | uint32_t lo_bits = row0 & (mask << 16u); 111 | row0 ^= lo_bits; 112 | lo_bits >>= 13u; 113 | hi_bits |= lo_bits; 114 | hi_bits ^= (hi_bits >> 10u); 115 | hi_bits = (hi_bits * 2) + ((hi_bits >> 11u) & 1u); 116 | row1 |= hi_bits << 24u; 117 | #endif 118 | s+=7; 119 | // row0=row1=0; 120 | } 121 | } else { 122 | row0 = s[0] | (s[1] << 8); 123 | row1 = row0 = row0 | (row0 << 16); 124 | uint v = s[2]; 125 | #ifndef PLATYPUS_565 126 | if (v & 0x80u) { 127 | v = (v << 8u) | s[3]; 128 | #else 129 | if (v & 0x01u) { 130 | v = (v >> 1u) | (s[3] << 7u); 131 | #endif 132 | // do Rs 133 | #if PICO_ON_DEVICE 134 | v <<= 11u; 135 | interp1->accum[0] = v; 136 | uint32_t *p = (uint32_t *)(interp1->peek[0]); 137 | row0 += p[0]; 138 | row1 += p[1]; 139 | #else 140 | // *2 for interleave 141 | v <<= 1u; 142 | row0 += row_5_table[v&0x3e] << PIXEL_RSHIFT; 143 | row1 += row_5_table[(v&0x3e)+1] << PIXEL_RSHIFT; 144 | #endif 145 | // do Gs and Bs 146 | #if PICO_ON_DEVICE 147 | interp0->accum[0] = v; 148 | uint32_t *p0 = (uint32_t *)(interp0->peek[0]); 149 | uint32_t *p1 = (uint32_t *)(interp0->peek[1]); 150 | 151 | row0 += p0[0] << PIXEL_GSHIFT; 152 | row1 += p0[1] << PIXEL_GSHIFT; 153 | row0 += p1[0] << PIXEL_BSHIFT; 154 | row1 += p1[1] << PIXEL_BSHIFT; 155 | #else 156 | row0 += row_5_table[(v>>5u)&0x3eu] << PIXEL_GSHIFT; 157 | row1 += row_5_table[1 + ((v>>5u)&0x3eu)] << PIXEL_GSHIFT; 158 | row0 += row_5_table[(v>>10u)&0x3eu] << PIXEL_BSHIFT; 159 | row1 += row_5_table[1 + ((v>>10u)&0x3eu)] << PIXEL_BSHIFT; 160 | #endif 161 | s += 4; 162 | } else { 163 | #ifdef PLATYPUS_565 164 | v >>= 2; 165 | #endif 166 | #if PICO_ON_DEVICE 167 | interp1->accum[1] = v << 12u; 168 | uint32_t *p = (uint32_t *)(interp1->peek[1]); 169 | row0 += p[0]; 170 | row1 += p[1]; 171 | #else 172 | row0 += row_222_table[v*2]; 173 | row1 += row_222_table[v*2 + 1]; 174 | #endif 175 | s += 3; 176 | } 177 | } 178 | *d0++ = row0; 179 | *d1++ = row1; 180 | } 181 | s += (4u - ((uintptr_t)s) & 3u) & 3u; 182 | // round up to word boundary 183 | return (uint32_t *)s; 184 | } 185 | 186 | #else 187 | void platypus_decompress_configure_interp(bool is_b) { 188 | #if PLATYPUS_TABLES_MAIN_RAM 189 | extern uint32_t shared_222_table, shared_5_table; 190 | uint32_t row_5 = (uintptr_t)&shared_5_table; 191 | interp0->base[0] = row_5; 192 | interp0->base[1] = row_5; 193 | interp1->base[0] = row_5; 194 | interp1->base[1] = (uintptr_t)&shared_222_table; 195 | #else 196 | extern uint32_t platypus_decompress_row_asm_a_222_table, platypus_decompress_row_asm_a_5_table; 197 | extern uint32_t platypus_decompress_row_asm_b_222_table, platypus_decompress_row_asm_b_5_table; 198 | uint32_t row_5 = (uintptr_t)(is_b?&platypus_decompress_row_asm_b_5_table:&platypus_decompress_row_asm_a_5_table); 199 | interp0->base[0] = row_5; 200 | interp0->base[1] = row_5; 201 | interp1->base[0] = row_5; 202 | interp1->base[1] = (uintptr_t)(is_b?&platypus_decompress_row_asm_b_222_table:&platypus_decompress_row_asm_a_222_table); 203 | #endif 204 | #ifndef VIDEO_DBI 205 | const uint es_555 = 0; 206 | const uint es_222 = 0; 207 | #else 208 | const uint es_555 = 8; 209 | const uint es_222 = 9; 210 | #endif 211 | interp_config c = interp_default_config(); 212 | interp_config_set_shift(&c, 5 + es_555); 213 | interp_config_set_mask(&c, 3, 7); 214 | interp_set_config(interp0, 0, &c); 215 | 216 | interp_config_set_shift(&c, es_555); 217 | interp_set_config(interp1, 0, &c); 218 | 219 | interp_config_set_mask(&c, 3, 8); 220 | interp_config_set_shift(&c, es_222); 221 | interp_set_config(interp1, 1, &c); 222 | 223 | interp_config_set_mask(&c, 3, 7); 224 | interp_config_set_shift(&c, 10 + es_555); 225 | interp_config_set_cross_input(&c, true); 226 | interp_set_config(interp0, 1, &c); 227 | 228 | // interp_configure_none(interp0, 0, 5 + es_555, 3, 7); 229 | // interp_configure_with_cross_input(interp0, 1, 10 + es_555, 3, 7); 230 | // interp_configure_none(interp1, 0, es_555, 3, 7); 231 | // interp_configure_none(interp1, 1, es_222, 3, 8); 232 | 233 | // interp_add_force_bits(interp0, 0, 2); 234 | // interp_add_force_bits(interp0, 1, 2); 235 | // interp_add_force_bits(interp1, 0, 2); 236 | // interp_add_force_bits(interp1, 1, 2); 237 | } 238 | #endif -------------------------------------------------------------------------------- /src/common/platypus/platypus.h: -------------------------------------------------------------------------------- 1 | #ifndef _PLATYPUS_H 2 | #define _PLATYPUS_H 3 | 4 | #include 5 | 6 | #ifdef VIDEO_DBI 7 | #undef PLATYPUS_565 8 | #define PLATYPUS_565 9 | #endif 10 | 11 | #if PICO_ON_DEVICE 12 | extern const uint32_t* platypus_decompress_row_asm_a(uint32_t *d0, uint32_t *d1, const uint32_t *s, uint32_t w); 13 | extern const uint32_t* platypus_decompress_row_asm_b(uint32_t *d0, uint32_t *d1, const uint32_t *s, uint32_t w); 14 | extern void platypus_decompress_configure_interp(bool is_b); 15 | #define platypus_decompress_row_a platypus_decompress_row_asm_a 16 | #define platypus_decompress_row_b platypus_decompress_row_asm_b 17 | #else 18 | extern const uint32_t* platypus_decompress_row(uint32_t *d0, uint32_t *d1, const uint32_t *s, uint32_t w); 19 | #define platypus_decompress_row_a platypus_decompress_row 20 | #define platypus_decompress_row_b platypus_decompress_row 21 | #endif 22 | 23 | #endif 24 | 25 | -------------------------------------------------------------------------------- /src/rp2_common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This CMakeLists.txt intended to be included from other projectgs 2 | pico_add_subdirectory(hardware_rosc) 3 | pico_add_subdirectory(pico_sleep) 4 | pico_add_subdirectory(pico_audio_i2s) 5 | pico_add_subdirectory(pico_audio_pwm) 6 | pico_add_subdirectory(pico_audio_spdif) 7 | pico_add_subdirectory(pico_sd_card) 8 | # currently very old and non-compiling 9 | #pico_add_subdirectory(pico_scanvideo_dbi) 10 | pico_add_subdirectory(pico_scanvideo_dpi) 11 | pico_add_subdirectory(usb_common) 12 | pico_add_subdirectory(usb_device) 13 | pico_add_subdirectory(usb_device_msc) -------------------------------------------------------------------------------- /src/rp2_common/hardware_rosc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | pico_simple_hardware_target(rosc) -------------------------------------------------------------------------------- /src/rp2_common/hardware_rosc/include/hardware/rosc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _HARDWARE_ROSC_H_ 8 | #define _HARDWARE_ROSC_H_ 9 | 10 | #include "pico.h" 11 | #include "hardware/structs/rosc.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /** \file rosc.h 18 | * \defgroup hardware_rosc hardware_rosc 19 | * 20 | * Ring Oscillator (ROSC) API 21 | * 22 | * A Ring Oscillator is an on-chip oscillator that requires no external crystal. Instead, the output is generated from a series of 23 | * inverters that are chained together to create a feedback loop. RP2040 boots from the ring oscillator initially, meaning the 24 | * first stages of the bootrom, including booting from SPI flash, will be clocked by the ring oscillator. If your design has a 25 | * crystal oscillator, you’ll likely want to switch to this as your reference clock as soon as possible, because the frequency is 26 | * more accurate than the ring oscillator. 27 | */ 28 | 29 | /*! \brief Set frequency of the Ring Oscillator 30 | * \ingroup hardware_rosc 31 | * 32 | * \param code The drive strengths. See the RP2040 datasheet for information on this value. 33 | */ 34 | void rosc_set_freq(uint32_t code); 35 | 36 | /*! \brief Set range of the Ring Oscillator 37 | * \ingroup hardware_rosc 38 | * 39 | * Frequency range. Frequencies will vary with Process, Voltage & Temperature (PVT). 40 | * Clock output will not glitch when changing the range up one step at a time. 41 | * 42 | * \param range 0x01 Low, 0x02 Medium, 0x03 High, 0x04 Too High. 43 | */ 44 | void rosc_set_range(uint range); 45 | 46 | /*! \brief Disable the Ring Oscillator 47 | * \ingroup hardware_rosc 48 | * 49 | */ 50 | void rosc_disable(void); 51 | 52 | /*! \brief Put Ring Oscillator in to dormant mode. 53 | * \ingroup hardware_rosc 54 | * 55 | * The ROSC supports a dormant mode,which stops oscillation until woken up up by an asynchronous interrupt. 56 | * This can either come from the RTC, being clocked by an external clock, or a GPIO pin going high or low. 57 | * If no IRQ is configured before going into dormant mode the ROSC will never restart. 58 | * 59 | * PLLs should be stopped before selecting dormant mode. 60 | */ 61 | void rosc_set_dormant(void); 62 | 63 | // FIXME: Add doxygen 64 | 65 | uint32_t next_rosc_code(uint32_t code); 66 | 67 | uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz); 68 | 69 | void rosc_set_div(uint32_t div); 70 | 71 | inline static void rosc_clear_bad_write(void) { 72 | hw_clear_bits(&rosc_hw->status, ROSC_STATUS_BADWRITE_BITS); 73 | } 74 | 75 | inline static bool rosc_write_okay(void) { 76 | return !(rosc_hw->status & ROSC_STATUS_BADWRITE_BITS); 77 | } 78 | 79 | inline static void rosc_write(io_rw_32 *addr, uint32_t value) { 80 | rosc_clear_bad_write(); 81 | assert(rosc_write_okay()); 82 | *addr = value; 83 | assert(rosc_write_okay()); 84 | }; 85 | 86 | void rosc_enable(void); 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif 91 | 92 | #endif -------------------------------------------------------------------------------- /src/rp2_common/hardware_rosc/rosc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "pico.h" 8 | 9 | // For MHZ definitions etc 10 | #include "hardware/clocks.h" 11 | #include "hardware/rosc.h" 12 | 13 | // Given a ROSC delay stage code, return the next-numerically-higher code. 14 | // Top result bit is set when called on maximum ROSC code. 15 | uint32_t next_rosc_code(uint32_t code) { 16 | return ((code | 0x08888888u) + 1u) & 0xf7777777u; 17 | } 18 | 19 | uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz) { 20 | // TODO: This could be a lot better 21 | rosc_set_div(1); 22 | for (uint32_t code = 0; code <= 0x77777777u; code = next_rosc_code(code)) { 23 | rosc_set_freq(code); 24 | uint rosc_mhz = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC) / 1000; 25 | if ((rosc_mhz >= low_mhz) && (rosc_mhz <= high_mhz)) { 26 | return rosc_mhz; 27 | } 28 | } 29 | return 0; 30 | } 31 | 32 | void rosc_set_div(uint32_t div) { 33 | assert(div <= 31 && div >= 1); 34 | rosc_write(&rosc_hw->div, ROSC_DIV_VALUE_PASS + div); 35 | } 36 | 37 | void rosc_set_freq(uint32_t code) { 38 | rosc_write(&rosc_hw->freqa, (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | (code & 0xffffu)); 39 | rosc_write(&rosc_hw->freqb, (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | (code >> 16u)); 40 | } 41 | 42 | void rosc_set_range(uint range) { 43 | // Range should use enumvals from the headers and thus have the password correct 44 | rosc_write(&rosc_hw->ctrl, (ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB) | range); 45 | } 46 | 47 | void rosc_disable(void) { 48 | uint32_t tmp = rosc_hw->ctrl; 49 | tmp &= (~ROSC_CTRL_ENABLE_BITS); 50 | tmp |= (ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB); 51 | rosc_write(&rosc_hw->ctrl, tmp); 52 | // Wait for stable to go away 53 | while(rosc_hw->status & ROSC_STATUS_STABLE_BITS); 54 | } 55 | 56 | void rosc_set_dormant(void) { 57 | // WARNING: This stops the rosc until woken up by an irq 58 | rosc_write(&rosc_hw->dormant, ROSC_DORMANT_VALUE_DORMANT); 59 | // Wait for it to become stable once woken up 60 | while(!(rosc_hw->status & ROSC_STATUS_STABLE_BITS)); 61 | } 62 | 63 | void rosc_enable(void) { 64 | //Re-enable the rosc 65 | rosc_write(&rosc_hw->ctrl, ROSC_CTRL_ENABLE_BITS); 66 | 67 | //Wait for it to become stable once restarted 68 | while (!(rosc_hw->status & ROSC_STATUS_STABLE_BITS)); 69 | } 70 | -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_i2s/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT TARGET pico_audio_i2s) 2 | add_library(pico_audio_i2s INTERFACE) 3 | 4 | pico_generate_pio_header(pico_audio_i2s ${CMAKE_CURRENT_LIST_DIR}/audio_i2s.pio) 5 | 6 | target_sources(pico_audio_i2s INTERFACE 7 | ${CMAKE_CURRENT_LIST_DIR}/audio_i2s.c 8 | ) 9 | 10 | target_include_directories(pico_audio_i2s INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 11 | target_link_libraries(pico_audio_i2s INTERFACE hardware_dma hardware_pio hardware_irq pico_audio) 12 | endif() -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_i2s/audio_i2s.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | ; Transmit a mono or stereo I2S audio stream as stereo 8 | ; This is 16 bits per sample; can be altered by modifying the "set" params, 9 | ; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register. 10 | ; 11 | ; Autopull must be enabled, with threshold set to 32. 12 | ; Since I2S is MSB-first, shift direction should be to left. 13 | ; Hence the format of the FIFO word is: 14 | ; 15 | ; | 31 : 16 | 15 : 0 | 16 | ; | sample ws=0 | sample ws=1 | 17 | ; 18 | ; Data is output at 1 bit per clock. Use clock divider to adjust frequency. 19 | ; Fractional divider will probably be needed to get correct bit clock period, 20 | ; but for common syslck freqs this should still give a constant word select period. 21 | ; 22 | ; One output pin is used for the data output. 23 | ; Two side-set pins are used. Two versions of the program are provided, so that 24 | ; the clock and word select pins can be in either order. 25 | 26 | ; Send 16 bit words to the PIO for mono, 32 bit words for stereo 27 | 28 | .program audio_i2s 29 | .side_set 2 30 | 31 | ; /--- LRCLK 32 | ; |/-- BCLK 33 | bitloop1: ; || 34 | out pins, 1 side 0b10 35 | jmp x-- bitloop1 side 0b11 36 | out pins, 1 side 0b00 37 | set x, 14 side 0b01 38 | 39 | bitloop0: 40 | out pins, 1 side 0b00 41 | jmp x-- bitloop0 side 0b01 42 | out pins, 1 side 0b10 43 | public entry_point: 44 | set x, 14 side 0b11 45 | 46 | .program audio_i2s_swapped 47 | .side_set 2 48 | 49 | ; /--- BCLK 50 | ; |/-- LRCLK 51 | bitloop1: ; || 52 | out pins, 1 side 0b01 53 | jmp x-- bitloop1 side 0b11 54 | out pins, 1 side 0b00 55 | set x, 14 side 0b10 56 | 57 | bitloop0: 58 | out pins, 1 side 0b00 59 | jmp x-- bitloop0 side 0b10 60 | out pins, 1 side 0b01 61 | public entry_point: 62 | set x, 14 side 0b11 63 | 64 | % c-sdk { 65 | 66 | static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) { 67 | pio_sm_config sm_config = audio_i2s_program_get_default_config(offset); 68 | 69 | sm_config_set_out_pins(&sm_config, data_pin, 1); 70 | sm_config_set_sideset_pins(&sm_config, clock_pin_base); 71 | sm_config_set_out_shift(&sm_config, false, true, 32); 72 | sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX); 73 | 74 | pio_sm_init(pio, sm, offset, &sm_config); 75 | 76 | #if PICO_PIO_USE_GPIO_BASE 77 | uint64_t pin_mask = (1ull << data_pin) | (3ull << clock_pin_base); 78 | pio_sm_set_pindirs_with_mask64(pio, sm, pin_mask, pin_mask); 79 | #else 80 | uint32_t pin_mask = (1u << data_pin) | (3u << clock_pin_base); 81 | pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask); 82 | #endif 83 | pio_sm_set_pins(pio, sm, 0); // clear pins 84 | 85 | pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point)); 86 | } 87 | 88 | %} 89 | -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_i2s/include/pico/audio_i2s.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _PICO_AUDIO_I2S_H 8 | #define _PICO_AUDIO_I2S_H 9 | 10 | #include "pico/audio.h" 11 | 12 | /** \file audio_i2s.h 13 | * \defgroup pico_audio_i2s pico_audio_i2s 14 | * I2S audio output using the PIO 15 | * 16 | * This library uses the \ref hardware_pio system to implement a I2S audio interface 17 | * 18 | * \todo Must be more we need to say here. 19 | * \todo certainly need an example 20 | * 21 | */ 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | #ifndef PICO_AUDIO_I2S_DMA_IRQ 28 | #ifdef PICO_AUDIO_DMA_IRQ 29 | #define PICO_AUDIO_I2S_DMA_IRQ PICO_AUDIO_DMA_IRQ 30 | #else 31 | #define PICO_AUDIO_I2S_DMA_IRQ 0 32 | #endif 33 | #endif 34 | 35 | #ifndef PICO_AUDIO_I2S_PIO 36 | #ifdef PICO_AUDIO_PIO 37 | #define PICO_AUDIO_I2S_PIO PICO_AUDIO_PIO 38 | #else 39 | #define PICO_AUDIO_I2S_PIO 0 40 | #endif 41 | #endif 42 | 43 | #if !(PICO_AUDIO_I2S_DMA_IRQ == 0 || PICO_AUDIO_I2S_DMA_IRQ == 1) 44 | #error PICO_AUDIO_I2S_DMA_IRQ must be 0 or 1 45 | #endif 46 | 47 | #if !(PICO_AUDIO_I2S_PIO == 0 || PICO_AUDIO_I2S_PIO == 1) 48 | #error PICO_AUDIO_I2S_PIO ust be 0 or 1 49 | #endif 50 | 51 | #ifndef PICO_AUDIO_I2S_MAX_CHANNELS 52 | #ifdef PICO_AUDIO_MAX_CHANNELS 53 | #define PICO_AUDIO_I2S_MAX_CHANNELS PICO_AUDIO_MAX_CHANNELS 54 | #else 55 | #define PICO_AUDIO_I2S_MAX_CHANNELS 2u 56 | #endif 57 | #endif 58 | 59 | #ifndef PICO_AUDIO_I2S_BUFFERS_PER_CHANNEL 60 | #ifdef PICO_AUDIO_BUFFERS_PER_CHANNEL 61 | #define PICO_AUDIO_I2S_BUFFERS_PER_CHANNEL PICO_AUDIO_BUFFERS_PER_CHANNEL 62 | #else 63 | #define PICO_AUDIO_I2S_BUFFERS_PER_CHANNEL 3u 64 | #endif 65 | #endif 66 | 67 | #ifndef PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH 68 | #ifdef PICO_AUDIO_BUFFER_SAMPLE_LENGTH 69 | #define PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH PICO_AUDIO_BUFFER_SAMPLE_LENGTH 70 | #else 71 | #define PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH 576u 72 | #endif 73 | #endif 74 | 75 | #ifndef PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH 76 | #ifdef PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH 77 | #define PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH PICO_AUDIO_SILENCE_BUFFER_SAMPLE_LENGTH 78 | #else 79 | #define PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH 256u 80 | #endif 81 | #endif 82 | 83 | // Allow use of pico_audio driver without actually doing anything much 84 | #ifndef PICO_AUDIO_I2S_NOOP 85 | #ifdef PICO_AUDIO_NOOP 86 | #define PICO_AUDIO_I2S_NOOP PICO_AUDIO_NOOP 87 | #else 88 | #define PICO_AUDIO_I2S_NOOP 0 89 | #endif 90 | #endif 91 | 92 | #ifndef PICO_AUDIO_I2S_MONO_INPUT 93 | #define PICO_AUDIO_I2S_MONO_INPUT 0 94 | #endif 95 | #ifndef PICO_AUDIO_I2S_MONO_OUTPUT 96 | #define PICO_AUDIO_I2S_MONO_OUTPUT 0 97 | #endif 98 | 99 | #ifndef PICO_AUDIO_I2S_DATA_PIN 100 | //#warning PICO_AUDIO_I2S_DATA_PIN should be defined when using AUDIO_I2S 101 | #define PICO_AUDIO_I2S_DATA_PIN 28 102 | #endif 103 | 104 | #ifndef PICO_AUDIO_I2S_CLOCK_PIN_BASE 105 | //#warning PICO_AUDIO_I2S_CLOCK_PIN_BASE should be defined when using AUDIO_I2S 106 | #define PICO_AUDIO_I2S_CLOCK_PIN_BASE 26 107 | #endif 108 | 109 | // The default order is CLOCK_PIN_BASE=LRCLK, CLOCK_PIN_BASE+1=BCLK 110 | // The swapped order is CLOCK_PIN_BASE=BCLK, CLOCK_PIN_BASE+1=LRCLK 111 | #ifndef PICO_AUDIO_I2S_CLOCK_PINS_SWAPPED 112 | #define PICO_AUDIO_I2S_CLOCK_PINS_SWAPPED 0 113 | #endif 114 | 115 | // todo this needs to come from a build config 116 | /** \brief Base configuration structure used when setting up 117 | * \ingroup pico_audio_i2s 118 | */ 119 | typedef struct audio_i2s_config { 120 | uint8_t data_pin; 121 | uint8_t clock_pin_base; 122 | uint8_t dma_channel; 123 | uint8_t pio_sm; 124 | } audio_i2s_config_t; 125 | 126 | /** \brief Set up system to output I2S audio 127 | * \ingroup pico_audio_i2s 128 | * 129 | * \param intended_audio_format \todo 130 | * \param config The configuration to apply. 131 | */ 132 | const audio_format_t *audio_i2s_setup(const audio_format_t *intended_audio_format, 133 | const audio_i2s_config_t *config); 134 | 135 | 136 | /** \brief \todo 137 | * \ingroup pico_audio_i2s 138 | * 139 | * \param producer 140 | * \param connection 141 | */ 142 | bool audio_i2s_connect_thru(audio_buffer_pool_t *producer, audio_connection_t *connection); 143 | 144 | 145 | /** \brief \todo 146 | * \ingroup pico_audio_i2s 147 | * 148 | * \param producer 149 | * 150 | * todo make a common version (or a macro) .. we don't want to pull in unnecessary code by default 151 | */ 152 | bool audio_i2s_connect(audio_buffer_pool_t *producer); 153 | 154 | 155 | /** \brief \todo 156 | * \ingroup pico_audio_i2s 157 | * 158 | * \param producer 159 | */ 160 | bool audio_i2s_connect_s8(audio_buffer_pool_t *producer); 161 | 162 | /** \brief \todo 163 | * \ingroup pico_audio_i2s 164 | * 165 | * \param producer 166 | * \param buffer_on_give 167 | * \param buffer_count 168 | * \param samples_per_buffer 169 | * \param connection 170 | * \return 171 | */ 172 | bool audio_i2s_connect_extra(audio_buffer_pool_t *producer, bool buffer_on_give, uint buffer_count, 173 | uint samples_per_buffer, audio_connection_t *connection); 174 | 175 | 176 | /** \brief Set up system to output I2S audio 177 | * \ingroup pico_audio_i2s 178 | * 179 | * \param enable true to enable I2S audio, false to disable. 180 | */ 181 | void audio_i2s_set_enabled(bool enabled); 182 | 183 | #ifdef __cplusplus 184 | } 185 | #endif 186 | 187 | #endif //_AUDIO_I2S_H 188 | -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_pwm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT TARGET pico_audio_pwm) 2 | add_library(pico_audio_pwm INTERFACE) 3 | 4 | pico_generate_pio_header(pico_audio_pwm ${CMAKE_CURRENT_LIST_DIR}/audio_pwm.pio) 5 | 6 | target_sources(pico_audio_pwm INTERFACE 7 | ${CMAKE_CURRENT_LIST_DIR}/audio_pwm.c 8 | ${CMAKE_CURRENT_LIST_DIR}/sample_encoding.cpp 9 | ) 10 | 11 | target_include_directories(pico_audio_pwm INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 12 | target_link_libraries(pico_audio_pwm INTERFACE 13 | hardware_dma 14 | hardware_pio 15 | hardware_irq 16 | hardware_interp 17 | pico_audio 18 | pico_multicore) 19 | endif() -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_pwm/audio_pwm.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | .program pwm_one_bit_dither 8 | .side_set 1 opt 9 | ; Format: 10 | ; | high len | low len | (dither) * n | 11 | ; OSR level 12 | ; cycle length = 7 + 2 + 127 13 | 14 | ; 136 clocks/cycle frequency 352941 / 16 = 22058 15 | delay: 16 | nop [2] 17 | .wrap_target 18 | out pins, 1 19 | loops: 20 | mov x, isr side 1 21 | loop1: 22 | jmp x-- loop1 23 | mov x, y side 0 24 | loop0: 25 | jmp x-- loop0 26 | jmp !osre delay 27 | public entry_point: 28 | pull 29 | out isr, 7 30 | out y, 7 31 | .wrap 32 | 33 | .program pwm_two_bit_dither 34 | .side_set 1 opt 35 | ; Format: 36 | ; | high len | low len | (dither) * n | 37 | ; OSR level 38 | 39 | ; this 138 clocks/cycle frequency 347826 / 16 = 21739Hz 40 | delay: 41 | nop [2] 42 | .wrap_target 43 | out pins, 1 44 | out pins, 1 45 | out pins, 1 46 | loops: 47 | mov x, isr side 1 48 | loop1: 49 | jmp x-- loop1 50 | mov x, y side 0 51 | loop0: 52 | jmp x-- loop0 53 | jmp !osre delay 54 | entry_point: 55 | pull 56 | out isr, 7 57 | out y, 7 58 | .wrap 59 | -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_pwm/include/pico/audio_pwm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | #ifndef _PICO_AUDIO_PWM_H 7 | #define _PICO_AUDIO_PWM_H 8 | 9 | #include "pico/audio.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | // ====================== 16 | // == CONFIG ============ 17 | 18 | #ifndef PICO_AUDIO_PWM_DMA_IRQ 19 | #ifdef PICO_AUDIO_IRQ 20 | #define PICO_AUDIO_PWM_DMA_IRQ PICO_AUDIO_DMA_IRQ 21 | #else 22 | #define PICO_AUDIO_PWM_DMA_IRQ 1 23 | #endif 24 | #endif 25 | 26 | #ifndef PICO_AUDIO_PWM_PIO 27 | #ifdef PICO_AUDIO_PIO 28 | #define PICO_AUDIO_PWM_PIO PICO_AUDIO_PIO 29 | #else 30 | #define PICO_AUDIO_PWM_PIO 0 31 | #endif 32 | #endif 33 | 34 | #if !(PICO_AUDIO_PWM_DMA_IRQ == 0 || PICO_AUDIO_PWM_DMA_IRQ == 1) 35 | #error PICO_AUDIO_PWM_DMA_IRQ must be 0 or 1 36 | #endif 37 | 38 | #if !(PICO_AUDIO_PWM_PIO == 0 || PICO_AUDIO_PWM_PIO == 1) 39 | #error PICO_AUDIO_PWM_PIO ust be 0 or 1 40 | #endif 41 | 42 | #ifndef PICO_AUDIO_PWM_MAX_CHANNELS 43 | #ifdef PICO_AUDIO_MAX_CHANNELS 44 | #define PICO_AUDIO_PWM_MAX_CHANNELS PICO_AUDIO_MAX_CHANNELS 45 | #else 46 | #define PICO_AUDIO_PWM_MAX_CHANNELS 2u 47 | #endif 48 | #endif 49 | 50 | #ifndef PICO_AUDIO_PWM_BUFFERS_PER_CHANNEL 51 | #ifdef PICO_AUDIO_BUFFERS_PER_CHANNEL 52 | #define PICO_AUDIO_PWM_BUFFERS_PER_CHANNEL PICO_AUDIO_BUFFERS_PER_CHANNEL 53 | #else 54 | #define PICO_AUDIO_PWM_BUFFERS_PER_CHANNEL 3u 55 | #endif 56 | #endif 57 | 58 | #ifndef PICO_AUDIO_PWM_BUFFER_SAMPLE_LENGTH 59 | #ifdef PICO_AUDIO_BUFFER_SAMPLE_LENGTH 60 | #define PICO_AUDIO_PWM_BUFFER_SAMPLE_LENGTH PICO_AUDIO_BUFFER_SAMPLE_LENGTH 61 | #else 62 | #define PICO_AUDIO_PWM_BUFFER_SAMPLE_LENGTH 576u 63 | #endif 64 | #endif 65 | 66 | #ifndef PICO_AUDIO_PWM_SILENCE_BUFFER_SAMPLE_LENGTH 67 | #ifdef PICO_AUDIO_PWM_SILENCE_BUFFER_SAMPLE_LENGTH 68 | #define PICO_AUDIO_PWM_SILENCE_BUFFER_SAMPLE_LENGTH PICO_AUDIO_SILENCE_BUFFER_SAMPLE_LENGTH 69 | #else 70 | #define PICO_AUDIO_PWM_SILENCE_BUFFER_SAMPLE_LENGTH 256u 71 | #endif 72 | #endif 73 | 74 | // Enable noise shaping when super-sampling 75 | // 76 | // This allows for runtime selection of noise shaping or not, however having the compile 77 | // time definition requires triple the pico_audio buffer RAM usage at runtime, and leads to marginally 78 | // slower code in general. 79 | #ifndef PICO_AUDIO_PWM_ENABLE_NOISE_SHAPING 80 | #define PICO_AUDIO_PWM_ENABLE_NOISE_SHAPING 0 81 | #endif 82 | 83 | #ifndef PICO_AUDIO_PWM_L_PIN 84 | #define PICO_AUDIO_PWM_L_PIN 0 85 | #endif 86 | 87 | #ifndef PICO_AUDIO_PWM_R_PIN 88 | #define PICO_AUDIO_PWM_R_PIN 1 89 | #endif 90 | 91 | #ifndef PICO_AUDIO_PWM_MONO_PIN 92 | #define PICO_AUDIO_PWM_MONO_PIN PICO_AUDIO_PWM_L_PIN 93 | #endif 94 | 95 | #ifndef PIO_AUDIO_PWM_INTERP_SAVE 96 | #define PIO_AUDIO_PWM_INTERP_SAVE 1 97 | #endif 98 | 99 | // Allow use of pico_audio driver without actually doing anything much 100 | #ifndef PICO_AUDIO_PWM_NOOP 101 | #ifdef PICO_AUDIO_NOOP 102 | #define PICO_AUDIO_PWM_NOOP PICO_AUDIO_NOOP 103 | #else 104 | #define PICO_AUDIO_PWM_NOOP 0 105 | #endif 106 | #endif 107 | 108 | /** \file audio_pwm.h 109 | * \defgroup pico_audio_pwm pico_audio_pwm 110 | * PWM audio output (with optional noise shaping and error diffusion) using the PIO 111 | * 112 | * This library uses the \ref hardware_pio system to implement a PWM audio interface 113 | * 114 | * \todo Must be more we need to say here. 115 | * \todo certainly need an example 116 | * 117 | */ 118 | 119 | // todo we need a place to register these or just allow them to overlap, or base them on a FOURCC - this is just made up 120 | #define AUDIO_BUFFER_FORMAT_PIO_PWM_FIRST 1000 121 | #define AUDIO_BUFFER_FORMAT_PIO_PWM_CMD1 (AUDIO_BUFFER_FORMAT_PIO_PWM_FIRST) 122 | #define AUDIO_BUFFER_FORMAT_PIO_PWM_CMD3 (AUDIO_BUFFER_FORMAT_PIO_PWM_FIRST+1) 123 | 124 | typedef struct __packed audio_pwm_channel_config { 125 | pio_audio_channel_config_t core; 126 | uint8_t pattern; 127 | } audio_pwm_channel_config_t; 128 | 129 | // can copy this to modify just the pin 130 | extern const audio_pwm_channel_config_t default_left_channel_config; 131 | extern const audio_pwm_channel_config_t default_right_channel_config; 132 | extern const audio_pwm_channel_config_t default_mono_channel_config; 133 | 134 | /*! \brief 135 | * \ingroup pico_audio_pwm 136 | * \todo 137 | * 138 | * max_latency_ms may be -1 (for don't care) 139 | * \param intended_audio_format 140 | * \param max_latency_ms 141 | * \param channel_config0 142 | * \param ... 143 | * \return 144 | */ 145 | extern const audio_format_t * 146 | audio_pwm_setup(const audio_format_t *intended_audio_format, int32_t max_latency_ms, 147 | const audio_pwm_channel_config_t *channel_config0, ...); 148 | 149 | /*! \brief 150 | * \ingroup pico_audio_pwm 151 | * \todo 152 | * 153 | * \param producer_pool 154 | * \param dedicate_core_1 155 | * attempt a default mapping of producer buffers to pio pwm pico_audio output 156 | * dedicate_core_1 to have core 1 set aside entirely to do work offloading as much stuff from the producer side as possible 157 | * todo also allow IRQ handler to do it I guess 158 | */ 159 | extern bool audio_pwm_default_connect(audio_buffer_pool_t *producer_pool, bool dedicate_core_1); 160 | 161 | /*! \brief 162 | * \ingroup pico_audio_pwm 163 | * \todo 164 | * 165 | * \param enable true to enable the PWM audio, false to disable 166 | */ 167 | extern void audio_pwm_set_enabled(bool enabled); 168 | 169 | /*! \brief Set the PWM correction mode 170 | * \ingroup pico_audio_pwm 171 | * 172 | * \param mode \todo 173 | */ 174 | extern bool audio_pwm_set_correction_mode(enum audio_correction_mode mode); 175 | 176 | /*! \brief Get the PWM correction mode 177 | * \ingroup pico_audio_pwm 178 | * 179 | * \return mode 180 | */ 181 | extern enum audio_correction_mode audio_pwm_get_correction_mode(); 182 | 183 | #ifdef __cplusplus 184 | } 185 | #endif 186 | 187 | #endif //_PIO_AUDIO_PWM_H 188 | -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_pwm/include/pico/audio_pwm/sample_encoding.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _PICO_AUDIO_PWM_SAMPLE_ENCODING_H 8 | #define _PICO_AUDIO_PWM_SAMPLE_ENCODING_H 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | // todo some if not all of this can go in sample_encoding.cpp 14 | 15 | #define FRACTIONAL_BITS 9u 16 | #define QUANTIZED_BITS 7u 17 | 18 | static_assert(FRACTIONAL_BITS + QUANTIZED_BITS == 16, ""); 19 | 20 | #ifndef ENABLE_NOISE_SHAPING 21 | const uint32_t audio_carrier_freq = 350364; 22 | #define program_name pwm_one_bit_dither 23 | #define NATIVE_BUFFER_FORMAT AUDIO_BUFFER_FORMAT_PIO_PWM_CMD1 24 | #else 25 | #define program_name pwm_two_bit_dither 26 | #define NATIVE_BUFFER_FORMAT AUDIO_BUFFER_FORMAT_PIO_PWM_CMD3 27 | #endif 28 | 29 | static_assert(QUANTIZED_BITS == 7, ""); // required by make_cmd below 30 | #define MAKE_CMD(q) (((q)) | (127u - (q)) << 7u) 31 | #define CMD_BITS (QUANTIZED_BITS * 2) 32 | #define SILENCE_LEVEL 0x40u 33 | #define SILENCE_CMD MAKE_CMD(SILENCE_LEVEL) 34 | 35 | #ifdef ENABLE_NOISE_SHAPING 36 | #define DITHER_BITS 3u 37 | // this needs to be divisible by dither bits 38 | #define CYCLES_PER_SAMPLE 15 39 | typedef struct { 40 | uint32_t a; 41 | uint32_t b; 42 | uint32_t c; 43 | #ifdef AUDIO_HALF_FREQ 44 | uint32_t d, e, f; 45 | #endif 46 | } pwm_cmd_t; // what we send to PIO for each sample 47 | const pwm_cmd_t silence_cmd = {SILENCE_CMD, SILENCE_CMD, SILENCE_CMD, 48 | #ifdef AUDIO_HALF_FREQ 49 | SILENCE_CMD, SILENCE_CMD, SILENCE_CMD, 50 | #endif 51 | }; 52 | #else 53 | #define CYCLES_PER_SAMPLE 16 54 | #ifndef AUDIO_HALF_FREQ 55 | typedef uint32_t pwm_cmd_t; // what we send to PIO for each sample 56 | const pwm_cmd_t silence_cmd = SILENCE_CMD; 57 | #else 58 | typedef struct { 59 | uint32_t a; 60 | uint32_t b; 61 | } pwm_cmd_t; // what we send to PIO for each sample 62 | const pwm_cmd_t silence_cmd = { SILENCE_CMD, SILENCE_CMD }; 63 | #endif 64 | #define DITHER_BITS 1u 65 | #endif 66 | 67 | static_assert(CYCLES_PER_SAMPLE % DITHER_BITS == 0, ""); 68 | #define CYCLES_PER_WORD (CYCLES_PER_SAMPLE / DITHER_BITS) 69 | #ifndef AUDIO_HALF_FREQ 70 | #define OUTER_LOOP_COUNT DITHER_BITS 71 | #else 72 | #define OUTER_LOOP_COUNT DITHER_BITS*2 73 | #endif 74 | #define FRACTIONAL_LSB 0u 75 | #define FRACTIONAL_MSB (FRACTIONAL_LSB + FRACTIONAL_BITS - 1u) 76 | #define FRACTIONAL_MASK ((1u << FRACTIONAL_BITS) - 1u) 77 | #define QUANTIZED_LSB FRACTION_BITS 78 | #define QUANTIZED_MSB (QUANTIZED_LSB + QUANTIZED_BITS - 1u) 79 | #define QUANTIZED_MAX ((1u << QUANTIZED_BITS) - 1u) 80 | #define QUANTIZED_MASK QUANTIZED_MAX 81 | 82 | 83 | void producer_pool_blocking_give_to_pwm_s16(audio_connection_t *connection, audio_buffer_t *buffer); 84 | void producer_pool_blocking_give_to_pwm_s8(audio_connection_t *connection, audio_buffer_t *buffer); 85 | void producer_pool_blocking_give_to_pwm_u16(audio_connection_t *connection, audio_buffer_t *buffer); 86 | void producer_pool_blocking_give_to_pwm_u8(audio_connection_t *connection, audio_buffer_t *buffer); 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif 91 | 92 | #endif //SOFTWARE_SAMPLE_ENCODING_H 93 | -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_spdif/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT TARGET pico_audio_spdif) 2 | add_library(pico_audio_spdif INTERFACE) 3 | 4 | pico_generate_pio_header(pico_audio_spdif ${CMAKE_CURRENT_LIST_DIR}/audio_spdif.pio) 5 | 6 | target_sources(pico_audio_spdif INTERFACE 7 | ${CMAKE_CURRENT_LIST_DIR}/audio_spdif.c 8 | ${CMAKE_CURRENT_LIST_DIR}/sample_encoding.cpp 9 | ) 10 | 11 | target_include_directories(pico_audio_spdif INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 12 | target_link_libraries(pico_audio_spdif INTERFACE hardware_dma hardware_pio hardware_irq pico_audio) 13 | endif() -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_spdif/audio_spdif.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | // Strictly this is NRZI decoder 8 | .program audio_spdif 9 | .side_set 1 10 | public output_low: 11 | out x, 1 side 0 12 | jmp !x, output_low side 0 13 | output_high: 14 | out x, 1 side 1 15 | jmp !x, output_high side 1 16 | 17 | % c-sdk { 18 | void spdif_program_init(PIO pio, uint sm, uint offset, uint pin) { 19 | pio_sm_config sm_config = audio_spdif_program_get_default_config(offset); 20 | sm_config_set_out_shift(&sm_config, true, true, 32); 21 | sm_config_set_sideset(&sm_config, 1, false, false); 22 | sm_config_set_sideset_pins(&sm_config, pin); 23 | pio_sm_init(pio, sm, offset, &sm_config); 24 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); 25 | pio_sm_set_pins(pio, sm, 0); 26 | pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_spdif_offset_output_low)); 27 | } 28 | %} -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_spdif/include/pico/audio_spdif.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _PICO_AUDIO_SPDIF_H 8 | #define _PICO_AUDIO_SPDIF_H 9 | 10 | #include "pico/audio.h" 11 | 12 | /** \file audio_spdif.h 13 | * \defgroup pico_audio_spdif pico_audio_spdif 14 | * S/PDIF audio output using the PIO 15 | * 16 | * This library uses the \ref pio system to implement a S/PDIF audio interface 17 | * 18 | * \todo Must be more we need to say here. 19 | * \todo certainly need an example 20 | * 21 | */ 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | #ifndef PICO_AUDIO_SPDIF_DMA_IRQ 28 | #ifdef PICO_AUDIO_DMA_IRQ 29 | #define PICO_AUDIO_SPDIF_DMA_IRQ PICO_AUDIO_DMA_IRQ 30 | #else 31 | #define PICO_AUDIO_SPDIF_DMA_IRQ 0 32 | #endif 33 | #endif 34 | 35 | #ifndef PICO_AUDIO_SPDIF_PIO 36 | #ifdef PICO_AUDIO_PIO 37 | #define PICO_AUDIO_SPDIF_PIO PICO_AUDIO_PIO 38 | #else 39 | #define PICO_AUDIO_SPDIF_PIO 0 40 | #endif 41 | #endif 42 | 43 | #if !(PICO_AUDIO_SPDIF_DMA_IRQ == 0 || PICO_AUDIO_SPDIF_DMA_IRQ == 1) 44 | #error PICO_AUDIO_SPDIF_DMA_IRQ must be 0 or 1 45 | #endif 46 | 47 | #if !(PICO_AUDIO_SPDIF_PIO == 0 || PICO_AUDIO_SPDIF_PIO == 1) 48 | #error PICO_AUDIO_SPDIF_PIO ust be 0 or 1 49 | #endif 50 | 51 | #ifndef PICO_AUDIO_SPDIF_MAX_CHANNELS 52 | #ifdef PICO_AUDIO_MAX_CHANNELS 53 | #define PICO_AUDIO_SPDIF_MAX_CHANNELS PICO_AUDIO_MAX_CHANNELS 54 | #else 55 | #define PICO_AUDIO_SPDIF_MAX_CHANNELS 2u 56 | #endif 57 | #endif 58 | 59 | #ifndef PICO_AUDIO_SPDIF_BUFFERS_PER_CHANNEL 60 | #ifdef PICO_AUDIO_BUFFERS_PER_CHANNEL 61 | #define PICO_AUDIO_SPDIF_BUFFERS_PER_CHANNEL PICO_AUDIO_BUFFERS_PER_CHANNEL 62 | #else 63 | #define PICO_AUDIO_SPDIF_BUFFERS_PER_CHANNEL 3u 64 | #endif 65 | #endif 66 | 67 | // fixed by S/PDIF 68 | #define PICO_AUDIO_SPDIF_BLOCK_SAMPLE_COUNT 192u 69 | 70 | // Allow use of pico_audio driver without actually doing anything much 71 | #ifndef PICO_AUDIO_SPDIF_NOOP 72 | #ifdef PICO_AUDIO_NOOP 73 | #define PICO_AUDIO_SPDIF_NOOP PICO_AUDIO_NOOP 74 | #else 75 | #define PICO_AUDIO_SPDIF_NOOP 0 76 | #endif 77 | #endif 78 | 79 | #ifndef PICO_AUDIO_SPDIF_MONO_INPUT 80 | #define PICO_AUDIO_SPDIF_MONO_INPUT 0 81 | #endif 82 | 83 | #ifndef PICO_AUDIO_SPDIF_PIN 84 | //#warning PICO_AUDIO_SPDIF_PIN should be defined when using AUDIO_SPDIF 85 | #define PICO_AUDIO_SPDIF_PIN 0 86 | #endif 87 | 88 | #define AUDIO_BUFFER_FORMAT_PIO_SPDIF 1300 89 | 90 | // todo this needs to come from a build config 91 | /** \brief Base configuration structure used when setting up 92 | * \ingroup audio_spdif 93 | */ 94 | typedef struct audio_spdif_config { 95 | uint8_t pin; 96 | uint8_t dma_channel; 97 | uint8_t pio_sm; 98 | } audio_spdif_config_t; 99 | 100 | extern const audio_spdif_config_t audio_spdif_default_config; 101 | 102 | /** \brief Set up system to output S/PDIF audio 103 | * \ingroup audio_spdif 104 | * 105 | * \param intended_audio_format \todo 106 | * \param config The configuration to apply. 107 | */ 108 | const audio_format_t *audio_spdif_setup(const audio_format_t *intended_audio_format, 109 | const audio_spdif_config_t *config); 110 | 111 | 112 | /** \brief \todo 113 | * \ingroup audio_spdif 114 | * 115 | * \param producer 116 | * \param connection 117 | */ 118 | bool audio_spdif_connect_thru(audio_buffer_pool_t *producer, audio_connection_t *connection); 119 | 120 | 121 | /** \brief \todo 122 | * \ingroup audio_spdif 123 | * 124 | * \param producer 125 | */ 126 | bool audio_spdif_connect(audio_buffer_pool_t *producer); 127 | 128 | 129 | /** \brief \todo 130 | * \ingroup audio_spdif 131 | * 132 | * \param producer 133 | */ 134 | bool audio_spdif_connect_s8(audio_buffer_pool_t *producer); 135 | bool audio_spdif_connect_extra(audio_buffer_pool_t *producer, bool buffer_on_give, uint buffer_count, 136 | audio_connection_t *connection); 137 | 138 | /** \brief \todo 139 | * \ingroup audio_spdif 140 | * 141 | * \param producer 142 | * \param buffer_on_give 143 | * \param buffer_count 144 | * \param samples_per_buffer 145 | * \param connection 146 | * \return 147 | */ 148 | bool audio_spdif_connect_extra(audio_buffer_pool_t *producer, bool buffer_on_give, uint buffer_count, 149 | audio_connection_t *connection); 150 | 151 | 152 | /** \brief Set up system to output S/PDIF audio 153 | * \ingroup audio_spdif 154 | * 155 | * \param enabled true to enable S/PDIF audio, false to disable. 156 | */ 157 | void audio_spdif_set_enabled(bool enabled); 158 | 159 | #ifdef __cplusplus 160 | } 161 | #endif 162 | 163 | #endif //_AUDIO_SPDIF_H 164 | -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_spdif/include/pico/audio_spdif/sample_encoding.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _PICO_AUDIO_SPDIF_SAMPLE_ENCODING_H 8 | #define _PICO_AUDIO_SPDIF_SAMPLE_ENCODING_H 9 | 10 | #include "pico/audio.h" 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | void mono_to_spdif_producer_give(audio_connection_t *connection, audio_buffer_t *buffer); 17 | void stereo_to_spdif_producer_give(audio_connection_t *connection, audio_buffer_t *buffer); 18 | 19 | typedef struct { 20 | uint32_t l; 21 | uint32_t h; 22 | } spdif_subframe_t; 23 | 24 | extern uint32_t spdif_lookup[256]; 25 | 26 | static inline void spdif_update_subframe(spdif_subframe_t *subframe, int16_t sample) { 27 | // the subframe is partially initialized, so we need to insert the sample 28 | // bits and update the parity 29 | uint32_t sl = spdif_lookup[(uint8_t)sample]; 30 | uint32_t sh = spdif_lookup[(uint8_t)(sample>>8u)]; 31 | subframe->l = (subframe->l & 0xffffffu) | (sl << 24u); 32 | uint32_t ph = subframe->h >> 24u; 33 | uint32_t h = (((uint16_t)sh) << 8u) | 34 | (((uint16_t)sl) >> 8u); 35 | uint32_t p = (sl>>16u)^(sh>>16u); 36 | p = p ^ ((__mul_instruction(ph&0x2a,0x2a) >> 6u) & 1u); 37 | subframe->h = h | ((ph&0x7f) << 24u) | (p << 31u); 38 | } 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif //SOFTWARE_SAMPLE_ENCODING_H 45 | -------------------------------------------------------------------------------- /src/rp2_common/pico_audio_spdif/sample_encoding.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | 9 | #include "pico/sample_conversion.h" 10 | #include "pico/audio_spdif/sample_encoding.h" 11 | #include "pico/audio_spdif.h" 12 | #include "hardware/gpio.h" 13 | 14 | static_assert(8 == sizeof(spdif_subframe_t), ""); 15 | 16 | // subframe within SPDIF 17 | struct FmtSPDIF : public FmtDetails { 18 | }; 19 | 20 | template 21 | struct converting_copy, Stereo> { 22 | static void copy(FmtSPDIF::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) { 23 | for (uint i = 0; i < sample_count * 2; i++) { 24 | spdif_update_subframe(dest++, sample_converter::convert_sample(*src++)); 25 | } 26 | } 27 | }; 28 | 29 | template 30 | struct converting_copy, Mono> { 31 | static void copy(FmtSPDIF::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) { 32 | for (uint i = 0; i < sample_count; i++) { 33 | int16_t sample = sample_converter::convert_sample(*src++); 34 | spdif_update_subframe(dest++, sample); 35 | spdif_update_subframe(dest++, sample); 36 | } 37 | } 38 | }; 39 | 40 | 41 | void stereo_to_spdif_producer_give(audio_connection_t *connection, audio_buffer_t *buffer) { 42 | producer_pool_blocking_give, Stereo>(connection, buffer); 43 | } 44 | 45 | void mono_to_spdif_producer_give(audio_connection_t *connection, audio_buffer_t *buffer) { 46 | producer_pool_blocking_give, Mono>(connection, buffer); 47 | } 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/rp2_common/pico_scanvideo_dbi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(video_dbi INTERFACE) 2 | 3 | # todo right now this is just a copy of video/ 4 | pico_generate_pio_header(video_dbi ${CMAKE_CURRENT_LIST_DIR}/video.pio) 5 | pico_generate_pio_header(video_dbi ${CMAKE_CURRENT_LIST_DIR}/control.pio) 6 | 7 | target_sources(video_dbi INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/video.h 9 | ${CMAKE_CURRENT_LIST_DIR}/vga_modes.c 10 | $<$:${CMAKE_CURRENT_LIST_DIR}/tft_driver.c> 11 | $<$:${CMAKE_CURRENT_LIST_DIR}/video_dbi.c> 12 | ) 13 | 14 | target_compile_definitions(video_dbi INTERFACE VIDEO_DBI) 15 | target_include_directories(video_dbi INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 16 | target_link_libraries(video_dbi INTERFACE dma pio) -------------------------------------------------------------------------------- /src/rp2_common/pico_scanvideo_dbi/README.md: -------------------------------------------------------------------------------- 1 | THIS IS CURRENTLY NOT COMPILING (A PLACEHOLDER THAT NEEDS FIXING) -------------------------------------------------------------------------------- /src/rp2_common/pico_scanvideo_dbi/control.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | .program video_dbi_control 8 | .define out_delay 1 9 | .define clk_delay 1 10 | 11 | .side_set 1 ; used for WR strobe 12 | .extern data_run_out 13 | out pins, 16 [out_delay] set 0 14 | jmp x-- data_run_out [out_delay] set 1 15 | 16 | .extern entry_point 17 | .wrap_target 18 | .extern new_state_wait 19 | out exec, 16 set 1 20 | out x, 11 set 1 21 | out pc, 5 set 1 22 | .extern clock_run 23 | nop [clk_delay] set 0 24 | jmp x-- clock_run [clk_delay] set 1 25 | .wrap 26 | -------------------------------------------------------------------------------- /src/rp2_common/pico_scanvideo_dbi/tft_driver.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef SOFTWARE_TFT_DRIVER_H 8 | #define SOFTWARE_TFT_DRIVER_H 9 | 10 | // These register enumerations are not all used, but kept for possible future use 11 | #define HX8357D 0xD 12 | #define HX8357B 0xB7 13 | 14 | #define HX8357_NOP 0x00 15 | #define HX8357_SWRESET 0x01 16 | #define HX8357_RDDID 0x04 17 | #define HX8357_RDDST 0x09 18 | 19 | #define HX8357_RDPOWMODE 0x0A 20 | #define HX8357_RDMADCTL 0x0B 21 | #define HX8357_RDCOLMOD 0x0C 22 | #define HX8357_RDDIM 0x0D 23 | #define HX8357_RDDSDR 0x0F 24 | 25 | #define HX8357_SLPIN 0x10 26 | #define HX8357_SLPOUT 0x11 27 | #define HX8357B_PTLON 0x12 28 | #define HX8357B_NORON 0x13 29 | 30 | #define HX8357_INVOFF 0x20 31 | #define HX8357_INVON 0x21 32 | #define HX8357_DISPOFF 0x28 33 | #define HX8357_DISPON 0x29 34 | 35 | #define HX8357_CASET 0x2A 36 | #define HX8357_PASET 0x2B 37 | #define HX8357_RAMWR 0x2C 38 | #define HX8357_RAMRD 0x2E 39 | 40 | #define HX8357B_PTLAR 0x30 41 | #define HX8357_TEON 0x35 42 | #define HX8357_TEARLINE 0x44 43 | #define HX8357_MADCTL 0x36 44 | #define HX8357_COLMOD 0x3A 45 | 46 | #define HX8357_SETOSC 0xB0 47 | #define HX8357_SETPWR1 0xB1 48 | #define HX8357B_SETDISPLAY 0xB2 49 | #define HX8357_SETRGB 0xB3 50 | #define HX8357D_SETCOM 0xB6 51 | 52 | #define HX8357B_SETDISPMODE 0xB4 53 | #define HX8357D_SETCYC 0xB4 54 | #define HX8357B_SETOTP 0xB7 55 | #define HX8357D_SETC 0xB9 56 | 57 | #define HX8357B_SET_PANEL_DRIVING 0xC0 58 | #define HX8357D_SETSTBA 0xC0 59 | #define HX8357B_SETDGC 0xC1 60 | #define HX8357B_SETID 0xC3 61 | #define HX8357B_SETDDB 0xC4 62 | #define HX8357B_SETDISPLAYFRAME 0xC5 63 | #define HX8357B_GAMMASET 0xC8 64 | #define HX8357B_SETCABC 0xC9 65 | #define HX8357_SETPANEL 0xCC 66 | 67 | 68 | #define HX8357B_SETPOWER 0xD0 69 | #define HX8357B_SETVCOM 0xD1 70 | #define HX8357B_SETPWRNORMAL 0xD2 71 | 72 | #define HX8357B_RDID1 0xDA 73 | #define HX8357B_RDID2 0xDB 74 | #define HX8357B_RDID3 0xDC 75 | #define HX8357B_RDID4 0xDD 76 | 77 | #define HX8357D_SETGAMMA 0xE0 78 | 79 | #define HX8357B_SETGAMMA 0xC8 80 | #define HX8357B_SETPANELRELATED 0xE9 81 | 82 | #define RS_PIN 24u 83 | #define CS_PIN 25u 84 | #define WR_PIN 26u 85 | #define RST_PIN 27u 86 | 87 | // don't care 88 | #define FCS_PIN 0 //23 89 | 90 | extern void tft_driver_init(); 91 | extern uint32_t *get_switch_buffer_sequence(uint *count, bool buffer); 92 | extern uint32_t *get_control_sequence(uint w, uint y, uint *count, bool buffer); 93 | 94 | #endif //SOFTWARE_TFT_DRIVER_H 95 | -------------------------------------------------------------------------------- /src/rp2_common/pico_scanvideo_dbi/vga_modes.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "video.h" 8 | 9 | // todo support for inverted-y (probably belongs in the scanline generators, as would inverted x) 10 | const uint32_t video_clock_freq = 24000000; 11 | 12 | extern const video_pio_program_t video_24mhz_composable; 13 | const video_timing_t vga_timing_640x480_60_default = 14 | { 15 | .clock_freq = 24000000, 16 | 17 | .h_active = 640, 18 | .v_active = 480, 19 | 20 | .h_front_porch = 16, 21 | .h_pulse = 64, 22 | .h_total = 800, 23 | .h_sync_polarity = 1, 24 | 25 | .v_front_porch = 1, 26 | .v_pulse = 2, 27 | .v_total = 500, 28 | .v_sync_polarity = 1, 29 | 30 | .enable_clock = 0, 31 | .clock_polarity = 0, 32 | 33 | .enable_den = 0 34 | }; 35 | 36 | const video_timing_t vga_timing_640x240_60_default = 37 | { 38 | .clock_freq = 24000000, 39 | 40 | .h_active = 640, 41 | .v_active = 240, 42 | 43 | .h_front_porch = 16, 44 | .h_pulse = 64, 45 | .h_total = 800, 46 | .h_sync_polarity = 1, 47 | 48 | .v_front_porch = 1, 49 | .v_pulse = 2, 50 | .v_total = 250, 51 | .v_sync_polarity = 1, 52 | 53 | .enable_clock = 0, 54 | .clock_polarity = 0, 55 | 56 | .enable_den = 0 57 | }; 58 | 59 | 60 | const video_timing_t vga_timing_648x480_60_alt1 = 61 | { 62 | 63 | .clock_freq = 24000000, 64 | 65 | .h_active = 640, 66 | .v_active = 480, 67 | 68 | .h_front_porch = 16, 69 | .h_pulse = 48, 70 | .h_total = 768, 71 | .h_sync_polarity = 1, 72 | 73 | .v_front_porch = 10, 74 | .v_pulse = 2, 75 | .v_total = 523, 76 | .v_sync_polarity = 1, 77 | 78 | .enable_clock = 0, 79 | .clock_polarity = 0, 80 | 81 | .enable_den = 0 82 | }; 83 | 84 | const video_timing_t vga_timing_648x480_50ish = 85 | { 86 | 87 | .clock_freq = 24000000, 88 | 89 | .h_active = 640, 90 | .v_active = 480, 91 | 92 | .h_front_porch = 56, 93 | .h_pulse = 72, 94 | .h_total = 896, 95 | .h_sync_polarity = 1, 96 | 97 | .v_front_porch = 30, 98 | .v_pulse = 2, 99 | .v_total = 536, 100 | .v_sync_polarity = 1, 101 | 102 | .enable_clock = 0, 103 | .clock_polarity = 0, 104 | 105 | .enable_den = 0 106 | }; 107 | 108 | const video_timing_t vga_timing_648x480_50ish2 = 109 | { 110 | 111 | .clock_freq = 24000000, 112 | 113 | .h_active = 640, 114 | .v_active = 480, 115 | 116 | .h_front_porch = 32, 117 | .h_pulse = 64, 118 | .h_total = 832, 119 | .h_sync_polarity = 1, 120 | 121 | .v_front_porch = 27, 122 | .v_pulse = 2, 123 | .v_total = 577, 124 | .v_sync_polarity = 1, 125 | 126 | .enable_clock = 0, 127 | .clock_polarity = 0, 128 | 129 | .enable_den = 0 130 | }; 131 | 132 | const video_timing_t vga_timing_648x480_50ish3 = 133 | { 134 | 135 | .clock_freq = 24000000, 136 | 137 | .h_active = 640, 138 | .v_active = 480, 139 | 140 | .h_front_porch = 72, 141 | .h_pulse = 96, 142 | .h_total = 928, 143 | .h_sync_polarity = 1, 144 | 145 | .v_front_porch = 8, 146 | .v_pulse = 2, 147 | .v_total = 518, 148 | .v_sync_polarity = 1, 149 | 150 | .enable_clock = 0, 151 | .clock_polarity = 0, 152 | 153 | .enable_den = 0 154 | }; 155 | 156 | #define actual_vga_timing_50 vga_timing_648x480_50ish3 157 | 158 | const video_mode_t vga_mode_160x120_60 = 159 | { 160 | .default_timing = &vga_timing_640x480_60_default, 161 | .pio_program = &video_24mhz_composable, 162 | .width = 160, 163 | .height = 120, 164 | .xscale = 4, 165 | .yscale = 4, 166 | }; 167 | 168 | const video_mode_t vga_mode_213x160_60 = 169 | { 170 | .default_timing = &vga_timing_640x480_60_default, 171 | .pio_program = &video_24mhz_composable, 172 | .width = 213, 173 | .height = 160, 174 | .xscale = 3, 175 | .yscale = 3, 176 | }; 177 | 178 | const video_mode_t vga_mode_320x240_60 = 179 | { 180 | .default_timing = &vga_timing_640x240_60_default, 181 | .pio_program = &video_24mhz_composable, 182 | .width = 320, 183 | .height = 240, 184 | .xscale = 2, 185 | .yscale = 1, 186 | }; 187 | 188 | const video_mode_t vga_mode_320x480_60 = 189 | { 190 | .default_timing = &vga_timing_640x480_60_default, 191 | .pio_program = &video_24mhz_composable, 192 | .width = 320, 193 | .height = 480, 194 | .xscale = 2, 195 | .yscale = 2, 196 | }; 197 | 198 | 199 | const video_mode_t vga_mode_640x480_60 = 200 | { 201 | .default_timing = &vga_timing_640x480_60_default, 202 | .pio_program = &video_24mhz_composable, 203 | .width = 640, 204 | .height = 480, 205 | .xscale = 1, 206 | .yscale = 1, 207 | }; 208 | 209 | 210 | const video_mode_t vga_mode_640x480_50 = 211 | { 212 | .default_timing = &actual_vga_timing_50, 213 | .pio_program = &video_24mhz_composable, 214 | .width = 640, 215 | .height = 480, 216 | .xscale = 1, 217 | .yscale = 1, 218 | }; 219 | 220 | const video_mode_t vga_mode_320x240_50 = 221 | { 222 | .default_timing = &actual_vga_timing_50, 223 | .pio_program = &video_24mhz_composable, 224 | .width = 320, 225 | .height = 240, 226 | .xscale = 2, 227 | .yscale = 2, 228 | }; 229 | 230 | /* this is 50 hz */ 231 | const video_timing_t vga_timing_wide_480_50 = 232 | { 233 | .clock_freq = 24000000, 234 | 235 | .h_active = 800, 236 | .v_active = 480, 237 | 238 | .h_front_porch = 32, 239 | .h_pulse = 48, 240 | .h_total = 960, 241 | .h_sync_polarity = 0, 242 | 243 | .v_front_porch = 1, 244 | .v_pulse = 2, 245 | .v_total = 500, 246 | .v_sync_polarity = 0, 247 | 248 | .enable_clock = 1, 249 | .clock_polarity = 0, 250 | 251 | .enable_den = 1 252 | }; 253 | 254 | const video_mode_t vga_mode_tft_800x480_50 = 255 | { 256 | .default_timing = &vga_timing_wide_480_50, 257 | .pio_program = &video_24mhz_composable, 258 | .width = 800, 259 | .height = 480, 260 | .xscale = 1, 261 | .yscale = 1, 262 | }; 263 | 264 | const video_mode_t vga_mode_tft_400x240_50 = 265 | { 266 | .default_timing = &vga_timing_wide_480_50, 267 | .pio_program = &video_24mhz_composable, 268 | .width = 400, 269 | .height = 240, 270 | .xscale = 2, 271 | .yscale = 2, 272 | }; 273 | 274 | const video_timing_t vga_timing_512x576_50_attempt1 = 275 | { 276 | .clock_freq = 24000000, 277 | 278 | .h_active = 512, 279 | .v_active = 576, 280 | 281 | .h_front_porch = 64, 282 | .h_pulse = 64, 283 | .h_total = 768, 284 | .h_sync_polarity = 1, 285 | 286 | .v_front_porch = 30, 287 | .v_pulse = 2, 288 | .v_total = 612, 289 | .v_sync_polarity = 1, 290 | 291 | .enable_clock = 0, 292 | .clock_polarity = 0, 293 | 294 | .enable_den = 0 295 | }; 296 | 297 | const video_timing_t vga_timing_512x576_60_attempt1 = 298 | { 299 | .clock_freq = 24000000, 300 | 301 | .h_active = 512, 302 | .v_active = 576, 303 | 304 | .h_front_porch = 64, 305 | .h_pulse = 64, 306 | .h_total = 768, 307 | .h_sync_polarity = 1, 308 | 309 | .v_front_porch = 30, 310 | .v_pulse = 2, 311 | .v_total = 612, 312 | .v_sync_polarity = 1, 313 | 314 | .enable_clock = 0, 315 | .clock_polarity = 0, 316 | 317 | .enable_den = 0 318 | }; 319 | 320 | const video_mode_t vga_mode_256x192_50 = 321 | { 322 | .default_timing = &vga_timing_512x576_50_attempt1, 323 | .pio_program = &video_24mhz_composable, 324 | .width = 256, 325 | .height = 192, 326 | .xscale = 2, 327 | .yscale = 3, 328 | }; 329 | 330 | const video_timing_t vga_timing_800x600_38 = 331 | { 332 | .clock_freq = 24000000, 333 | 334 | .h_active = 800, 335 | .v_active = 600, 336 | 337 | .h_front_porch = 24, 338 | .h_pulse = 80, 339 | .h_total = 1008, 340 | .h_sync_polarity = 1, 341 | 342 | .v_front_porch = 3, 343 | .v_pulse = 4, 344 | .v_total = 621, 345 | .v_sync_polarity = 1, 346 | 347 | .enable_clock = 0, 348 | .clock_polarity = 0, 349 | 350 | .enable_den = 0 351 | }; 352 | 353 | const video_mode_t vga_mode_800x600_38 = 354 | { 355 | .default_timing = &vga_timing_800x600_38, 356 | .pio_program = &video_24mhz_composable, 357 | .width = 800, 358 | .height = 600, 359 | .xscale = 1, 360 | .yscale = 1, 361 | }; 362 | 363 | -------------------------------------------------------------------------------- /src/rp2_common/pico_scanvideo_dbi/video.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | 8 | #ifndef _VIDEO_H 9 | #define _VIDEO_H 10 | 11 | #include "platform.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | // == CONFIG ============ 18 | #if PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA || PICO_SCANVIDEO_PLANE1_FIXED_FRAGMENT_DMA 19 | #define PICO_SCANVIDEO_PLANE1_FRAGMENT_DMA 1 20 | #endif 21 | #if PICO_SCANVIDEO_PLANE2_VARIABLE_FRAGMENT_DMA) || PICO_SCANVIDEO_PLANE2_FIXED_FRAGMENT_DMA 22 | #define PICO_SCANVIDEO_PLANE2_FRAGMENT_DMA 1 23 | #endif 24 | #if PICO_SCANVIDEO_PLANE3_VARIABLE_FRAGMENT_DMA || PICO_SCANVIDEO_PLANE3_FIXED_FRAGMENT_DMA 25 | #define PICO_SCANVIDEO_PLANE3_FRAGMENT_DMA 1 26 | #endif 27 | 28 | #define ENABLE_VIDEO_CLOCK 29 | #define ENABLE_VIDEO_DEN 30 | // todo make multi plane play nicely with mode swapping; 31 | // today we have hard coded blank/empty lines 32 | 33 | //#define ENABLE_VIDEO_PLANE2 34 | //#define ENABLE_VIDEO_PLANE3 35 | //#define PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA 1 36 | //#define PICO_SCANVIDEO_PLANE2_VARIABLE_FRAGMENT_DMA 1 37 | 38 | #ifndef PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS 39 | #define PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS 128 40 | #endif 41 | #ifndef PICO_SCANVIDEO_MAX_SCANLINE_BUFFER2_WORDS 42 | #define PICO_SCANVIDEO_MAX_SCANLINE_BUFFER2_WORDS 16 43 | #endif 44 | #ifndef PICO_SCANVIDEO_MAX_SCANLINE_BUFFER3_WORDS 45 | #define PICO_SCANVIDEO_MAX_SCANLINE_BUFFER3_WORDS 16 46 | #endif 47 | 48 | //extern struct semaphore vmode_updated; 49 | 50 | // note by default we allow for alpha mask (and lose a bit of green) 51 | // todo make this configurable 52 | #define PICO_SCANVIDEO_ALPHA_MASK 0x0020 53 | #define PICO_SCANVIDEO_PIXEL_RSHIFT 0 54 | #define PICO_SCANVIDEO_PIXEL_GSHIFT 6 55 | #define PICO_SCANVIDEO_PIXEL_BSHIFT 11 56 | #define PICO_SCANVIDEO_PIXEL_FROM_RGB8(r, g, b) ((((b)>>3)<>3)<>3)< 1 126 | uint32_t *data2; 127 | uint16_t data2_used; 128 | uint16_t data2_max; 129 | #if PICO_SCANVIDEO_PLANE_COUNT > 2 130 | uint32_t *data3; 131 | uint16_t data3_used; 132 | uint16_t data3_max; 133 | #endif 134 | #endif 135 | // useful to track state between the buffer being passed to 136 | // video_end_scanline_generation, and when the buffer is no longer 137 | // in use by the video code and is returned to a subsequent caller 138 | // via video_begin_scanline_generation 139 | // todo we caould add a callback to begin scanline generation to enuerate 140 | // all already discarded buffers early - not clear this would be useful in general 141 | // because it only saves you space if stuff is running with low buffer utilization 142 | void *user_data; 143 | uint8_t status; 144 | }; 145 | 146 | enum 147 | { 148 | SCANLINE_OK = 1, 149 | SCANLINE_ERROR, 150 | SCANLINE_SKIPPED 151 | }; 152 | 153 | // note frame numbers wrap 154 | static inline uint16_t frame_number(uint32_t scanline_id) 155 | { 156 | return (uint16_t) (scanline_id >> 16u); 157 | } 158 | 159 | static inline uint16_t scanline_number(uint32_t scanline_id) 160 | { 161 | return (uint16_t) scanline_id; 162 | } 163 | 164 | /** 165 | * @return the current vga mode (if there is one) 166 | */ 167 | extern struct video_mode video_get_mode(); 168 | 169 | /** 170 | * @return the next scanline_id to be displayed (may be from the next frame) 171 | */ 172 | extern uint32_t video_get_next_scanline_id(); 173 | 174 | /** 175 | * @return true if in the vblank interval 176 | */ 177 | extern bool video_in_vblank(); 178 | /** 179 | * @return true if in the hblank interval (or more accurately scanline data is not currently being sent to the PIO, which roughly corresponds, but is not exact). Note also that in 180 | * yscale-d modes, there are height * yscale hblank intervals per frame. 181 | */ 182 | extern bool video_in_hblank(); 183 | 184 | extern void video_wait_for_vblank(); 185 | 186 | extern uint32_t video_wait_for_scanline_complete(uint32_t scanline_id); 187 | /** 188 | * Acquire a scanline that needs generating. The scanline_id field indicates which scanline is required. 189 | * 190 | * This method may be called concurrently 191 | * 192 | * @param block true to block if the vga system is not ready to generate a new scanline 193 | * @return the scanline_buffer or NULL if block is false, and the vga system is not ready 194 | */ 195 | struct scanline_buffer *video_begin_scanline_generation(bool block); 196 | 197 | /** 198 | * Return a scanline that has been generated / or at least the client is done with. 199 | * 200 | * The status field indicates whether the scanline was actually generated OK 201 | * 202 | * This method may be called concurrently (for different buffers) 203 | * 204 | * @param scanline_buffer \todo 205 | */ 206 | void video_end_scanline_generation(struct scanline_buffer *scanline_buffer); 207 | 208 | extern const struct video_timing vga_timing_640x480_60_default; 209 | extern const struct video_timing vga_timing_wide_480_50; 210 | extern const struct video_timing vga_timing_648x480_60_alt1; 211 | 212 | extern const struct video_mode vga_mode_160x120_60; // 3d monster maze anyone :-) 213 | extern const struct video_mode vga_mode_213x160_60; 214 | extern const struct video_mode vga_mode_320x240_60; 215 | extern const struct video_mode vga_mode_640x480_60; 216 | extern const struct video_mode vga_mode_320x480_60; 217 | 218 | extern const struct video_mode vga_mode_tft_800x480_50; 219 | extern const struct video_mode vga_mode_tft_400x240_50; 220 | 221 | #ifndef NDEBUG 222 | // todo this is only for vga composable 24... should exist behind mode impl 223 | extern void validate_scanline(const uint32_t *dma_data, uint dma_data_size, uint max_pixels, uint expected_width); 224 | #endif 225 | 226 | 227 | // mode implementation 228 | 229 | pio_hw_t; 230 | 231 | struct video_pio_program 232 | { 233 | #if !PICO_NO_HARDWARE 234 | const uint16_t *program; 235 | const int program_size; 236 | const int entry_point; 237 | bool (*adapt_for_mode)(const struct video_pio_program *program, const struct video_mode *mode, 238 | struct scanline_buffer *missing_scanline_buffer, uint16_t *buffer, uint buffer_max); 239 | void (*configure_pio)(pio_hw_t *pio, uint sm); 240 | #else 241 | const char *id; 242 | #endif 243 | }; 244 | 245 | extern void video_default_configure_pio(pio_hw_t *pio, uint sm, uint wrap_trarget, uint wrap, bool overlay); 246 | 247 | #include "video.pio.h" 248 | #if !PICO_SCANVIDEO_USE_RAW1P_2CYCLE 249 | #define video_24mhz_composable_prefix video_24mhz_composable_default 250 | #else 251 | #define video_24mhz_composable_prefix video_24mhz_composable_raw1p_2cycle 252 | #endif 253 | // yuk... extra __P needed for native on some platforms 254 | #define video_24mhz_composable_program_extern(x) __SAFE_CONCAT(__SAFE_CONCAT(video_24mhz_composable_prefix, _offset_), x) 255 | #define __DVP_JMP(x) ((unsigned)video_24mhz_composable_program_extern(x)) 256 | #define COMPOSABLE_COLOR_RUN __DVP_JMP(color_run) 257 | #define COMPOSABLE_EOL_ALIGN __DVP_JMP(end_of_scanline_ALIGN) 258 | #define COMPOSABLE_EOL_SKIP_ALIGN __DVP_JMP(end_of_scanline_skip_word_ALIGN) 259 | #define COMPOSABLE_RAW_RUN __DVP_JMP(raw_run) 260 | #define COMPOSABLE_RAW_1P __DVP_JMP(raw_1p) 261 | #define COMPOSABLE_RAW_2P __DVP_JMP(raw_2p) 262 | #if !PICO_SCANVIDEO_USE_RAW1P_2CYCLE 263 | #define COMPOSABLE_RAW_1P_SKIP_ALIGN __DVP_JMP(raw_1p_skip_word_ALIGN) 264 | #else 265 | #define COMPOSABLE_RAW_1P_2CYCLE __DVP_JMP(raw_1p_2cycle) 266 | #endif 267 | 268 | #ifdef __cplusplus 269 | } 270 | #endif 271 | #endif //_VIDEO_H 272 | -------------------------------------------------------------------------------- /src/rp2_common/pico_scanvideo_dpi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (TARGET pico_scanvideo) 2 | add_library(pico_scanvideo_dpi INTERFACE) 3 | 4 | pico_generate_pio_header(pico_scanvideo_dpi ${CMAKE_CURRENT_LIST_DIR}/timing.pio) 5 | 6 | target_sources(pico_scanvideo_dpi INTERFACE 7 | ${CMAKE_CURRENT_LIST_DIR}/scanvideo.c 8 | ) 9 | 10 | target_include_directories(pico_scanvideo_dpi INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 11 | target_compile_definitions(pico_scanvideo_dpi INTERFACE VIDEO_DPI) 12 | target_link_libraries(pico_scanvideo_dpi INTERFACE hardware_dma hardware_pio hardware_irq pico_scanvideo) 13 | 14 | if (PICO_C_COMPILER_IS_CLANG) 15 | # Clang does not support optimize pragma 16 | set_source_files_properties( 17 | ${CMAKE_CURRENT_LIST_DIR}/scanvideo.c 18 | PROPERTIES 19 | COMPILE_OPTIONS "-O3" 20 | ) 21 | endif() 22 | endif() -------------------------------------------------------------------------------- /src/rp2_common/pico_scanvideo_dpi/include/pico/scanvideo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef PICO_SCANVIDEO_H_ 8 | #define PICO_SCANVIDEO_H_ 9 | 10 | // note that defining to false will force non-inclusion also 11 | #if !defined(PICO_SCANVIDEO_DPI) 12 | #define PICO_SCANVIDEO_DPI 1 13 | 14 | #ifndef PARAM_ASSERTIONS_ENABLED_SCANVIDEO_DPI 15 | #define PARAM_ASSERTIONS_ENABLED_SCANVIDEO_DPI 0 16 | #endif 17 | 18 | #include "pico/scanvideo/scanvideo_base.h" 19 | 20 | #ifndef PICO_SCANVIDEO_DPI_ALPHA_PIN 21 | #define PICO_SCANVIDEO_DPI_ALPHA_PIN 5u 22 | #endif 23 | 24 | #ifndef PICO_SCANVIDEO_DPI_PIXEL_RSHIFT 25 | #define PICO_SCANVIDEO_DPI_PIXEL_RSHIFT 0u 26 | #endif 27 | 28 | #ifndef PICO_SCANVIDEO_DPI_PIXEL_GSHIFT 29 | #define PICO_SCANVIDEO_DPI_PIXEL_GSHIFT 6u 30 | #endif 31 | 32 | #ifndef PICO_SCANVIDEO_DPI_PIXEL_BSHIFT 33 | #define PICO_SCANVIDEO_DPI_PIXEL_BSHIFT 11u 34 | #endif 35 | 36 | #ifndef PICO_SCANVIDEO_DPI_PIXEL_RCOUNT 37 | #define PICO_SCANVIDEO_DPI_PIXEL_RCOUNT 5 38 | #endif 39 | 40 | #ifndef PICO_SCANVIDEO_DPI_PIXEL_GCOUNT 41 | #define PICO_SCANVIDEO_DPI_PIXEL_GCOUNT 5 42 | #endif 43 | 44 | #ifndef PICO_SCANVIDEO_DPI_PIXEL_BCOUNT 45 | #define PICO_SCANVIDEO_DPI_PIXEL_BCOUNT 5 46 | #endif 47 | 48 | #ifndef PICO_SCANVIDEO_ALPHA_PIN 49 | #define PICO_SCANVIDEO_ALPHA_PIN PICO_SCANVIDEO_DPI_ALPHA_PIN 50 | #endif 51 | 52 | #ifndef PICO_SCANVIDEO_PIXEL_RSHIFT 53 | #define PICO_SCANVIDEO_PIXEL_RSHIFT PICO_SCANVIDEO_DPI_PIXEL_RSHIFT 54 | #endif 55 | 56 | #ifndef PICO_SCANVIDEO_PIXEL_GSHIFT 57 | #define PICO_SCANVIDEO_PIXEL_GSHIFT PICO_SCANVIDEO_DPI_PIXEL_GSHIFT 58 | #endif 59 | 60 | #ifndef PICO_SCANVIDEO_PIXEL_BSHIFT 61 | #define PICO_SCANVIDEO_PIXEL_BSHIFT PICO_SCANVIDEO_DPI_PIXEL_BSHIFT 62 | #endif 63 | 64 | #ifndef PICO_SCANVIDEO_PIXEL_RCOUNT 65 | #define PICO_SCANVIDEO_PIXEL_RCOUNT PICO_SCANVIDEO_DPI_PIXEL_RCOUNT 66 | #endif 67 | 68 | #ifndef PICO_SCANVIDEO_PIXEL_GCOUNT 69 | #define PICO_SCANVIDEO_PIXEL_GCOUNT PICO_SCANVIDEO_DPI_PIXEL_GCOUNT 70 | #endif 71 | 72 | #ifndef PICO_SCANVIDEO_PIXEL_BCOUNT 73 | #define PICO_SCANVIDEO_PIXEL_BCOUNT PICO_SCANVIDEO_DPI_PIXEL_BCOUNT 74 | #endif 75 | 76 | 77 | /** \file scanvideo.h 78 | * \defgroup pico_scanvideo_dpi pico_scanvideo_dpi 79 | * 80 | * DPI Scan-out Video using the PIO 81 | */ 82 | 83 | #endif 84 | #endif 85 | -------------------------------------------------------------------------------- /src/rp2_common/pico_scanvideo_dpi/timing.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | .program video_htiming 8 | .side_set 1 ; used for clock 9 | public entry_point: 10 | ; todo we can do this with one off setup via pio_exec 11 | pull block side 0 12 | .wrap_target 13 | new_state: 14 | out exec, 16 side 1 ; this does any per state inline work (or it can be a JMP to entry_point to sleep.. 15 | ; note the EXECed instruction should have a side set 0 16 | out x, 13 side 1 17 | out pins, 3 side 0 ; we want an OUT EXEC above which sets an IRQ to start scanline output 18 | ; to cause this out and the pixel out in the same cycle (this would be positive clk edge latch) 19 | loop: 20 | nop side 1 21 | jmp x-- loop side 0 22 | .wrap 23 | 24 | ; these are the values used in the out exec in video_htiming 25 | .program video_htiming_states 26 | .side_set 1 27 | ; state 0 = set irq 0 28 | irq 0 side 0 29 | ; state 1 = set irq 1 30 | irq 1 side 0 31 | ; state 2 = set irq 4 32 | irq 4 side 0 33 | ; state 3 = clear irq 4 34 | irq clear 4 side 0 35 | -------------------------------------------------------------------------------- /src/rp2_common/pico_sd_card/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT TARGET pico_sd_card) 2 | add_library(pico_sd_card INTERFACE) 3 | 4 | pico_generate_pio_header(pico_sd_card ${CMAKE_CURRENT_LIST_DIR}/sd_card.pio) 5 | 6 | target_sources(pico_sd_card INTERFACE 7 | ${CMAKE_CURRENT_LIST_DIR}/sd_card.c 8 | ) 9 | 10 | target_include_directories(pico_sd_card INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 11 | target_link_libraries(pico_sd_card INTERFACE pico_sd_card_headers hardware_dma hardware_pio) 12 | endif() -------------------------------------------------------------------------------- /src/rp2_common/pico_sd_card/README.md: -------------------------------------------------------------------------------- 1 | THIS CODE IS AT PROTOTYPING LEVEL ONLY (THOUGH IS USABLE) -------------------------------------------------------------------------------- /src/rp2_common/pico_sd_card/crc-itu-t.h: -------------------------------------------------------------------------------- 1 | const uint16_t crc_itu_t_table[256] = { 2 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 3 | 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 4 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 5 | 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 6 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 7 | 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 8 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 9 | 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 10 | 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 11 | 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 12 | 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 13 | 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 14 | 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 15 | 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 16 | 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 17 | 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 18 | 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 19 | 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 20 | 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 21 | 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 22 | 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 23 | 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 24 | 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 25 | 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 26 | 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 27 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 28 | 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 29 | 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 30 | 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 31 | 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 32 | 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 33 | 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 34 | }; 35 | -------------------------------------------------------------------------------- /src/rp2_common/pico_sd_card/crc7.h: -------------------------------------------------------------------------------- 1 | static const uint8_t crc7_table[256] = { 2 | 0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, 3 | 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee, 4 | 0x32, 0x20, 0x16, 0x04, 0x7a, 0x68, 0x5e, 0x4c, 5 | 0xa2, 0xb0, 0x86, 0x94, 0xea, 0xf8, 0xce, 0xdc, 6 | 0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x08, 0x1a, 7 | 0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a, 8 | 0x56, 0x44, 0x72, 0x60, 0x1e, 0x0c, 0x3a, 0x28, 9 | 0xc6, 0xd4, 0xe2, 0xf0, 0x8e, 0x9c, 0xaa, 0xb8, 10 | 0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6, 11 | 0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x02, 0x34, 0x26, 12 | 0xfa, 0xe8, 0xde, 0xcc, 0xb2, 0xa0, 0x96, 0x84, 13 | 0x6a, 0x78, 0x4e, 0x5c, 0x22, 0x30, 0x06, 0x14, 14 | 0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2, 15 | 0x3c, 0x2e, 0x18, 0x0a, 0x74, 0x66, 0x50, 0x42, 16 | 0x9e, 0x8c, 0xba, 0xa8, 0xd6, 0xc4, 0xf2, 0xe0, 17 | 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x70, 18 | 0x82, 0x90, 0xa6, 0xb4, 0xca, 0xd8, 0xee, 0xfc, 19 | 0x12, 0x00, 0x36, 0x24, 0x5a, 0x48, 0x7e, 0x6c, 20 | 0xb0, 0xa2, 0x94, 0x86, 0xf8, 0xea, 0xdc, 0xce, 21 | 0x20, 0x32, 0x04, 0x16, 0x68, 0x7a, 0x4c, 0x5e, 22 | 0xe6, 0xf4, 0xc2, 0xd0, 0xae, 0xbc, 0x8a, 0x98, 23 | 0x76, 0x64, 0x52, 0x40, 0x3e, 0x2c, 0x1a, 0x08, 24 | 0xd4, 0xc6, 0xf0, 0xe2, 0x9c, 0x8e, 0xb8, 0xaa, 25 | 0x44, 0x56, 0x60, 0x72, 0x0c, 0x1e, 0x28, 0x3a, 26 | 0x4a, 0x58, 0x6e, 0x7c, 0x02, 0x10, 0x26, 0x34, 27 | 0xda, 0xc8, 0xfe, 0xec, 0x92, 0x80, 0xb6, 0xa4, 28 | 0x78, 0x6a, 0x5c, 0x4e, 0x30, 0x22, 0x14, 0x06, 29 | 0xe8, 0xfa, 0xcc, 0xde, 0xa0, 0xb2, 0x84, 0x96, 30 | 0x2e, 0x3c, 0x0a, 0x18, 0x66, 0x74, 0x42, 0x50, 31 | 0xbe, 0xac, 0x9a, 0x88, 0xf6, 0xe4, 0xd2, 0xc0, 32 | 0x1c, 0x0e, 0x38, 0x2a, 0x54, 0x46, 0x70, 0x62, 33 | 0x8c, 0x9e, 0xa8, 0xba, 0xc4, 0xd6, 0xe0, 0xf2 34 | }; 35 | -------------------------------------------------------------------------------- /src/rp2_common/pico_sd_card/sd_card.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | ; NOTE IT IS IMPERATIVE THAT YOU DON"T SCREW UP THE CLOCK EVEN FOR ONE HALF CYCLE, OTHERWISE THE DEVICE WILL LIKELY BE DISPLEASED AND MAY SEND GARBAGE (EVEN IF IT ISN"T DOING ANYTHING IMPORTANT WHEN YOU DO SO) 8 | 9 | .define sd_irq_num 7 10 | 11 | .program sd_clk 12 | .side_set 1 13 | .wrap_target 14 | irq sd_irq_num side 1 15 | irq clear sd_irq_num side 0 16 | .wrap 17 | 18 | .program sd_cmd_or_dat 19 | .origin 0 ; must load at zero (offsets are hardcoded in instruction stream) 20 | public no_arg_state_wait_high: ; this is a no arg state which means it must always appear in the second half of a word 21 | ; make sure pins are hi when we set output dir (note if we are 1 bit we'll be configured for 1 pin only, so sending 0b1111 is fine) 22 | set pins, 0b1111 23 | set pindirs, 0b1111 24 | 25 | public no_arg_state_waiting_for_cmd: 26 | out exec, 16 ; expected to be a jmp to a state 27 | 28 | public state_send_bits: 29 | out x, 16 30 | wait 0 irq sd_irq_num 31 | send_loop1: 32 | out pins, 1 33 | jmp x-- send_loop1 34 | 35 | public state_inline_instruction: 36 | out exec, 16 ; may be any instruction 37 | .wrap_target 38 | out exec, 16 ; expected to be a jmp to a state 39 | 40 | public state_receive_bits: 41 | out x, 16 42 | set pindirs, 0 43 | wait 1 pin, 0 44 | wait 0 pin, 0 45 | wait 0 irq sd_irq_num 46 | ; note we use wrap setup to configure receive bit/nibble transfers 47 | public wrap_for_4bit_receive: 48 | receive_loop1: 49 | in pins, 1 50 | jmp x-- receive_loop1 51 | .wrap 52 | 53 | ; #if INCLUDE_4BIT 54 | public wrap_target_for_4bit_receive: 55 | receive_loop4: 56 | in pins, 4 57 | jmp x-- receive_loop4 58 | out exec, 16 ; expected to be a jmp to a state 59 | ; #endif 60 | -------------------------------------------------------------------------------- /src/rp2_common/pico_sleep/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | pico_simple_hardware_target(sleep) 2 | pico_mirrored_target_link_libraries(hardware_sleep INTERFACE 3 | hardware_clocks 4 | hardware_rosc 5 | hardware_irq 6 | pico_aon_timer 7 | ) 8 | target_include_directories(hardware_sleep INTERFACE 9 | ${CMAKE_CURRENT_LIST_DIR}/include 10 | ) 11 | -------------------------------------------------------------------------------- /src/rp2_common/pico_sleep/include/pico/sleep.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _PICO_SLEEP_H_ 8 | #define _PICO_SLEEP_H_ 9 | 10 | #include "pico.h" 11 | #include "hardware/rosc.h" 12 | 13 | #include "pico/aon_timer.h" 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | /** \file sleep.h 20 | * \defgroup hardware_sleep hardware_sleep 21 | * 22 | * Lower Power Sleep API 23 | * 24 | * The difference between sleep and dormant is that ALL clocks are stopped in dormant mode, 25 | * until the source (either xosc or rosc) is started again by an external event. 26 | * In sleep mode some clocks can be left running controlled by the SLEEP_EN registers in the clocks 27 | * block. For example you could keep clk_rtc running. Some destinations (proc0 and proc1 wakeup logic) 28 | * can't be stopped in sleep mode otherwise there wouldn't be enough logic to wake up again. 29 | * 30 | * \subsection sleep_example Example 31 | * \addtogroup hardware_sleep 32 | * \include hello_sleep.c 33 | 34 | */ 35 | typedef enum { 36 | DORMANT_SOURCE_NONE, 37 | DORMANT_SOURCE_XOSC, 38 | DORMANT_SOURCE_ROSC, 39 | DORMANT_SOURCE_LPOSC, // rp2350 only 40 | } dormant_source_t; 41 | 42 | /*! \brief Set all clock sources to the the dormant clock source to prepare for sleep. 43 | * \ingroup hardware_sleep 44 | * 45 | * \param dormant_source The dormant clock source to use 46 | */ 47 | void sleep_run_from_dormant_source(dormant_source_t dormant_source); 48 | 49 | /*! \brief Set the dormant clock source to be the crystal oscillator 50 | * \ingroup hardware_sleep 51 | */ 52 | static inline void sleep_run_from_xosc(void) { 53 | sleep_run_from_dormant_source(DORMANT_SOURCE_XOSC); 54 | } 55 | 56 | #if !PICO_RP2040 57 | static inline void sleep_run_from_lposc(void) { 58 | sleep_run_from_dormant_source(DORMANT_SOURCE_LPOSC); 59 | } 60 | #endif 61 | 62 | /*! \brief Set the dormant clock source to be the ring oscillator 63 | * \ingroup hardware_sleep 64 | */ 65 | static inline void sleep_run_from_rosc(void) { 66 | sleep_run_from_dormant_source(DORMANT_SOURCE_ROSC); 67 | } 68 | 69 | /*! \brief Send system to sleep until the specified time 70 | * \ingroup hardware_sleep 71 | * 72 | * One of the sleep_run_* functions must be called prior to this call 73 | * 74 | * \param ts The time to wake up 75 | * \param callback Function to call on wakeup. 76 | */ 77 | void sleep_goto_sleep_until(struct timespec *ts, aon_timer_alarm_handler_t callback); 78 | 79 | /*! \brief Send system to sleep for a specified duration in milliseconds. This provides an alternative to sleep_goto_sleep_until 80 | to allow for shorter duration sleeps. 81 | * \ingroup hardware_sleep 82 | * 83 | * One of the sleep_run_* functions must be called prior to this call 84 | * 85 | * \param delay_ms The duration to sleep for in milliseconds. 86 | * \param callback Function to call on wakeup. 87 | * \return Returns true if the device went to sleep 88 | */ 89 | bool sleep_goto_sleep_for(uint32_t delay_ms, hardware_alarm_callback_t callback); 90 | 91 | /*! \brief Send system to dormant until the specified time, note for RP2040 the RTC must be driven by an external clock 92 | * \ingroup hardware_sleep 93 | * 94 | * One of the sleep_run_* functions must be called prior to this call 95 | * 96 | * \param ts The time to wake up 97 | * \param callback Function to call on wakeup. 98 | */ 99 | void sleep_goto_dormant_until(struct timespec *ts, aon_timer_alarm_handler_t callback); 100 | 101 | /*! \brief Send system to sleep until the specified GPIO changes 102 | * \ingroup hardware_sleep 103 | * 104 | * One of the sleep_run_* functions must be called prior to this call 105 | * 106 | * \param gpio_pin The pin to provide the wake up 107 | * \param edge true for leading edge, false for trailing edge 108 | * \param high true for active high, false for active low 109 | */ 110 | 111 | void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high); 112 | 113 | /*! \brief Send system to sleep until a leading high edge is detected on GPIO 114 | * \ingroup hardware_sleep 115 | * 116 | * One of the sleep_run_* functions must be called prior to this call 117 | * 118 | * \param gpio_pin The pin to provide the wake up 119 | */ 120 | static inline void sleep_goto_dormant_until_edge_high(uint gpio_pin) { 121 | sleep_goto_dormant_until_pin(gpio_pin, true, true); 122 | } 123 | 124 | /*! \brief Send system to sleep until a high level is detected on GPIO 125 | * \ingroup hardware_sleep 126 | * 127 | * One of the sleep_run_* functions must be called prior to this call 128 | * 129 | * \param gpio_pin The pin to provide the wake up 130 | */ 131 | static inline void sleep_goto_dormant_until_level_high(uint gpio_pin) { 132 | sleep_goto_dormant_until_pin(gpio_pin, false, true); 133 | } 134 | 135 | /*! \brief Reconfigure clocks to wake up properly from sleep/dormant mode 136 | * \ingroup hardware_sleep 137 | * 138 | * This must be called immediately after continuing execution when waking up from sleep/dormant mode 139 | * 140 | */ 141 | void sleep_power_up(void); 142 | 143 | #ifdef __cplusplus 144 | } 145 | #endif 146 | 147 | #endif 148 | -------------------------------------------------------------------------------- /src/rp2_common/pico_sleep/sleep.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include "pico.h" 11 | 12 | #include "pico/stdlib.h" 13 | #include "pico/sleep.h" 14 | 15 | #include "hardware/pll.h" 16 | #include "hardware/regs/clocks.h" 17 | #include "hardware/clocks.h" 18 | #include "hardware/watchdog.h" 19 | #include "hardware/xosc.h" 20 | #include "hardware/rosc.h" 21 | #include "hardware/regs/io_bank0.h" 22 | // For __wfi 23 | #include "hardware/sync.h" 24 | #include "pico/runtime_init.h" 25 | 26 | #ifdef __riscv 27 | #include "hardware/riscv.h" 28 | #else 29 | // For scb_hw so we can enable deep sleep 30 | #include "hardware/structs/scb.h" 31 | #endif 32 | 33 | #if !PICO_RP2040 34 | #include "hardware/powman.h" 35 | #endif 36 | 37 | // The difference between sleep and dormant is that ALL clocks are stopped in dormant mode, 38 | // until the source (either xosc or rosc) is started again by an external event. 39 | // In sleep mode some clocks can be left running controlled by the SLEEP_EN registers in the clocks 40 | // block. For example you could keep clk_rtc running. Some destinations (proc0 and proc1 wakeup logic) 41 | // can't be stopped in sleep mode otherwise there wouldn't be enough logic to wake up again. 42 | 43 | static dormant_source_t _dormant_source; 44 | 45 | bool dormant_source_valid(dormant_source_t dormant_source) 46 | { 47 | switch (dormant_source) { 48 | case DORMANT_SOURCE_XOSC: 49 | return true; 50 | case DORMANT_SOURCE_ROSC: 51 | return true; 52 | #if !PICO_RP2040 53 | case DORMANT_SOURCE_LPOSC: 54 | return true; 55 | #endif 56 | default: 57 | return false; 58 | } 59 | } 60 | 61 | // In order to go into dormant mode we need to be running from a stoppable clock source: 62 | // either the xosc or rosc with no PLLs running. This means we disable the USB and ADC clocks 63 | // and all PLLs 64 | void sleep_run_from_dormant_source(dormant_source_t dormant_source) { 65 | assert(dormant_source_valid(dormant_source)); 66 | _dormant_source = dormant_source; 67 | 68 | uint src_hz; 69 | uint clk_ref_src; 70 | switch (dormant_source) { 71 | case DORMANT_SOURCE_XOSC: 72 | src_hz = XOSC_HZ; 73 | clk_ref_src = CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC; 74 | break; 75 | case DORMANT_SOURCE_ROSC: 76 | src_hz = 6500 * KHZ; // todo 77 | clk_ref_src = CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH; 78 | break; 79 | #if !PICO_RP2040 80 | case DORMANT_SOURCE_LPOSC: 81 | src_hz = 32 * KHZ; 82 | clk_ref_src = CLOCKS_CLK_REF_CTRL_SRC_VALUE_LPOSC_CLKSRC; 83 | break; 84 | #endif 85 | default: 86 | hard_assert(false); 87 | } 88 | 89 | // CLK_REF = XOSC or ROSC 90 | clock_configure(clk_ref, 91 | clk_ref_src, 92 | 0, // No aux mux 93 | src_hz, 94 | src_hz); 95 | 96 | // CLK SYS = CLK_REF 97 | clock_configure(clk_sys, 98 | CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF, 99 | 0, // Using glitchless mux 100 | src_hz, 101 | src_hz); 102 | 103 | // CLK ADC = 0MHz 104 | clock_stop(clk_adc); 105 | clock_stop(clk_usb); 106 | #if PICO_RP2350 107 | clock_stop(clk_hstx); 108 | #endif 109 | 110 | #if PICO_RP2040 111 | // CLK RTC = ideally XOSC (12MHz) / 256 = 46875Hz but could be rosc 112 | uint clk_rtc_src = (dormant_source == DORMANT_SOURCE_XOSC) ? 113 | CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC : 114 | CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH; 115 | 116 | clock_configure(clk_rtc, 117 | 0, // No GLMUX 118 | clk_rtc_src, 119 | src_hz, 120 | 46875); 121 | #endif 122 | 123 | // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable 124 | clock_configure(clk_peri, 125 | 0, 126 | CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, 127 | src_hz, 128 | src_hz); 129 | 130 | pll_deinit(pll_sys); 131 | pll_deinit(pll_usb); 132 | 133 | // Assuming both xosc and rosc are running at the moment 134 | if (dormant_source == DORMANT_SOURCE_XOSC) { 135 | // Can disable rosc 136 | rosc_disable(); 137 | } else { 138 | // Can disable xosc 139 | xosc_disable(); 140 | } 141 | 142 | // Reconfigure uart with new clocks 143 | setup_default_uart(); 144 | } 145 | 146 | static void processor_deep_sleep(void) { 147 | // Enable deep sleep at the proc 148 | #ifdef __riscv 149 | uint32_t bits = RVCSR_MSLEEP_POWERDOWN_BITS; 150 | if (!get_core_num()) { 151 | bits |= RVCSR_MSLEEP_DEEPSLEEP_BITS; 152 | } 153 | riscv_set_csr(RVCSR_MSLEEP_OFFSET, bits); 154 | #else 155 | scb_hw->scr |= ARM_CPU_PREFIXED(SCR_SLEEPDEEP_BITS); 156 | #endif 157 | } 158 | 159 | void sleep_goto_sleep_until(struct timespec *ts, aon_timer_alarm_handler_t callback) 160 | { 161 | 162 | // We should have already called the sleep_run_from_dormant_source function 163 | // This is only needed for dormancy although it saves power running from xosc while sleeping 164 | //assert(dormant_source_valid(_dormant_source)); 165 | 166 | #if PICO_RP2040 167 | clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS; 168 | clocks_hw->sleep_en1 = 0x0; 169 | #else 170 | clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_REF_POWMAN_BITS; 171 | clocks_hw->sleep_en1 = 0x0; 172 | #endif 173 | 174 | aon_timer_enable_alarm(ts, callback, false); 175 | 176 | stdio_flush(); 177 | 178 | // Enable deep sleep at the proc 179 | processor_deep_sleep(); 180 | 181 | // Go to sleep 182 | __wfi(); 183 | } 184 | 185 | bool sleep_goto_sleep_for(uint32_t delay_ms, hardware_alarm_callback_t callback) 186 | { 187 | // We should have already called the sleep_run_from_dormant_source function 188 | // This is only needed for dormancy although it saves power running from xosc while sleeping 189 | //assert(dormant_source_valid(_dormant_source)); 190 | 191 | // Turn off all clocks except for the timer 192 | clocks_hw->sleep_en0 = 0x0; 193 | #if PICO_RP2040 194 | clocks_hw->sleep_en1 = CLOCKS_SLEEP_EN1_CLK_SYS_TIMER_BITS; 195 | #elif PICO_RP2350 196 | clocks_hw->sleep_en1 = CLOCKS_SLEEP_EN1_CLK_REF_TICKS_BITS | CLOCKS_SLEEP_EN1_CLK_SYS_TIMER0_BITS; 197 | #else 198 | #error Unknown processor 199 | #endif 200 | 201 | int alarm_num = hardware_alarm_claim_unused(true); 202 | hardware_alarm_set_callback(alarm_num, callback); 203 | absolute_time_t t = make_timeout_time_ms(delay_ms); 204 | if (hardware_alarm_set_target(alarm_num, t)) { 205 | hardware_alarm_set_callback(alarm_num, NULL); 206 | hardware_alarm_unclaim(alarm_num); 207 | return false; 208 | } 209 | 210 | stdio_flush(); 211 | 212 | // Enable deep sleep at the proc 213 | processor_deep_sleep(); 214 | 215 | // Go to sleep 216 | __wfi(); 217 | return true; 218 | } 219 | 220 | static void _go_dormant(void) { 221 | assert(dormant_source_valid(_dormant_source)); 222 | 223 | if (_dormant_source == DORMANT_SOURCE_XOSC) { 224 | xosc_dormant(); 225 | } else { 226 | rosc_set_dormant(); 227 | } 228 | } 229 | 230 | void sleep_goto_dormant_until(struct timespec *ts, aon_timer_alarm_handler_t callback) { 231 | // We should have already called the sleep_run_from_dormant_source function 232 | 233 | #if PICO_RP2040 234 | clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS; 235 | clocks_hw->sleep_en1 = 0x0; 236 | #else 237 | assert(_dormant_source == DORMANT_SOURCE_LPOSC); 238 | uint64_t restore_ms = powman_timer_get_ms(); 239 | powman_timer_set_1khz_tick_source_lposc(); 240 | powman_timer_set_ms(restore_ms); 241 | 242 | clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_REF_POWMAN_BITS; 243 | clocks_hw->sleep_en1 = 0x0; 244 | #endif 245 | 246 | // Set the AON timer to wake up the proc from dormant mode 247 | aon_timer_enable_alarm(ts, callback, true); 248 | 249 | stdio_flush(); 250 | 251 | // Enable deep sleep at the proc 252 | processor_deep_sleep(); 253 | 254 | // Go dormant 255 | _go_dormant(); 256 | } 257 | 258 | void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high) { 259 | bool low = !high; 260 | bool level = !edge; 261 | 262 | // Configure the appropriate IRQ at IO bank 0 263 | assert(gpio_pin < NUM_BANK0_GPIOS); 264 | 265 | uint32_t event = 0; 266 | 267 | if (level && low) event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_LOW_BITS; 268 | if (level && high) event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_HIGH_BITS; 269 | if (edge && high) event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_HIGH_BITS; 270 | if (edge && low) event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_LOW_BITS; 271 | 272 | gpio_init(gpio_pin); 273 | gpio_set_input_enabled(gpio_pin, true); 274 | gpio_set_dormant_irq_enabled(gpio_pin, event, true); 275 | 276 | _go_dormant(); 277 | // Execution stops here until woken up 278 | 279 | // Clear the irq so we can go back to dormant mode again if we want 280 | gpio_acknowledge_irq(gpio_pin, event); 281 | gpio_set_input_enabled(gpio_pin, false); 282 | } 283 | 284 | // To be called after waking up from sleep/dormant mode to restore system clocks properly 285 | void sleep_power_up(void) 286 | { 287 | // Re-enable the ring oscillator, which will essentially kickstart the proc 288 | rosc_enable(); 289 | 290 | // Reset the sleep enable register so peripherals and other hardware can be used 291 | clocks_hw->sleep_en0 |= ~(0u); 292 | clocks_hw->sleep_en1 |= ~(0u); 293 | 294 | // Restore all clocks 295 | clocks_init(); 296 | 297 | #if PICO_RP2350 298 | // make powerman use xosc again 299 | uint64_t restore_ms = powman_timer_get_ms(); 300 | powman_timer_set_1khz_tick_source_xosc(); 301 | powman_timer_set_ms(restore_ms); 302 | #endif 303 | 304 | // UART needs to be reinitialised with the new clock frequencies for stable output 305 | setup_default_uart(); 306 | } 307 | -------------------------------------------------------------------------------- /src/rp2_common/usb_common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(usb_common INTERFACE) 2 | 3 | target_include_directories(usb_common INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) -------------------------------------------------------------------------------- /src/rp2_common/usb_common/include/usb/usb_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _USB_USB_COMMON_H 8 | #define _USB_USB_COMMON_H 9 | 10 | #include "pico/types.h" 11 | #include "hardware/structs/usb.h" 12 | 13 | // bmRequestType bit definitions 14 | #define USB_REQ_TYPE_STANDARD 0x00u 15 | #define USB_REQ_TYPE_TYPE_MASK 0x60u 16 | #define USB_REQ_TYPE_TYPE_CLASS 0x20u 17 | #define USB_REQ_TYPE_TYPE_VENDOR 0x40u 18 | 19 | #define USB_REQ_TYPE_RECIPIENT_MASK 0x1fu 20 | #define USB_REQ_TYPE_RECIPIENT_DEVICE 0x00u 21 | #define USB_REQ_TYPE_RECIPIENT_INTERFACE 0x01u 22 | #define USB_REQ_TYPE_RECIPIENT_ENDPOINT 0x02u 23 | 24 | #define USB_DIR_OUT 0x00u 25 | #define USB_DIR_IN 0x80u 26 | 27 | #define USB_TRANSFER_TYPE_CONTROL 0x0 28 | #define USB_TRANSFER_TYPE_ISOCHRONOUS 0x1 29 | #define USB_TRANSFER_TYPE_BULK 0x2 30 | #define USB_TRANSFER_TYPE_INTERRUPT 0x3 31 | #define USB_TRANSFER_TYPE_BITS 0x3 32 | 33 | // Descriptor types 34 | #define USB_DT_DEVICE 0x01 35 | #define USB_DT_CONFIG 0x02 36 | #define USB_DT_STRING 0x03 37 | #define USB_DT_INTERFACE 0x04 38 | #define USB_DT_ENDPOINT 0x05 39 | 40 | #define USB_REQUEST_GET_STATUS 0x0 41 | #define USB_REQUEST_CLEAR_FEATURE 0x01 42 | #define USB_REQUEST_SET_FEATURE 0x03 43 | #define USB_REQUEST_SET_ADDRESS 0x05 44 | #define USB_REQUEST_GET_DESCRIPTOR 0x06 45 | #define USB_REQUEST_SET_DESCRIPTOR 0x07 46 | #define USB_REQUEST_GET_CONFIGURATION 0x08 47 | #define USB_REQUEST_SET_CONFIGURATION 0x09 48 | #define USB_REQUEST_GET_INTERFACE 0x0a 49 | #define USB_REQUEST_SET_INTERFACE 0x0b 50 | #define USB_REQUEST_SYNC_FRAME 0x0c 51 | 52 | #define USB_REQUEST_MSC_GET_MAX_LUN 0xfe 53 | #define USB_REQUEST_MSC_RESET 0xff 54 | 55 | #define USB_FEAT_ENDPOINT_HALT 0x00 56 | #define USB_FEAT_DEVICE_REMOTE_WAKEUP 0x01 57 | #define USB_FEAT_TEST_MODE 0x02 58 | 59 | #define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05 60 | 61 | struct usb_setup_packet { 62 | uint8_t bmRequestType; 63 | uint8_t bRequest; 64 | uint16_t wValue; 65 | uint16_t wIndex; 66 | uint16_t wLength; 67 | } __packed; 68 | 69 | struct usb_descriptor { 70 | uint8_t bLength; 71 | uint8_t bDescriptorType; 72 | }; 73 | 74 | struct usb_device_descriptor { 75 | uint8_t bLength; 76 | uint8_t bDescriptorType; 77 | uint16_t bcdUSB; 78 | uint8_t bDeviceClass; 79 | uint8_t bDeviceSubClass; 80 | uint8_t bDeviceProtocol; 81 | uint8_t bMaxPacketSize0; 82 | uint16_t idVendor; 83 | uint16_t idProduct; 84 | uint16_t bcdDevice; 85 | uint8_t iManufacturer; 86 | uint8_t iProduct; 87 | uint8_t iSerialNumber; 88 | uint8_t bNumConfigurations; 89 | } __packed; 90 | 91 | struct usb_configuration_descriptor { 92 | uint8_t bLength; 93 | uint8_t bDescriptorType; 94 | uint16_t wTotalLength; 95 | uint8_t bNumInterfaces; 96 | uint8_t bConfigurationValue; 97 | uint8_t iConfiguration; 98 | uint8_t bmAttributes; 99 | uint8_t bMaxPower; 100 | } __packed; 101 | 102 | struct usb_interface_descriptor { 103 | uint8_t bLength; 104 | uint8_t bDescriptorType; 105 | uint8_t bInterfaceNumber; 106 | uint8_t bAlternateSetting; 107 | uint8_t bNumEndpoints; 108 | uint8_t bInterfaceClass; 109 | uint8_t bInterfaceSubClass; 110 | uint8_t bInterfaceProtocol; 111 | uint8_t iInterface; 112 | } __packed; 113 | 114 | struct usb_endpoint_descriptor { 115 | uint8_t bLength; 116 | uint8_t bDescriptorType; 117 | uint8_t bEndpointAddress; 118 | uint8_t bmAttributes; 119 | uint16_t wMaxPacketSize; 120 | uint8_t bInterval; 121 | } __packed; 122 | 123 | struct usb_endpoint_descriptor_long { 124 | uint8_t bLength; 125 | uint8_t bDescriptorType; 126 | uint8_t bEndpointAddress; 127 | uint8_t bmAttributes; 128 | uint16_t wMaxPacketSize; 129 | uint8_t bInterval; 130 | uint8_t bRefresh; 131 | uint8_t bSyncAddr; 132 | } __attribute__((packed)); 133 | 134 | #endif -------------------------------------------------------------------------------- /src/rp2_common/usb_device/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(usb_device INTERFACE) 2 | 3 | target_sources(usb_device INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/usb_device.c 5 | ${CMAKE_CURRENT_LIST_DIR}/usb_stream_helper.c 6 | ) 7 | 8 | target_include_directories(usb_device INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 9 | target_link_libraries(usb_device INTERFACE usb_common hardware_irq hardware_dma hardware_pio pico_fix_rp2040_usb_device_enumeration) -------------------------------------------------------------------------------- /src/rp2_common/usb_device/include/pico/usb_device.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _USB_USB_DEVICE_H 8 | #define _USB_USB_DEVICE_H 9 | 10 | #include "usb/usb_common.h" 11 | #include "pico/assert.h" 12 | 13 | #ifndef PICO_USBDEV_ENABLE_DEBUG_TRACE 14 | #define PICO_USBDEV_ENABLE_DEBUG_TRACE 0 15 | #endif 16 | 17 | #ifndef PICO_USBDEV_ASSUME_ZERO_INIT 18 | #define PICO_USBDEV_ASSUME_ZERO_INIT 0 19 | #endif 20 | 21 | #ifndef PICO_USBDEV_MAX_ENDPOINTS 22 | #define PICO_USBDEV_MAX_ENDPOINTS USB_NUM_ENDPOINTS 23 | #endif 24 | static_assert(PICO_USBDEV_MAX_ENDPOINTS >= 1 && PICO_USBDEV_MAX_ENDPOINTS <= 16, ""); 25 | 26 | #ifndef PICO_USBDEV_MAX_DESCRIPTOR_SIZE 27 | #define PICO_USBDEV_MAX_DESCRIPTOR_SIZE 64 28 | #endif 29 | 30 | // Enabling configuration items can reduce the size of the runtime code at the cost of some functionality 31 | // or improve speed at the cost of some flexibility 32 | 33 | // no custom per device setup packet handler 34 | #ifndef PICO_USBDEV_NO_DEVICE_SETUP_HANDLER 35 | #define PICO_USBDEV_NO_DEVICE_SETUP_HANDLER 0 36 | #endif 37 | 38 | // no custom per endpoint setup packet handlers 39 | #ifndef PICO_USBDEV_NO_ENDPOINT_SETUP_HANDLER 40 | #define PICO_USBDEV_NO_ENDPOINT_SETUP_HANDLER 0 41 | #endif 42 | 43 | // if all endpoints are bulk, then it allows simplification of some code 44 | #ifndef PICO_USBDEV_BULK_ONLY_EP1_THRU_16 45 | #define PICO_USBDEV_BULK_ONLY_EP1_THRU_16 0 46 | #endif 47 | 48 | // our interfaces are zero based number in the order they appear on the device - require that 49 | #ifndef PICO_USBDEV_USE_ZERO_BASED_INTERFACES 50 | #define PICO_USBDEV_USE_ZERO_BASED_INTERFACES 0 51 | #endif 52 | 53 | // no_init method for transfer 54 | #ifndef PICO_USBDEV_NO_TRANSFER_ON_INIT_METHOD 55 | #define PICO_USBDEV_NO_TRANSFER_ON_INIT_METHOD 0 56 | #endif 57 | 58 | // do on_cancel method for transfer 59 | #ifndef PICO_USBDEV_NO_TRANSFER_ON_CANCEL_METHOD 60 | #define PICO_USBDEV_NO_TRANSFER_ON_CANCEL_METHOD 0 61 | #endif 62 | 63 | // todo this needs to be part of configuration 64 | #ifndef PICO_USBDEV_NO_INTERFACE_ALTERNATES 65 | #define PICO_USBDEV_NO_INTERFACE_ALTERNATES 0 66 | #endif 67 | 68 | // todo this needs to be part of configuration 69 | #ifndef PICO_USBDEV_ISOCHRONOUS_BUFFER_STRIDE_TYPE 70 | #define PICO_USBDEV_ISOCHRONOUS_BUFFER_STRIDE_TYPE 0 71 | #endif 72 | 73 | static_assert((PICO_USBDEV_ISOCHRONOUS_BUFFER_STRIDE_TYPE >= 0) && (PICO_USBDEV_ISOCHRONOUS_BUFFER_STRIDE_TYPE < 4), 74 | ""); 75 | 76 | // don't zero out most structures (since we do so globablly for BSS) 77 | //#define USB_SKIP_COMMON_INIT 78 | 79 | // only 16 bytes saved to not set a sense code 80 | //#define USB_SILENT_FAIL_ON_EXCLUSIVE 81 | 82 | struct usb_transfer; 83 | struct usb_endpoint; 84 | 85 | typedef void (*usb_transfer_func)(struct usb_endpoint *ep); 86 | typedef void (*usb_transfer_completed_func)(struct usb_endpoint *ep, struct usb_transfer *transfer); 87 | 88 | struct usb_buffer { 89 | uint8_t *data; 90 | uint8_t data_len; 91 | uint8_t data_max; 92 | // then... 93 | bool valid; // aka user owned 94 | }; 95 | 96 | #include "usb_device_private.h" 97 | 98 | struct usb_transfer_type { 99 | // for IN transfers this is called to setup new packet buffers 100 | // for OUT transfers this is called with packet data 101 | // 102 | // In any case usb_packet_done must be called if this function has handled the buffer 103 | usb_transfer_func on_packet; 104 | #if !PICO_USBDEV_NO_TRANSFER_ON_INIT_METHOD 105 | usb_transfer_func on_init; 106 | #endif 107 | #if !PICO_USBDEV_NO_TRANSFER_ON_CANCEL_METHOD 108 | usb_transfer_func on_cancel; 109 | #endif 110 | uint8_t initial_packet_count; 111 | }; 112 | 113 | struct usb_interface *usb_interface_init(struct usb_interface *interface, const struct usb_interface_descriptor *desc, 114 | struct usb_endpoint *const *endpoints, uint endpoint_count, 115 | bool double_buffered); 116 | struct usb_device *usb_device_init(const struct usb_device_descriptor *desc, 117 | const struct usb_configuration_descriptor *config_desc, 118 | struct usb_interface *const *interfaces, uint interface_count, 119 | const char *(*get_descriptor_string)(uint index)); 120 | 121 | void usb_device_start(); 122 | void usb_device_stop(); 123 | 124 | // explicit stall 125 | void usb_halt_endpoint_on_condition(struct usb_endpoint *ep); 126 | void usb_halt_endpoint(struct usb_endpoint *endpoint); 127 | void usb_clear_halt_condition(struct usb_endpoint *ep); 128 | static inline bool usb_is_endpoint_stalled(struct usb_endpoint *endpoint); 129 | void usb_set_default_transfer(struct usb_endpoint *ep, struct usb_transfer *transfer); 130 | void usb_reset_transfer(struct usb_transfer *transfer, const struct usb_transfer_type *type, 131 | usb_transfer_completed_func on_complete); 132 | void usb_start_transfer(struct usb_endpoint *ep, struct usb_transfer *transfer); 133 | void usb_reset_and_start_transfer(struct usb_endpoint *ep, struct usb_transfer *transfer, 134 | const struct usb_transfer_type *type, usb_transfer_completed_func on_complete); 135 | void usb_chain_transfer(struct usb_endpoint *ep, struct usb_transfer *transfer); 136 | void usb_grow_transfer(struct usb_transfer *transfer, uint packet_count); 137 | void usb_start_default_transfer_if_not_already_running_or_halted(struct usb_endpoint *ep); 138 | 139 | typedef void (*usb_transfer_func)(struct usb_endpoint *ep); 140 | 141 | struct usb_buffer *usb_current_in_packet_buffer(struct usb_endpoint *ep); 142 | struct usb_buffer *usb_current_out_packet_buffer(struct usb_endpoint *ep); 143 | uint8_t *usb_get_single_packet_response_buffer(struct usb_endpoint *ep, uint len); 144 | 145 | // call during (or asynchronously after) on_packet to mark the packet as done 146 | void usb_packet_done(struct usb_endpoint *ep); 147 | 148 | extern const struct usb_transfer_type usb_current_packet_only_transfer_type; 149 | 150 | static inline struct usb_endpoint *usb_get_control_in_endpoint(); 151 | static inline struct usb_endpoint *usb_get_control_out_endpoint(); 152 | 153 | void usb_start_empty_control_in_transfer(usb_transfer_completed_func on_complete); 154 | void usb_start_empty_control_in_transfer_null_completion(); 155 | void usb_start_tiny_control_in_transfer(uint32_t data, uint len); 156 | void usb_start_single_buffer_control_in_transfer(); 157 | void usb_start_control_out_transfer(const struct usb_transfer_type *type); 158 | void usb_start_empty_transfer(struct usb_endpoint *endpoint, struct usb_transfer *transfer, 159 | usb_transfer_completed_func on_complete); 160 | 161 | void usb_soft_reset_endpoint(struct usb_endpoint *ep); 162 | void usb_hard_reset_endpoint(struct usb_endpoint *ep); 163 | 164 | #if PICO_USBDEV_ENABLE_DEBUG_TRACE 165 | void usb_dump_trace(void); 166 | void usb_reset_trace(void); 167 | #else 168 | 169 | static inline void usb_dump_trace() {} 170 | 171 | static inline void usb_reset_trace() {} 172 | 173 | #endif 174 | 175 | #define usb_warn(format, args...) ({printf("WARNING: "); printf(format, ## args); }) 176 | #if false && !defined(NDEBUG) 177 | #define usb_debug(format,args...) printf(format, ## args) 178 | #else 179 | #define usb_debug(format, ...) ((void)0) 180 | #endif 181 | 182 | #if false && !defined(NDEBUG) 183 | #define usb_trace(format,args...) printf(format, ## args) 184 | #else 185 | #define usb_trace(format, ...) ((void)0) 186 | #endif 187 | 188 | #endif -------------------------------------------------------------------------------- /src/rp2_common/usb_device/include/pico/usb_device_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _USB_DEVICE_PRIVATE_H 8 | #define _USB_DEVICE_PRIVATE_H 9 | 10 | #include "usb/usb_common.h" 11 | #include 12 | 13 | struct usb_transfer { 14 | // prototype 15 | const struct usb_transfer_type *type; 16 | usb_transfer_completed_func on_complete; 17 | // total number of buffers (packets) that still need to be handed over to the hardware 18 | // during the remaining course of the transfer (with data for IN, empty for data for out) 19 | uint32_t remaining_packets_to_submit; 20 | // total number of buffers when we will expect to receive IRQ/handle_buffer for during 21 | // the remaining course of the transfer 22 | uint32_t remaining_packets_to_handle; 23 | #ifdef GENERAL_SIZE_HACKS 24 | union { 25 | struct { 26 | #endif 27 | // number of packets which require usb_packet_done() 28 | bool outstanding_packet; 29 | // received a packet which we couldn't deliver because there was one outstanding 30 | bool packet_queued; 31 | bool started; 32 | bool completed; 33 | #ifdef GENERAL_SIZE_HACKS 34 | }; 35 | uint32_t all_flags; 36 | }; 37 | #endif 38 | }; 39 | 40 | struct usb_interface { 41 | const struct usb_interface_descriptor *descriptor; 42 | struct usb_endpoint *const *endpoints; 43 | uint8_t endpoint_count; 44 | bool (*setup_request_handler)(struct usb_interface *interface, struct usb_setup_packet *setup); 45 | #if !PICO_USBDEV_NO_INTERFACE_ALTERNATES 46 | bool (*set_alternate_handler)(struct usb_interface *interface, uint alt); 47 | uint8_t alt; 48 | #endif 49 | }; 50 | 51 | struct usb_configuration { 52 | const struct usb_configuration_descriptor *descriptor; 53 | struct usb_interface *const *interfaces; 54 | #ifndef PICO_USBDEV_FIXED_INTERFACE_COUNT 55 | uint8_t interface_count; 56 | #endif 57 | }; 58 | #ifdef PICO_USBDEV_FIXED_INTERFACE_COUNT 59 | #define _usb_interface_count(config) PICO_USBDEV_FIXED_INTERFACE_COUNT 60 | #else 61 | #define _usb_interface_count(config) config->interface_count 62 | #endif 63 | 64 | struct usb_device { 65 | const struct usb_device_descriptor *descriptor; 66 | #if !PICO_USBDEV_NO_DEVICE_SETUP_HANDLER 67 | bool (*setup_request_handler)(struct usb_device *dev, struct usb_setup_packet *setup); 68 | #endif 69 | void (*on_configure)(struct usb_device *dev, bool configured); 70 | const char *(*get_descriptor_string)(uint index); 71 | // only support one config for now 72 | struct usb_configuration config; 73 | uint8_t current_address; // 0 if unaddressed 74 | uint8_t current_config_num; // 0 if unconfigured 75 | uint8_t pending_address; // address to set on completion of SET_ADDRESS CSW 76 | uint16_t next_buffer_offset; 77 | // bool started; 78 | }; 79 | 80 | enum usb_halt_state { 81 | HS_NONE = 0, 82 | HS_NON_HALT_STALL = 1, // just stalled 83 | HS_HALTED = 2, // halted or command halted 84 | HS_HALTED_ON_CONDITION = 3 // halted that cannot be simply cleared by CLEAR_FEATURE 85 | }; 86 | 87 | struct usb_endpoint { 88 | const struct usb_endpoint_descriptor *descriptor; 89 | struct usb_transfer *default_transfer; 90 | struct usb_transfer *current_transfer; 91 | struct usb_transfer *chain_transfer; 92 | void (*on_stall_change)(struct usb_endpoint *ep); 93 | #if !PICO_USBDEV_NO_ENDPOINT_SETUP_HANDLER 94 | bool (*setup_request_handler)(struct usb_endpoint *ep, struct usb_setup_packet *setup); 95 | #endif 96 | uint16_t dpram_buffer_offset; 97 | uint16_t buffer_size; // for an individual buffer 98 | struct usb_buffer current_hw_buffer; 99 | #if !PICO_USBDEV_BULK_ONLY_EP1_THRU_16 100 | uint16_t buffer_stride; 101 | #endif 102 | uint8_t num; 103 | uint8_t next_pid; 104 | uint8_t buffer_bit_index; 105 | uint8_t owned_buffer_count; 106 | uint8_t current_give_buffer; 107 | uint8_t current_take_buffer; 108 | uint8_t halt_state; 109 | bool first_buffer_after_reset; 110 | bool double_buffered; 111 | bool in; 112 | }; 113 | 114 | static inline uint usb_endpoint_number(struct usb_endpoint *ep) { 115 | assert(ep); 116 | return ep->descriptor ? ep->descriptor->bEndpointAddress & 0xfu : 0; 117 | } 118 | 119 | static inline bool usb_is_endpoint_stalled(struct usb_endpoint *endpoint) { 120 | return endpoint->halt_state != HS_NONE; 121 | } 122 | 123 | const char *usb_endpoint_dir_string(struct usb_endpoint *ep); 124 | 125 | static inline struct usb_endpoint *usb_get_control_in_endpoint() { 126 | extern struct usb_endpoint usb_control_in; 127 | return &usb_control_in; 128 | } 129 | 130 | static inline struct usb_endpoint *usb_get_control_out_endpoint() { 131 | extern struct usb_endpoint usb_control_out; 132 | return &usb_control_out; 133 | } 134 | 135 | #endif //_USB_DEVICE_PRIVATE_H 136 | -------------------------------------------------------------------------------- /src/rp2_common/usb_device/include/pico/usb_stream_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _USB_STREAM_HELPER_H 8 | #define _USB_STREAM_HELPER_H 9 | 10 | #include "usb_device.h" 11 | 12 | struct usb_transfer_funcs; 13 | 14 | struct usb_stream_transfer { 15 | struct usb_transfer core; 16 | uint32_t offset; // offset within the stream 17 | uint32_t transfer_length; 18 | uint32_t chunk_size; 19 | uint8_t *chunk_buffer; 20 | struct usb_endpoint *ep; 21 | const struct usb_stream_transfer_funcs *funcs; 22 | #ifndef NDEBUG 23 | bool packet_handler_complete_expected; 24 | #endif 25 | }; 26 | 27 | typedef void (*stream_on_packet_complete_function)(struct usb_stream_transfer *transfer); 28 | typedef bool (*stream_on_chunk_function)(uint32_t chunk_len, 29 | struct usb_stream_transfer *transfer); 30 | 31 | struct usb_stream_transfer_funcs { 32 | stream_on_packet_complete_function on_packet_complete; 33 | // returns whether processing async 34 | stream_on_chunk_function on_chunk; 35 | }; 36 | 37 | void usb_stream_setup_transfer(struct usb_stream_transfer *transfer, const struct usb_stream_transfer_funcs *funcs, 38 | uint8_t *chunk_buffer, uint32_t chunk_size, uint32_t transfer_length, 39 | usb_transfer_completed_func on_complete); 40 | 41 | void usb_stream_chunk_done(struct usb_stream_transfer *transfer); 42 | 43 | void usb_stream_noop_on_packet_complete(struct usb_stream_transfer *transfer); 44 | bool usb_stream_noop_on_chunk(uint32_t chunk_len, struct usb_stream_transfer *transfer); 45 | #endif //SOFTWARE_USB_STREAM_HELPER_H 46 | -------------------------------------------------------------------------------- /src/rp2_common/usb_device/usb_stream_helper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include "pico/usb_stream_helper.h" 10 | 11 | static uint32_t _usb_stream_chunk_offset(struct usb_stream_transfer *transfer) { 12 | return transfer->offset & (transfer->chunk_size - 1); 13 | } 14 | 15 | void usb_stream_packet_handler_complete(struct usb_stream_transfer *transfer) { 16 | struct usb_buffer *buffer; 17 | struct usb_endpoint *ep = transfer->ep; 18 | #ifndef NDEBUG 19 | assert(transfer->packet_handler_complete_expected); 20 | transfer->packet_handler_complete_expected = false; 21 | #endif 22 | assert(ep); 23 | if (ep->in) { 24 | buffer = usb_current_in_packet_buffer(ep); 25 | assert(buffer); 26 | assert(buffer->data_max == 64); 27 | uint chunk_offset = _usb_stream_chunk_offset(transfer); 28 | uint data_len = 64; 29 | if (transfer->offset + 64 > transfer->transfer_length) { 30 | data_len = transfer->transfer_length - transfer->offset; 31 | } 32 | buffer->data_len = data_len; 33 | memcpy(buffer->data, transfer->chunk_buffer + chunk_offset, data_len); 34 | } else { 35 | buffer = usb_current_out_packet_buffer(ep); 36 | assert(buffer); 37 | assert(buffer->data_len); 38 | } 39 | transfer->offset += buffer->data_len; 40 | if (ep->num > 2) usb_debug(" %d transfer_offset %d\n", ep->num, (uint) transfer->offset); 41 | assert(transfer->funcs && transfer->funcs->on_packet_complete); 42 | transfer->funcs->on_packet_complete(transfer); 43 | #ifdef USE_BOOTROM_GPIO 44 | gpio_clr_mask(usb_activity_gpio_pin_mask); 45 | #endif 46 | usb_packet_done(ep); 47 | } 48 | 49 | void usb_stream_chunk_done(struct usb_stream_transfer *transfer) { 50 | usb_stream_packet_handler_complete(transfer); 51 | } 52 | 53 | void _usb_stream_packet_packet_handler(struct usb_endpoint *ep) { 54 | #ifdef USE_BOOTROM_GPIO 55 | gpio_set_mask(usb_activity_gpio_pin_mask); 56 | #endif 57 | // todo assert type 58 | struct usb_stream_transfer *transfer = (struct usb_stream_transfer *) ep->current_transfer; 59 | uint chunk_offset = _usb_stream_chunk_offset(transfer); 60 | uint chunk_len = 0; // set to non zero to call on_chunk 61 | if (ep->in) { 62 | if (!chunk_offset) { 63 | // we are at the beginning of a chunk so want to call on_chunk 64 | chunk_len = (transfer->offset + transfer->chunk_size) > transfer->transfer_length ? 65 | transfer->transfer_length - transfer->offset : transfer->chunk_size; 66 | if (ep->num > 2) 67 | usb_warn("chunko %d len %05x offset %08x size %04x transfer %08x\n", ep->num, chunk_len, chunk_offset, 68 | (uint) transfer->chunk_size, (uint) transfer->transfer_length); 69 | } 70 | } else { 71 | // usb_debug("write packet %04x %d\n", (uint)transfer->offset, ep->current_take_buffer); 72 | struct usb_buffer *buffer = usb_current_out_packet_buffer(ep); 73 | assert(buffer); 74 | // note we only set chunk_len if this is the end of a chunk 75 | if (transfer->offset + 64 >= transfer->transfer_length) { 76 | // we have ended the transfer (possibly mid-chunk) 77 | chunk_len = transfer->transfer_length & (transfer->chunk_size - 1); 78 | if (chunk_len) { 79 | usb_warn(">> Truncated %08x\n", chunk_len); 80 | } else { 81 | chunk_len = transfer->chunk_size; 82 | } 83 | } else if (chunk_offset + 64 >= transfer->chunk_size) { 84 | // end of regular chunk 85 | chunk_len = transfer->chunk_size; 86 | } 87 | assert(chunk_len || buffer->data_len == 64); 88 | // if (!(!chunk_len || buffer->data_len == ((chunk_len & 63u) ? (chunk_len & 63u) : 64u))) { 89 | // usb_warn("ooh off=%08x len=%08x chunk_off=%04x chunk_len=%04x data_len=%04x\n", (uint)transfer->offset, (uint)transfer->transfer_length, chunk_offset, chunk_len, buffer->data_len); 90 | // } 91 | assert(!chunk_len || buffer->data_len == ((chunk_len & 63u) ? (chunk_len & 63u) : 64u)); 92 | // zero buffer when we start a new buffer, so that the chunk callback never sees data it shouldn't (for partial chunks) 93 | if (!chunk_offset) { 94 | memset(transfer->chunk_buffer, 0, transfer->chunk_size); 95 | } 96 | memcpy(transfer->chunk_buffer + chunk_offset, buffer->data, buffer->data_len); // always safe to copy all 97 | } 98 | #ifndef NDEBUG 99 | transfer->packet_handler_complete_expected = true; 100 | #endif 101 | 102 | // todo i think this is reasonable since 0 length chunk does nothing 103 | if (chunk_len) { 104 | assert(transfer->funcs && transfer->funcs->on_chunk); 105 | if (transfer->funcs->on_chunk(chunk_len, transfer)) 106 | return; 107 | } 108 | usb_stream_packet_handler_complete(transfer); 109 | } 110 | 111 | static const struct usb_transfer_type _usb_stream_transfer_type = { 112 | .on_packet = _usb_stream_packet_packet_handler 113 | }; 114 | 115 | void usb_stream_setup_transfer(struct usb_stream_transfer *transfer, const struct usb_stream_transfer_funcs *funcs, 116 | uint8_t *chunk_buffer, uint32_t chunk_size, uint32_t transfer_length, 117 | usb_transfer_completed_func on_complete) { 118 | transfer->funcs = funcs; 119 | transfer->chunk_buffer = chunk_buffer; 120 | assert(!(chunk_size & 63u)); // buffer should be a multiple of USB packet buffer size 121 | transfer->chunk_size = chunk_size; 122 | transfer->offset = 0; 123 | // todo combine with residue? 124 | transfer->transfer_length = transfer_length; 125 | usb_reset_transfer(&transfer->core, &_usb_stream_transfer_type, on_complete); 126 | usb_grow_transfer(&transfer->core, (transfer_length + 63) / 64); 127 | } 128 | 129 | void usb_stream_noop_on_packet_complete(__unused struct usb_stream_transfer *transfer) { 130 | 131 | } 132 | 133 | bool usb_stream_noop_on_chunk(uint32_t size, __unused struct usb_stream_transfer *transfer) { 134 | return false; 135 | } 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /src/rp2_common/usb_device_msc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(usb_device_msc INTERFACE) 2 | 3 | target_sources(usb_device_msc INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/usb_device_msc.c 5 | ) 6 | 7 | target_include_directories(usb_device_msc INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 8 | target_link_libraries(usb_device_msc INTERFACE usb_device) -------------------------------------------------------------------------------- /src/rp2_common/usb_device_msc/include/pico/scsi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _SCSI_H 8 | #define _SCSI_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define CBW_SIG 0x43425355 15 | struct __aligned(4) __packed scsi_cbw { 16 | uint32_t sig; 17 | uint32_t tag; 18 | uint32_t data_transfer_length; 19 | uint8_t flags; 20 | uint8_t lun; 21 | uint8_t cb_length; 22 | uint8_t cb[16]; 23 | }; 24 | 25 | #define CSW_SIG 0x53425355 26 | struct __packed scsi_csw { 27 | uint32_t sig; 28 | uint32_t tag; 29 | uint32_t residue; 30 | uint8_t status; 31 | }; 32 | 33 | struct __packed scsi_capacity { 34 | uint32_t lba; // last block addr 35 | uint32_t block_len; // probably 512 36 | }; 37 | 38 | struct __packed scsi_read_cb { 39 | uint8_t opcode; 40 | uint8_t flags; 41 | uint32_t lba; 42 | uint8_t reserved; 43 | uint16_t blocks; 44 | uint8_t control; 45 | }; 46 | 47 | enum csw_status { 48 | CSW_STATUS_COMMAND_PASSED = 0x00, 49 | CSW_STATUS_COMMAND_FAILED = 0x01, 50 | CSW_STATUS_PHASE_ERROR = 0x02, 51 | }; 52 | 53 | enum scsi_cmd { 54 | INQUIRY = 0x12, 55 | MODE_SELECT_6 = 0x15, 56 | MODE_SELECT_10 = 0x55, 57 | MODE_SENSE_6 = 0x1a, 58 | MODE_SENSE_10 = 0x5a, 59 | PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1e, 60 | READ_6 = 0x08, 61 | READ_10 = 0x28, 62 | READ_12 = 0xa8, 63 | READ_FORMAT_CAPACITIES = 0x23, 64 | READ_CAPACITY_10 = 0x25, 65 | REPORT_LUNS = 0xa0, 66 | REQUEST_SENSE = 0x03, 67 | SEND_DIAGNOSTIC = 0x1d, 68 | START_STOP_UNIT = 0x1b, 69 | SYNCHRONIZE_CACHE = 0x35, 70 | TEST_UNIT_READY = 0x00, 71 | VERIFY = 0x2f, 72 | WRITE_6 = 0x0a, 73 | WRITE_10 = 0x2a, 74 | WRITE_12 = 0xaa, 75 | }; 76 | 77 | enum scsi_sense_key { 78 | SK_OK = 0x00, 79 | SK_NOT_READY = 0x02, 80 | SK_ILLEGAL_REQUEST = 0x05, 81 | SK_UNIT_ATTENTION = 0x06, 82 | SK_DATA_PROTECT = 0x07 83 | }; 84 | 85 | enum scsi_additional_sense_code { 86 | ASC_NONE = 0x00, 87 | ASC_INVALID_COMMAND_OPERATION_CODE = 0x20, 88 | ASC_PERIPHERAL_DEVICE_WRITE_FAULT = 0x03, 89 | ASC_ACCESS_DENIED = 0x20, 90 | ASC_LBA_OUT_OF_RANGE = 0x21, 91 | ASC_WRITE_PROTECTED = 0x27, 92 | ASC_NOT_READY_TO_READY_CHANGE = 0x28, 93 | ASC_MEDIUM_NOT_PRESENT = 0x3a, 94 | }; 95 | 96 | enum scsi_additional_sense_code_qualifier { 97 | ASCQ_NA = 0x00, 98 | }; 99 | #endif -------------------------------------------------------------------------------- /src/rp2_common/usb_device_msc/include/pico/scsi_ir.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _SCSI_IR_H 8 | #define _SCSI_IR_H 9 | 10 | // NOTE THIS IS IN A SEPARATE HEADER AS IT IS COMPRESSED WHEN USING COMPRESS_TEXT 11 | typedef unsigned char uint8_t; 12 | 13 | struct scsi_inquiry_response { 14 | uint8_t pdt; 15 | uint8_t rmb; 16 | uint8_t spc_version; 17 | uint8_t rdf; 18 | uint8_t additional_length; 19 | uint8_t inquiry5; 20 | uint8_t inquiry6; 21 | uint8_t inquiry7; 22 | char vendor[8]; 23 | char product[16]; 24 | char version[4]; 25 | } __packed; 26 | 27 | #ifndef COMPRESS_TEXT 28 | static const struct scsi_inquiry_response scsi_ir = { 29 | .rmb = 0x80, 30 | .spc_version = 2, 31 | .rdf = 2, 32 | .additional_length = sizeof(struct scsi_inquiry_response) - 4, 33 | .vendor = "RPI ", 34 | .product = "RP2 ", 35 | .version = "1 ", 36 | }; 37 | #endif 38 | #endif -------------------------------------------------------------------------------- /src/rp2_common/usb_device_msc/include/pico/usb_device_msc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _USB_MSC_H 8 | #define _USB_MSC_H 9 | 10 | #define SECTOR_SIZE 512u 11 | 12 | bool msc_setup_request_handler(struct usb_interface *interface, struct usb_setup_packet *setup); 13 | void msc_on_configure(__unused struct usb_device *device, bool configured); 14 | //struct usb_endpoint msc_in, msc_out; 15 | extern struct usb_endpoint msc_endpoints[2]; 16 | 17 | // provided by the hosting code 18 | uint32_t msc_get_serial_number32(); 19 | void msc_eject(); 20 | 21 | #endif -------------------------------------------------------------------------------- /src/rp2_common/usb_device_msc/include/pico/virtual_disk.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _VIRTUAL_DISK_H 8 | #define _VIRTUAL_DISK_H 9 | 10 | #include "usb_device_msc.h" 11 | 12 | #define USE_INFO_UF2 13 | 14 | void vd_init(); 15 | void vd_reset(); 16 | 17 | // return true for async operation 18 | bool vd_read_block(uint32_t token, uint32_t lba, uint8_t *buf, uint32_t buf_size); 19 | bool vd_write_block(uint32_t token, uint32_t lba, uint8_t *buf, uint32_t buf_size); 20 | 21 | // give us ourselves 16M which should strictly be the minimum for FAT16 - Note Win10 doesn't like FAT12 - go figure! 22 | // upped to 64M which allows us to download a 32M UF2 23 | #define CLUSTER_UP_SHIFT 0u 24 | #define CLUSTER_UP_MUL (1u << CLUSTER_UP_SHIFT) 25 | #define VOLUME_SIZE (CLUSTER_UP_MUL * 128u * 1024u * 1024u) 26 | 27 | #define SECTOR_COUNT (VOLUME_SIZE / SECTOR_SIZE) 28 | 29 | #ifndef GENERAL_SIZE_HACKS 30 | 31 | static inline uint32_t vd_sector_count() { 32 | return SECTOR_COUNT; 33 | } 34 | 35 | #else 36 | // needs to be a compile time constant 37 | #define vd_sector_count() SECTOR_COUNT 38 | #endif 39 | 40 | void vd_async_complete(uint32_t token, uint32_t result); 41 | #endif 42 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(sample_conversion_test) 2 | add_subdirectory(sd_test) 3 | -------------------------------------------------------------------------------- /test/sample_conversion_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT PICO_ON_DEVICE OR NOT PICO_NO_FLASH) # too big for RAM 2 | add_executable(sample_conversion_test sample_conversion_test.cpp) 3 | 4 | target_compile_definitions(sample_conversion_test PRIVATE 5 | #PICO_ENTER_USB_BOOT_ON_EXIT=1 6 | ) 7 | target_link_libraries(sample_conversion_test PRIVATE pico_stdlib pico_audio) 8 | pico_add_extra_outputs(sample_conversion_test) 9 | endif() 10 | -------------------------------------------------------------------------------- /test/sample_conversion_test/sample_conversion_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "pico/stdlib.h" 11 | #include "pico/bit_ops.h" 12 | #include "pico/sample_conversion.h" 13 | 14 | typedef int (*sample_converter_fn)(int); 15 | 16 | int u16_to_u16(int s) { return (uint16_t) s; } 17 | 18 | int s16_to_u16(int s) { return (uint16_t) (s ^ 0x8000u); } 19 | 20 | int u8_to_u16(int s) { return (uint16_t) (s << 8); } 21 | 22 | int s8_to_u16(int s) { return (uint16_t) ((s << 8u) ^ 0x8000u); } 23 | 24 | int u16_to_s16(int s) { return (int16_t) (s ^ 0x8000u); } 25 | 26 | int s16_to_s16(int s) { return (int16_t) s; } 27 | 28 | int u8_to_s16(int s) { return (int16_t) ((s << 8u) ^ 0x8000u); } 29 | 30 | int s8_to_s16(int s) { return (int16_t) (s << 8u); } 31 | 32 | int u16_to_u8(int s) { return (uint8_t) (s >> 8u); } 33 | 34 | int s16_to_u8(int s) { return (uint8_t) ((s ^ 0x8000u) >> 8u); } 35 | 36 | int u8_to_u8(int s) { return (uint8_t) s; } 37 | 38 | int s8_to_u8(int s) { return (uint8_t) (s ^ 0x80); } 39 | 40 | int u16_to_s8(int s) { return (int8_t) ((s ^ 0x8000u) >> 8u); } 41 | 42 | int s16_to_s8(int s) { return (int8_t) (s >> 8u); } 43 | 44 | int u8_to_s8(int s) { return (int8_t) (s ^ 0x80); } 45 | 46 | int s8_to_s8(int s) { return (int8_t) s; } 47 | 48 | template 49 | typename Fmt::sample_t random_sample() { 50 | return (typename Fmt::sample_t) rand(); 51 | } 52 | 53 | void check_sample(int from, int expected, int actual) { 54 | if (expected != actual) { 55 | printf("Failed converting %04x to %04x (got %04x)\n", from, expected, actual); 56 | assert(false); 57 | } 58 | } 59 | 60 | template 61 | void check_conversion(sample_converter_fn converter_fn) { 62 | uint length = 256 + rand() & 0xffu; 63 | typename ToFmt::sample_t to_buffer[length * ToFmt::channel_count]; 64 | typename FromFmt::sample_t from_buffer[length * FromFmt::channel_count]; 65 | for (uint i = 0; i < length * FromFmt::channel_count; i++) { 66 | from_buffer[i] = random_sample(); 67 | } 68 | converting_copy::copy(to_buffer, from_buffer, length); 69 | if (ToFmt::channel_count == FromFmt::channel_count) { 70 | for (uint i = 0; i < length * ToFmt::channel_count; i++) { 71 | check_sample(from_buffer[i], converter_fn(from_buffer[i]), to_buffer[i]); 72 | } 73 | } else if (ToFmt::channel_count == 2 & FromFmt::channel_count == 1) { 74 | // mono -> stereo duplicates 75 | for (uint i = 0; i < length; i++) { 76 | check_sample(from_buffer[i], converter_fn(from_buffer[i]), to_buffer[i * 2]); 77 | check_sample(from_buffer[i], converter_fn(from_buffer[i]), to_buffer[i * 2 + 1]); 78 | } 79 | } else if (ToFmt::channel_count == 1 & FromFmt::channel_count == 2) { 80 | // stereo -> mono averages 81 | for (uint i = 0; i < length; i++) { 82 | // can't represent both samples 83 | check_sample(0xf00d, converter_fn((from_buffer[i * 2] + from_buffer[i * 2 + 1]) / 2), to_buffer[i]); 84 | } 85 | } else { 86 | assert(false); 87 | } 88 | } 89 | 90 | template 91 | void check_conversions(sample_converter_fn converter_fn) { 92 | // for a given format check conversions to and from 93 | check_conversion, Mono>(converter_fn); 94 | check_conversion, Mono>(converter_fn); 95 | check_conversion, Stereo>(converter_fn); 96 | check_conversion, Stereo>(converter_fn); 97 | } 98 | 99 | int main() { 100 | // On FPGA, pins 28 and 29 are connected to the VC707 board USB-UART 101 | uart_init(uart0, 115200); 102 | gpio_set_function(28, GPIO_FUNC_UART); 103 | gpio_set_function(29, GPIO_FUNC_UART); 104 | 105 | // check all permutations of supported formats 106 | 107 | check_conversions(u16_to_u16); 108 | check_conversions(u16_to_s16); 109 | check_conversions(u16_to_u8); 110 | check_conversions(u16_to_s8); 111 | 112 | check_conversions(s16_to_u16); 113 | check_conversions(s16_to_s16); 114 | check_conversions(s16_to_u8); 115 | check_conversions(s16_to_s8); 116 | 117 | check_conversions(u8_to_u16); 118 | check_conversions(u8_to_s16); 119 | check_conversions(u8_to_u8); 120 | check_conversions(u8_to_s8); 121 | 122 | check_conversions(s8_to_u16); 123 | check_conversions(s8_to_s16); 124 | check_conversions(s8_to_u8); 125 | check_conversions(s8_to_s8); 126 | 127 | printf("OK\n"); 128 | } 129 | 130 | -------------------------------------------------------------------------------- /test/sd_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (PICO_ON_DEVICE) 2 | if (TARGET pico_sd_card) 3 | add_executable(sd_test 4 | sd_test.c 5 | ) 6 | 7 | # target_compile_definitions(sd_test PRIVATE 8 | # PICO_DEFAULT_UART_TX_PIN=28 9 | # PICO_DEFAULT_UART_RX_PIN=29 10 | # PICO_DEFAULT_UART=0 11 | # ) 12 | target_link_libraries(sd_test pico_stdlib pico_sd_card) 13 | pico_add_extra_outputs(sd_test) 14 | endif() 15 | endif() -------------------------------------------------------------------------------- /test/sd_test/sd_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "pico/stdlib.h" 12 | #include "pico/sd_card.h" 13 | #include "hardware/clocks.h" 14 | 15 | static uint8_t sector_data[1024]; 16 | 17 | #define ENABLE_4_PIN 0 18 | 19 | int main(void) { 20 | set_sys_clock_48mhz(); 21 | stdio_init_all(); 22 | 23 | printf("SD Card test\n"); 24 | 25 | int i = 0; 26 | 27 | // 0-2 for SD_CLK, SD_CMD, SD_DAT 28 | 29 | int sd_pin_base = 25; 30 | 31 | // gpio_init(0); 32 | // gpio_init(5); 33 | // gpio_init(10); 34 | // gpio_set_dir_out_masked(0x421); 35 | 36 | #if ENABLE_4_PIN 37 | if (sd_init_4pin() < 0) 38 | #else 39 | if (sd_init_1pin() < 0) 40 | #endif 41 | { 42 | panic("doh"); 43 | } 44 | 45 | static int block_base = 0; 46 | #define BLOCK_COUNT 2 47 | 48 | #define STREAMING 49 | 50 | #ifdef STREAMING 51 | static uint32_t b[BLOCK_COUNT * 128]; 52 | for(int div = 4; div >= 1; div--) 53 | { 54 | uint8_t *buf = (uint8_t *)b; 55 | printf("-----------------------\n"); 56 | printf("SPEED %uMB/s\n", 12/div); 57 | sd_set_clock_divider(div); 58 | printf("1 bit no crc\n"); 59 | sd_set_wide_bus(false); 60 | memset(buf, 0xaa, 512); 61 | sd_readblocks_sync(b, block_base, BLOCK_COUNT); 62 | for(int byte = 0; byte < 512; byte += 16) 63 | { 64 | printf("%08x ", i * 512 + byte); 65 | for(int j = 0; j < 16; j++) printf("%02x ", buf[byte + j]); 66 | for(int j = 0; j < 16; j++) putchar(isprint(buf[byte + j]) ? buf[byte + j] : '.'); 67 | printf("\n"); 68 | } 69 | #if ENABLE_4_PIN 70 | memset(buf, 0xaa, 512); 71 | printf("4 bit no crc\n"); 72 | sd_set_wide_bus(true); 73 | sd_readblocks_sync(b, block_base, BLOCK_COUNT); 74 | for(int byte = 0; byte < 512; byte += 16) 75 | { 76 | printf("%08x ", i * 512 + byte); 77 | for(int j = 0; j < 16; j++) printf("%02x ", buf[byte + j]); 78 | for(int j = 0; j < 16; j++) putchar(isprint(buf[byte + j]) ? buf[byte + j] : '.'); 79 | printf("\n"); 80 | } 81 | #endif 82 | memset(buf, 0xaa, 512); 83 | printf("1 bit crc\n"); 84 | sd_read_sectors_1bit_crc_async(b, block_base, BLOCK_COUNT); 85 | int status = 0; 86 | while (!sd_scatter_read_complete(&status)); 87 | printf("Status: %d\n", status); 88 | #endif 89 | for(i = 0; i < BLOCK_COUNT; i++) 90 | { 91 | #ifndef STREAMING 92 | uint8_t *buf = sd_readblock(i); 93 | #endif 94 | //if (i == BLOCK_COUNT-1) 95 | for(int byte = 0; byte < 512; byte += 16) 96 | { 97 | printf("%08x ", i * 512 + byte); 98 | for(int j = 0; j < 16; j++) printf("%02x ", buf[byte + j]); 99 | for(int j = 0; j < 16; j++) putchar(isprint(buf[byte + j]) ? buf[byte + j] : '.'); 100 | printf("\n"); 101 | } 102 | printf("\n"); 103 | #ifdef STREAMING 104 | buf += 512; 105 | #endif 106 | } 107 | } 108 | 109 | #if 0 110 | strcpy(sector_data, "fish And Hello there zif squiffy!"); 111 | sector_data[511] = 0xaa; 112 | sd_writeblocks_async((uint32_t*)sector_data, 0, 1); 113 | static int timeout = 10; 114 | int rc; 115 | while (!sd_write_complete(&rc)) { 116 | printf("Waiting for completion\n"); 117 | if (!--timeout) break; 118 | } 119 | printf("Done %d!\n", rc); 120 | strcpy(sector_data, "vasil fleplic yoeville frentucky arrivant sklim avary ron giblet And Hello there zif squiffy!"); 121 | sector_data[511] = 0x55; 122 | strcpy(sector_data + 512, "and this is sector 2 folks"); 123 | sd_writeblocks_async((uint32_t*)sector_data, 0, 2); 124 | timeout = 10; 125 | while (!sd_write_complete(&rc)) { 126 | printf("Waiting for completion\n"); 127 | if (!--timeout) break; 128 | } 129 | #endif 130 | printf("Done!\n"); 131 | __breakpoint(); 132 | } 133 | --------------------------------------------------------------------------------