[
  {
    "path": ".gitignore",
    "content": "build/*\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.12)\ninclude(pico_sdk_import.cmake)\n\nproject(hsdaoh-rp2350 C CXX ASM)\nset(CMAKE_C_STANDARD 11)\nset(CMAKE_CXX_STANDARD 17)\npico_sdk_init()\n\nadd_compile_options(-Wall)\n\ninclude_directories(\n\tinclude\n\t)\n\nadd_subdirectory(libpicohsdaoh)\nadd_subdirectory(apps)\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2024 by Steve Markgraf\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# hsdaoh-rp2350 - High Speed Data Acquisition over HDMI\n## Stream up to 175 MByte/s from your Raspberry Pi Pico2 to your PC\n\nUsing $5 USB3 HDMI capture sticks based on the MacroSilicon MS2130, this project allows to stream out up to 175 MByte/s of real time data from an RP2350 (with overclocking) to a host computer with USB3.\nFor more information and the host library, see the [main repository](https://github.com/steve-m/hsdaoh) and the [talk at OsmoDevcon '24](https://media.ccc.de/v/osmodevcon2024-200-low-cost-high-speed-data-acquisition-over-hdmi).\n\n![Raspberry Pi Pico2 with MS2130 stick](https://steve-m.de/projects/hsdaoh/pico2_hsdaoh.jpg)\n\n## Building\n\nMake sure you have the latest version of the [pico-sdk](https://github.com/raspberrypi/pico-sdk) installed together with an appropriate compiler. You should be able to build the [pico-examples](https://github.com/raspberrypi/pico-examples).\n\nTo build hsdaoh-rp2350:\n\n    git clone https://github.com/steve-m/hsdaoh-rp2350.git\n    mkdir hsdaoh-rp2350/build\n    cd hsdaoh-rp2350/build\n    export PICO_SDK_PATH=/<path-to>/pico-sdk\n    cmake -DPICO_PLATFORM=rp2350 -DPICO_BOARD=pico2 ../\n    make -j 8\n\nAfter the build succeeds you can copy the resulting *.uf2 file of the application you want to run to the board.\n\nApart from the Pico2 with the [Pico-DVI-Sock](https://github.com/Wren6991/Pico-DVI-Sock), it also should work with the Adafruit Feather RP2350 with HSTX Port, but so far only the Pico2 was tested.\n\n## Example applications\n\nThe repository contains a library - libpicohsdaoh - which implements the main functionality. It reads the data from a ringbuffer, and streams it out via the HSTX port.\nIn addition to that, the apps folder contains a couple of example applications:\n\n### counter\n\nThis application uses the PIO to generate a 16-bit counter value which is written to a DMA ringbuffer, which is then streamed out via hsdaoh. The counter can be verified using the hsdaoh_test host application.\n\n### logic_analyzer\n\nSample 16 GPIOs with the PIO and transfer the data, can be used as a 16 bit @ 32 MHz logic analyzer, or be adapted to 8 bit @ 64 MHz and so on.\nThe IOs used for input are GP0-11, GP20-22 and GP26.\n\n### internal_adc\n\nThe data from the internal ADC is streamed out via USB. Default configuration is overclocking the ADC to 3.33 MS/s. Using the USB PLL and overvolting beyond VREG_VOLTAGE_MAX, up to 7.9 MS/s can be achieved.\n\n### external_adc\n\nThis app contains a PIO program that reads the data from a 12-bit ADC connected to GP0-GP11, outputs the ADC clock on GP20, and packs the 12-bit samples to 16-bit words to achieve maximum throughput.\nIt is meant to be used with cheap AD9226 ADC boards. The default setting is overclocking the RP2350 to 160 MHz and driving the ADC with a 40 MHz clock. With higher overclocking up to 96 MHz ADC clock can be used.\n\nThis can be used for sampling the IF of a tuner/downcoverter, as a direct-sampling HF SDR, or for capturing a video signal e.g. with [vhsdecode](https://github.com/oyvindln/vhs-decode).\nFor the vhsdecode use-case, there is also an [adapter PCB](https://github.com/Sev5000/Pico2_12bitADC_PCMAudio). It also supports sampling a PCM1802 audio ADC board.\n\n![Pico2 with AD9226 ADC board](https://steve-m.de/projects/hsdaoh/rp2350_external_adc_rot.jpg)\n\n### external_dualchan_adc\n\nSimilar to the external_adc app, but samples a AD9238 dual channel 12-bit ADC connected to GP0-GP11, with clock for both channels on GP20. The benefit of using the AD9238 is that only 12 data lines are required to transfer the data DDR-style, so it works with a regular RP2350A/Pico2 board.\n\n### dual_external_adc\n\nAlso similar to the external_adc app, but samples two 12-bit ADCs connected to a RP2350B, as well as two PCM1802 modules. Intended for use with vhs-decode, see [this PCB](https://github.com/Sev5000/RP2350B_DualADC_DualPCM) for the matching hardware.\nThis example needs to be built with an RP2350B-board in order to work correctly:\n\n    cmake -DPICO_PLATFORM=rp2350 -DPICO_BOARD=solderparty_rp2350_stamp_xl ../\n\n### sdr\n\nThis app can be used for attaching the [hsdaohSDR prototype](https://github.com/steve-m/hsdaohSDR) to an RP2350B instead of the Tang nano 20K FPGA board. Contains PIO code for packing the 2x 10 bit AD9218 data, and also implements a USB UART to I2C bridge for controlling the tuner. Like the dual_external_adc, it also needs to be built with a RP2350B board.\n\n## Credits\n\nhsdaoh-rp2350 is developed by Steve Markgraf, and is based on the [dvi_out_hstx_encoder](https://github.com/raspberrypi/pico-examples/tree/master/hstx/dvi_out_hstx_encoder) example, and code by Shuichi Takano [implementing the HDMI data island encoding](https://github.com/shuichitakano/pico_lib/blob/master/dvi/data_packet.cpp), required to send HDMI info frames.\n"
  },
  {
    "path": "apps/CMakeLists.txt",
    "content": "add_subdirectory(counter)\nadd_subdirectory(external_adc)\nadd_subdirectory(dual_external_adc)\nadd_subdirectory(external_dualchan_adc)\nadd_subdirectory(sdr)\nadd_subdirectory(internal_adc)\nadd_subdirectory(logic_analyzer)\n"
  },
  {
    "path": "apps/counter/CMakeLists.txt",
    "content": "add_executable(counter\n\tcounter.c\n)\n\ntarget_compile_options(counter PRIVATE -Wall)\n\ntarget_link_libraries(counter\n\tpico_stdlib\n\tpico_multicore\n\tpico_util\n\thardware_pio\n\thardware_dma\n\tlibpicohsdaoh\n)\npico_generate_pio_header(counter ${CMAKE_CURRENT_LIST_DIR}/counter.pio)\n\n# enable usb output, disable uart output\npico_enable_stdio_usb(counter 1)\n\n# create map/bin/hex file etc.\npico_add_extra_outputs(counter)\n"
  },
  {
    "path": "apps/counter/counter.c",
    "content": "/*\n * hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks\n * Implementation for the Raspberry Pi RP2350 HSTX peripheral\n *\n * PIO counter value example, counter can be verified with hsdaoh_test\n *\n * Copyright (c) 2024 by Steve Markgraf <steve@steve-m.de>\n *\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the author nor the names of its contributors may\n *    be used to endorse or promote products derived from this software\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"pico/stdlib.h\"\n#include \"hardware/clocks.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/sync.h\"\n#include \"hardware/vreg.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/pio.h\"\n\n#include \"picohsdaoh.h\"\n#include \"counter.pio.h\"\n\n#define SYS_CLK\t\t336000\n\n#define DMACH_PIO_PING 0\n#define DMACH_PIO_PONG 1\n\nstatic bool pio_dma_pong = false;\nuint16_t ringbuffer[RBUF_DEFAULT_TOTAL_LEN];\nint ringbuf_head = 2;\n\nvoid __scratch_y(\"\") pio_dma_irq_handler()\n{\n\tuint ch_num = pio_dma_pong ? DMACH_PIO_PONG : DMACH_PIO_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\tpio_dma_pong = !pio_dma_pong;\n\n\tringbuf_head = (ringbuf_head + 1) % RBUF_DEFAULT_SLICES;\n\n\tch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = RBUF_MAX_DATA_LEN;\n\n\thsdaoh_update_head(0, ringbuf_head);\n}\n\nvoid init_pio_input(void)\n{\n\tPIO pio = pio0;\n\tuint offset = pio_add_program(pio, &counter_program);\n\tuint sm_data = pio_claim_unused_sm(pio, true);\n\tcounter_program_init(pio, sm_data, offset);\n\n\tdma_channel_config c;\n\tc = dma_channel_get_default_config(DMACH_PIO_PING);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PONG);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PING,\n\t\t&c,\n\t\t&ringbuffer[0 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tRBUF_MAX_DATA_LEN,\n\t\tfalse\n\t);\n\tc = dma_channel_get_default_config(DMACH_PIO_PONG);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PING);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PONG,\n\t\t&c,\n\t\t&ringbuffer[1 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tRBUF_MAX_DATA_LEN,\n\t\tfalse\n\t);\n\n\tdma_hw->ints0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tdma_hw->inte0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tirq_set_exclusive_handler(DMA_IRQ_0, pio_dma_irq_handler);\n\tirq_set_enabled(DMA_IRQ_0, true);\n\n\tdma_channel_start(DMACH_PIO_PING);\n}\n\nint main()\n{\n\tvreg_set_voltage(VREG_VOLTAGE_MAX);\n\tsleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US);\n\thsdaoh_set_sys_clock_khz(SYS_CLK);\n\n\t/* set HSTX clock to sysclk/1 */\n\thw_write_masked(\n\t\t&clocks_hw->clk[clk_hstx].div,\n\t\t1 << CLOCKS_CLK_HSTX_DIV_INT_LSB,\n\t\tCLOCKS_CLK_HSTX_DIV_INT_BITS\n\t);\n\n\tstdio_init_all();\n\n\thsdaoh_init(GPIO_DRIVE_STRENGTH_4MA, GPIO_SLEW_RATE_SLOW);\n\thsdaoh_add_stream(0, 1, (SYS_CLK/8) * 1000, RBUF_MAX_DATA_LEN, RBUF_DEFAULT_SLICES, ringbuffer);\n\thsdaoh_start();\n\tinit_pio_input();\n\n\twhile (1)\n\t\t__wfi();\n}\n"
  },
  {
    "path": "apps/counter/counter.pio",
    "content": ";\n; Copyright (c) 2024 Steve Markgraf <steve@steve-m.de>\n;\n; SPDX-License-Identifier: BSD-3-Clause\n;\n; Generate 16 bit counter in PIO\n;\n\n.pio_version 0\n.program counter\n\npublic entry_point:\n\n.wrap_target\n\tjmp x-- dummylabel\ndummylabel:\n\tmov isr, ~x\n\tpush\n.wrap\n\n% c-sdk {\nstatic inline void counter_program_init(PIO pio, uint sm, uint offset)\n{\n\tpio_sm_config c = counter_program_get_default_config(offset);\n\n\tsm_config_set_in_shift(\n\t\t&c,\n\t\tfalse,\t// Shift-to-right = false (i.e. shift to left)\n\t\tfalse,\t// Autopush enabled\n\t\t1\t// Autopush threshold, ignored\n\t);\n\n\t// disable the TX FIFO to make the RX FIFO deeper.\n\tsm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);\n\n\tsm_config_set_clkdiv(&c, 1.75f);\n\n\t// Load our configuration, and start the program from the beginning\n\tpio_sm_init(pio, sm, offset, &c);\n\tpio_sm_set_enabled(pio, sm, true);\n}\n%}\n"
  },
  {
    "path": "apps/dual_external_adc/CMakeLists.txt",
    "content": "add_executable(dual_external_adc\n\tdual_external_adc.c\n)\n\ntarget_compile_options(dual_external_adc PRIVATE -Wall)\n\ntarget_link_libraries(dual_external_adc\n\tpico_stdlib\n\tpico_util\n\thardware_pio\n\thardware_dma\n\tlibpicohsdaoh\n)\npico_generate_pio_header(dual_external_adc ${CMAKE_CURRENT_LIST_DIR}/adc_24bit_input.pio)\npico_generate_pio_header(dual_external_adc ${CMAKE_CURRENT_LIST_DIR}/pcm1802_fmt00.pio)\n\n# enable usb output, disable uart output\npico_enable_stdio_usb(dual_external_adc 1)\n\n# create map/bin/hex file etc.\npico_add_extra_outputs(dual_external_adc)\n"
  },
  {
    "path": "apps/dual_external_adc/adc_24bit_input.pio",
    "content": ";\n; Copyright (c) 2024-2025 Steve Markgraf <steve@steve-m.de>\n;\n; SPDX-License-Identifier: BSD-3-Clause\n;\n; Sample 2x12 bit parallel ADC (AD9226) every 2 PIO cycles on rising clock edge,\n; pack four 24 bit samples in three 32 bit words\n; ADC clock output as side-set\n;\n; Data being pushed to the FIFO, four 24 bit samples A-D\n; First word:  A07 A06 A05 A04 A03 A02 A01 A00 B23 B22 B21 B20 B19 B18 B17 B16 B15 B14 B13 B12 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00\n; Second word: A15 A14 A13 A12 A11 A10 A09 A08 C23 C22 C21 C20 C19 C18 C17 C16 C15 C14 C13 C12 C11 C10 C09 C08 C07 C06 C05 C04 C03 C02 C01 C00\n; Third word:  A23 A22 A21 A20 A19 A18 A17 A16 D23 D22 D21 D20 D19 D18 D17 D16 D15 D14 D13 D12 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00\n\n.pio_version 1\n.program adc_24bit_input\n.side_set 3\n\npublic entry_point:\n\n.wrap_target\n\tmov osr, pins\tside 7\t; sample A\n\tout isr, 8\tside 0\n\n\tin pins, 24\tside 7\t; sample B, autopush\n\tout isr, 8\tside 0\n\n\tin pins, 24\tside 7\t; sample C, autopush\n\tout isr, 8\tside 0\n\n\tin pins, 24\tside 7\t; sample D, autopush\n\tnop\t\tside 0\n.wrap\n\n% c-sdk {\nstatic inline void adc_24bit_input_program_init(PIO pio, uint sm, uint offset, uint pin, uint clk_pin)\n{\n\tpio_sm_config c = adc_24bit_input_program_get_default_config(offset);\n\n\t// Set the IN base pin to the provided `pin` parameter.\n\tsm_config_set_in_pins(&c, pin);\n\n\t// configure CLK pin for side-set\n\tsm_config_set_sideset_pins(&c, clk_pin);\n\tsm_config_set_sideset(&c, 3, false, false);\n\n\tpio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 3, true);\n\tpio_gpio_init(pio, clk_pin);\n\tpio_gpio_init(pio, clk_pin+1);\n\tpio_gpio_init(pio, clk_pin+2);\n\n\t// Set the pin directions to input at the PIO\n\t// Set D0-D23 of the ADC(s) as input\n\tpio_sm_set_consecutive_pindirs(pio, sm, pin, 24, false);\n\n\t// Connect these GPIOs to this PIO block\n\tfor (int i = pin; i < (pin+24); i++)\n\t\tpio_gpio_init(pio, i);\n\n\tsm_config_set_in_shift(\n\t\t&c,\n\t\tfalse,\t// Shift-to-right = false (i.e. shift to left)\n\t\ttrue,\t// Autopush enabled\n\t\t32\t// Autopush threshold = 32\n\t);\n\n\t// required in order to set shift-to-right to true (for the out ops)\n\tsm_config_set_out_shift(\n\t\t&c,\n\t\ttrue,\t// Shift-to-right = true\n\t\tfalse,\t// Autopush disabled\n\t\t1\t// Autopush threshold: ignored\n\t);\n\n\t// We only receive, so disable the TX FIFO to make the RX FIFO deeper.\n\tsm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);\n\n\tsm_config_set_clkdiv(&c, 4.f);\n\n\t// Load our configuration, and start the program from the beginning\n\tpio_sm_init(pio, sm, offset, &c);\n//\tpio_sm_set_enabled(pio, sm, true);\n}\n%}\n"
  },
  {
    "path": "apps/dual_external_adc/dual_external_adc.c",
    "content": "/*\n * hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks\n * Implementation for the Raspberry Pi RP2350 HSTX peripheral\n *\n * Dual External 12-bit ADC example, connected to the PIO\n *\n * Copyright (c) 2024-2025 by Steve Markgraf <steve@steve-m.de>\n *\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the author nor the names of its contributors may\n *    be used to endorse or promote products derived from this software\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"pico/stdlib.h\"\n#include \"hardware/clocks.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/sync.h\"\n#include \"hardware/vreg.h\"\n#include \"hardware/pll.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/pio.h\"\n\n#include \"picohsdaoh.h\"\n#include \"adc_24bit_input.pio.h\"\n#include \"pcm1802_fmt00.pio.h\"\n\n#if (PICO_PIO_USE_GPIO_BASE != 1)\n#warning \"PICO_PIO_USE_GPIO_BASE is not set to 1, this application will not work correctly!\"\n#endif\n\n/* The PIO is running with sys_clk/2, and needs 4 cycles per sample,\n * so the ADC clock is sys_clk/8 */\n#define SYS_CLK\t\t320000\t// 40 MHz ADC clock\n\n// For alignment of 3x32 bit words in the payload, so that every line starts with word 0\n#define ADC_DATA_LEN\t(RBUF_SLICE_LEN - 6)\n\n// Same here for 2x32 bit words\n#define AUDIO_DATA_LEN\t\t(RBUF_SLICE_LEN - 4)\n#define AUDIO_RBUF_SLICES\t8\n\n// ADC is attached to GP0 - GP11 with clock on GP20\n#define PIO_INPUT_PIN_BASE 23\n#define PIO_OUTPUT_CLK_PIN 20\n\n#define DMACH_PIO_PING 0\n#define DMACH_PIO_PONG 1\n\nstatic bool pio_dma_pong = false;\nuint16_t ringbuffer[RBUF_DEFAULT_TOTAL_LEN];\nint ringbuf_head = 2;\n\nvoid __scratch_y(\"\") pio_dma_irq_handler()\n{\n\tuint ch_num = pio_dma_pong ? DMACH_PIO_PONG : DMACH_PIO_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\tpio_dma_pong = !pio_dma_pong;\n\n\tringbuf_head = (ringbuf_head + 1) % RBUF_DEFAULT_SLICES;\n\n\tch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = ADC_DATA_LEN/2;\n\n\thsdaoh_update_head(0, ringbuf_head);\n}\n\nvoid init_pio_input(void)\n{\n\tPIO pio = pio0;\n\n\t/* move up GPIO base of PIO to access all ADC pins */\n\tpio_set_gpio_base(pio, 16);\n\n\tuint offset = pio_add_program(pio, &adc_24bit_input_program);\n\tuint sm_data = pio_claim_unused_sm(pio, true);\n\n\tadc_24bit_input_program_init(pio, sm_data, offset, PIO_INPUT_PIN_BASE, PIO_OUTPUT_CLK_PIN);\n\n\tdma_channel_config c;\n\tc = dma_channel_get_default_config(DMACH_PIO_PING);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PONG);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_32);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PING,\n\t\t&c,\n\t\t&ringbuffer[0 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tADC_DATA_LEN/2,\n\t\tfalse\n\t);\n\tc = dma_channel_get_default_config(DMACH_PIO_PONG);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PING);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_32);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PONG,\n\t\t&c,\n\t\t&ringbuffer[1 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tADC_DATA_LEN/2,\n\t\tfalse\n\t);\n\n\tdma_hw->ints0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tdma_hw->inte0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tirq_set_exclusive_handler(DMA_IRQ_0, pio_dma_irq_handler);\n\tirq_set_enabled(DMA_IRQ_0, true);\n\n\tdma_channel_start(DMACH_PIO_PING);\n}\n\n#define PCM1802_DATA_PIN_1\t3\n#define AUDIO1_STREAM_ID\t2\n#define DMACH_AUDIO1_PIO_PING\t2\n#define DMACH_AUDIO1_PIO_PONG\t3\n\n#define PCM1802_DATA_PIN_2\t0\n#define AUDIO2_STREAM_ID\t3\n#define DMACH_AUDIO2_PIO_PING\t4\n#define DMACH_AUDIO2_PIO_PONG\t5\n\nstatic bool audio1_pio_dma_pong = false;\nuint16_t audio1_ringbuffer[AUDIO_RBUF_SLICES * RBUF_SLICE_LEN];\nint audio1_ringbuf_head = 2;\n\nstatic bool audio2_pio_dma_pong = false;\nuint16_t audio2_ringbuffer[AUDIO_RBUF_SLICES * RBUF_SLICE_LEN];\nint audio2_ringbuf_head = 2;\n\nvoid __scratch_y(\"\") audio1_pio_dma_irq_handler()\n{\n\tuint ch_num = audio1_pio_dma_pong ? DMACH_AUDIO1_PIO_PONG : DMACH_AUDIO1_PIO_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\taudio1_pio_dma_pong = !audio1_pio_dma_pong;\n\n\taudio1_ringbuf_head = (audio1_ringbuf_head + 1) % AUDIO_RBUF_SLICES;\n\n\tch->write_addr = (uintptr_t)&audio1_ringbuffer[audio1_ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = AUDIO_DATA_LEN/2;\n\n\thsdaoh_update_head(AUDIO1_STREAM_ID, audio1_ringbuf_head);\n}\n\nvoid __scratch_y(\"\") audio2_pio_dma_irq_handler()\n{\n\tuint ch_num = audio2_pio_dma_pong ? DMACH_AUDIO2_PIO_PONG : DMACH_AUDIO2_PIO_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\taudio2_pio_dma_pong = !audio2_pio_dma_pong;\n\n\taudio2_ringbuf_head = (audio2_ringbuf_head + 1) % AUDIO_RBUF_SLICES;\n\n\tch->write_addr = (uintptr_t)&audio2_ringbuffer[audio2_ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = AUDIO_DATA_LEN/2;\n\n\thsdaoh_update_head(AUDIO2_STREAM_ID, audio2_ringbuf_head);\n}\n\nvoid init_audio_pio_dma(PIO pio, uint sm_data, uint dmach_ping, uint dmach_pong, uint16_t *rbuf)\n{\n\tdma_channel_config c;\n\tc = dma_channel_get_default_config(dmach_ping);\n\tchannel_config_set_chain_to(&c, dmach_pong);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_32);\n\n\tdma_channel_configure(\n\t\tdmach_ping,\n\t\t&c,\n\t\t&rbuf[0 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tAUDIO_DATA_LEN/2,\n\t\tfalse\n\t);\n\tc = dma_channel_get_default_config(dmach_pong);\n\tchannel_config_set_chain_to(&c, dmach_ping);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_32);\n\n\tdma_channel_configure(\n\t\tdmach_pong,\n\t\t&c,\n\t\t&rbuf[1 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tAUDIO_DATA_LEN/2,\n\t\tfalse\n\t);\n\n\tuint dma_irq = (dmach_ping == DMACH_AUDIO1_PIO_PING) ? DMA_IRQ_1 : DMA_IRQ_2;\n\tif (dma_irq == DMA_IRQ_1) {\n\t\tdma_hw->ints1 |= (1u << dmach_ping) | (1u << dmach_pong);\n\t\tdma_hw->inte1 |= (1u << dmach_ping) | (1u << dmach_pong);\n\t\tirq_set_exclusive_handler(dma_irq, audio1_pio_dma_irq_handler);\n\t} else {\n\t\tdma_hw->ints2 |= (1u << dmach_ping) | (1u << dmach_pong);\n\t\tdma_hw->inte2 |= (1u << dmach_ping) | (1u << dmach_pong);\n\t\tirq_set_exclusive_handler(dma_irq, audio2_pio_dma_irq_handler);\n\t}\n\tirq_set_enabled(dma_irq, true);\n\n\tdma_channel_start(dmach_ping);\n}\n\nvoid init_audio_pio_input(void)\n{\n\tPIO pio = pio1;\n\tpio_set_gpio_base(pio, 0);\n\tuint offset = pio_add_program(pio, &pcm1802_fmt00_program);\n\n\tuint sm_data = pio_claim_unused_sm(pio, true);\n\tpcm1802_fmt00_program_init(pio, sm_data, offset, PCM1802_DATA_PIN_1);\n\tinit_audio_pio_dma(pio, sm_data, DMACH_AUDIO1_PIO_PING, DMACH_AUDIO1_PIO_PONG, audio1_ringbuffer);\n\n\tsm_data = pio_claim_unused_sm(pio, true);\n\tpcm1802_fmt00_program_init(pio, sm_data, offset, PCM1802_DATA_PIN_2);\n\tinit_audio_pio_dma(pio, sm_data, DMACH_AUDIO2_PIO_PING, DMACH_AUDIO2_PIO_PONG, audio2_ringbuffer);\n}\n\n#define OVERVOLT 1\n\nint main()\n{\n#ifdef OVERVOLT\n\t/* set maximum 'allowed' voltage without voiding warranty */\n\tvreg_set_voltage(VREG_VOLTAGE_MAX);\n\tsleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US);\n#endif\n\n\thsdaoh_set_sys_clock_khz(SYS_CLK);\n\n\t/* set HSTX clock to sysclk/1 */\n\thw_write_masked(\n\t\t&clocks_hw->clk[clk_hstx].div,\n\t\t1 << CLOCKS_CLK_HSTX_DIV_INT_LSB,\n\t\tCLOCKS_CLK_HSTX_DIV_INT_BITS\n\t);\n\n#ifdef GPOUT_AUDIO_CLOCK\n\tpll_init(pll_usb, 1, 1536 * MHZ, 4, 2);\n\n\t/* set USB clock to clk_usb/4 */\n\thw_write_masked(&clocks_hw->clk[clk_usb].div, 4 << CLOCKS_CLK_USB_DIV_INT_LSB, CLOCKS_CLK_USB_DIV_INT_BITS);\n\n\t/* set GPOUT0 clock to USB PLL/10 -> 19.2 MHz, resulting in 75 kHz ADC sample rate (19.2M/256) */\n\tclock_gpio_init(21, CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 10);\n#endif\n\n\tstdio_init_all();\n\thsdaoh_init(GPIO_DRIVE_STRENGTH_12MA, GPIO_SLEW_RATE_FAST);\n\thsdaoh_add_stream(0, PIO_12BIT_DUAL, (SYS_CLK/8) * 1000, ADC_DATA_LEN, RBUF_DEFAULT_SLICES, ringbuffer);\n\thsdaoh_add_stream(AUDIO1_STREAM_ID, PIO_PCM1802_AUDIO, 78125, AUDIO_DATA_LEN, AUDIO_RBUF_SLICES, audio1_ringbuffer);\n\thsdaoh_add_stream(AUDIO2_STREAM_ID, PIO_PCM1802_AUDIO, 78125, AUDIO_DATA_LEN, AUDIO_RBUF_SLICES, audio2_ringbuffer);\n\thsdaoh_start();\n\tinit_pio_input();\n\tinit_audio_pio_input();\n\n\t/* synchronously start data input */\n\tpio_set_sm_mask_enabled(pio1, 3, true);\n\tpio_set_sm_mask_enabled(pio0, 1, true);\n\n\twhile (1)\n\t\t__wfi();\n}\n"
  },
  {
    "path": "apps/dual_external_adc/pcm1802_fmt00.pio",
    "content": ";\n; Copyright (c) 2023 Rene Wolf\n;\n; adapted for hsdaoh-rp2350:\n; Copyright (c) 2024 Steve Markgraf <steve@steve-m.de>\n;\n; SPDX-License-Identifier: BSD-3-Clause\n;\n; See PCM1802 data sheet, figure 25, format 00\n; This output technically has 4 wires, however we don't need the frame sync:\n; - Data is sampled on a positive edge of the bit clock\n; - LRClk changes on the preceeding  falling  edge of bit clock\n; - So we wait for an edge on LRClk which tells us\n;   - the next falling edge on bit clock is MSB of a channel\n;   - what channel follows determined by eitehr positive (left) or negative (right) edge\n; - Frame sync would tell us how many bits are valid, but we  know its 24\n\n\n.define PUBLIC pcm1802_index_data   0\n.define PUBLIC pcm1802_index_bitclk 1\n.define PUBLIC pcm1802_index_lrclk  2\n.define PUBLIC pcm1802_index_auxin  6\n.define polarity_left  1\n.define polarity_right 0\n.program pcm1802_fmt00\n\npublic entry_point:\n\nright_ch:\n\tset x, 1 ; set jump to left next\n\twait polarity_right pin pcm1802_index_lrclk ; right channel started, next rising edge on bitclk is msb\n\tjmp read_sample\nleft_ch:\n\tset x, 0 ; set jump to right next\n\twait polarity_left pin pcm1802_index_lrclk ; left channel started, next rising edge on bitclk is msb\n\tjmp read_sample\n\nread_sample:\n\tin x, 1\n\tset y, 23 ; load 24-1 into y, because jmp is a pre-decrement check\nread_bit:\n\twait 0 pin pcm1802_index_bitclk\n\twait 1 pin pcm1802_index_bitclk ; positive edge -> data valid now\n\tin pins, 1       ; read one bit from data pin into ISR\n\tjmp y-- read_bit ; jump if Y!=0 && decrement Y if jump taken\n\tpush\n\n\t; sample the auxilary input pin (e.g. for headswitch signal)\n\tmov osr, pins\n\tout null, pcm1802_index_auxin\n\tin osr, 1\n\n\tjmp !x right_ch  ; jmp to right_ch if X==0\n\tjmp left_ch      ; go for next sample start\n\n% c-sdk {\nstatic inline void pcm1802_fmt00_program_init(PIO pio, uint sm, uint offset, uint pin)\n{\n\tpio_sm_config c = pcm1802_fmt00_program_get_default_config(offset);\n\n\t// Set the IN base pin to the provided `pin` parameter.\n\tsm_config_set_in_pins(&c, pin);\n\n\tuint32_t pin_mask = (1 << pcm1802_index_auxin) |\n\t\t\t    (1 << pcm1802_index_lrclk) |\n\t\t\t    (1 << pcm1802_index_bitclk) |\n\t\t\t    (1 << pcm1802_index_data);\n\n\tpio_sm_set_pindirs_with_mask (pio, sm, 0, pin_mask << pin);\n\n\tsm_config_set_jmp_pin(&c, pin + pcm1802_index_data);\n\n\t// Connect these GPIOs to this PIO block\n\tpio_gpio_init(pio, pin + pcm1802_index_data);\n\tpio_gpio_init(pio, pin + pcm1802_index_bitclk);\n\tpio_gpio_init(pio, pin + pcm1802_index_lrclk);\n\tpio_gpio_init(pio, pin + pcm1802_index_auxin);\n\n\tsm_config_set_in_shift(\n\t\t&c,\n\t\tfalse,\t// Shift-to-right = false (i.e. shift to left)\n\t\tfalse,\t// Autopush disabled\n\t\t32\t// Autopush threshold: ignored\n\t);\n\n\t// required in order to set shift-to-right to true (for the out, null ops)\n\tsm_config_set_out_shift(\n\t\t&c,\n\t\ttrue,\t// Shift-to-right = true\n\t\tfalse,\t// Autopush disabled\n\t\t1\t// Autopush threshold: ignored\n\t);\n\n\t// We only receive, so disable the TX FIFO to make the RX FIFO deeper.\n\tsm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);\n\n\tsm_config_set_clkdiv(&c, 2.f);\n\n\t// Load our configuration, and start the program from the beginning\n\tpio_sm_init(pio, sm, offset, &c);\n//\tpio_sm_set_enabled(pio, sm, true);\n}\n%}\n"
  },
  {
    "path": "apps/external_adc/CMakeLists.txt",
    "content": "add_executable(external_adc\n\texternal_adc.c\n)\n\ntarget_compile_options(external_adc PRIVATE -Wall)\n\ntarget_link_libraries(external_adc\n\tpico_stdlib\n\tpico_util\n\thardware_pio\n\thardware_dma\n\tlibpicohsdaoh\n)\npico_generate_pio_header(external_adc ${CMAKE_CURRENT_LIST_DIR}/adc_12bit_input.pio)\npico_generate_pio_header(external_adc ${CMAKE_CURRENT_LIST_DIR}/pcm1802_fmt00.pio)\n\n# enable usb output, disable uart output\npico_enable_stdio_usb(external_adc 1)\n\n# create map/bin/hex file etc.\npico_add_extra_outputs(external_adc)\n"
  },
  {
    "path": "apps/external_adc/adc_12bit_input.pio",
    "content": ";\n; Copyright (c) 2024-2025 Steve Markgraf <steve@steve-m.de>\n;\n; SPDX-License-Identifier: BSD-3-Clause\n;\n; Sample 12 bit parallel ADC (AD9226) every 2 PIO cycles on rising clock edge,\n; pack four 12 bit samples in three 16 bit words\n; ADC clock output as side-set\n;\n; Data being pushed to the FIFO, four 12 bit samples A-D\n; First word:  A03 A02 A01 A00 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00\n; Second word: A07 A06 A05 A04 C11 C10 C09 C08 C07 C06 C05 C04 C03 C02 C01 C00\n; Third word:  A11 A10 A09 A08 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00\n\n.pio_version 1\n.program adc_12bit_input\n.side_set 2\n\npublic entry_point:\n\n.wrap_target\n\tmov osr, pins\tside 3\t; sample A\n\tout isr, 4\tside 0\n\n\tin pins, 12\tside 3\t; sample B, autopush\n\tout isr, 4\tside 0\n\n\tin pins, 12\tside 3\t; sample C, autopush\n\tout isr, 4\tside 0\n\n\tin pins, 12\tside 3\t; sample D, autopush\n\tnop\t\tside 0\n.wrap\n\n% c-sdk {\nstatic inline void adc_12bit_input_program_init(PIO pio, uint sm, uint offset, uint pin, uint clk_pin)\n{\n\tpio_sm_config c = adc_12bit_input_program_get_default_config(offset);\n\n\t// Set the IN base pin to the provided `pin` parameter.\n\tsm_config_set_in_pins(&c, pin);\n\n\t// configure CLK pin for side-set\n\tsm_config_set_sideset_pins(&c, clk_pin);\n\tsm_config_set_sideset(&c, 2, false, false);\n\n\tpio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 2, true);\n\tpio_gpio_init(pio, clk_pin);\n\tpio_gpio_init(pio, clk_pin+1);\n\n\t// Set the pin directions to input at the PIO\n\t// Set D0-D11 of the ADC as input\n\tpio_sm_set_consecutive_pindirs(pio, sm, pin, 12, false);\n\n\t// Connect these GPIOs to this PIO block\n\tfor (int i = pin; i < (pin+12); i++)\n\t\tpio_gpio_init(pio, i);\n\n\tsm_config_set_in_shift(\n\t\t&c,\n\t\tfalse,\t// Shift-to-right = false (i.e. shift to left)\n\t\ttrue,\t// Autopush enabled\n\t\t16\t// Autopush threshold = 16\n\t);\n\n\t// required in order to set shift-to-right to true (for the out ops)\n\tsm_config_set_out_shift(\n\t\t&c,\n\t\ttrue,\t// Shift-to-right = true\n\t\tfalse,\t// Autopush disabled\n\t\t1\t// Autopush threshold: ignored\n\t);\n\n\t// We only receive, so disable the TX FIFO to make the RX FIFO deeper.\n\tsm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);\n\n\tsm_config_set_clkdiv(&c, 2.f);\n\n\t// Load our configuration, and start the program from the beginning\n\tpio_sm_init(pio, sm, offset, &c);\n//\tpio_sm_set_enabled(pio, sm, true);\n}\n%}\n"
  },
  {
    "path": "apps/external_adc/external_adc.c",
    "content": "/*\n * hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks\n * Implementation for the Raspberry Pi RP2350 HSTX peripheral\n *\n * External 12-bit ADC example, connected to the PIO\n *\n * Copyright (c) 2024-2025 by Steve Markgraf <steve@steve-m.de>\n *\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the author nor the names of its contributors may\n *    be used to endorse or promote products derived from this software\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"pico/stdlib.h\"\n#include \"hardware/clocks.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/sync.h\"\n#include \"hardware/vreg.h\"\n#include \"hardware/pll.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/pio.h\"\n\n#include \"picohsdaoh.h\"\n#include \"adc_12bit_input.pio.h\"\n#include \"pcm1802_fmt00.pio.h\"\n\n/* The PIO is running with sys_clk/1, and needs 4 cycles per sample,\n * so the ADC clock is sys_clk/4 */\n#define SYS_CLK\t\t160000\t// 40 MHz ADC clock\n//#define SYS_CLK\t192000\t// 48 MHz ADC clock\n//#define SYS_CLK\t320000\t// 80 MHz ADC clock\n//#define SYS_CLK\t384000\t// 96 MHz ADC clock, maximum that works on my Pico2 (with overvolting)\n\n// For alignment of 3x16 bit words in the payload, so that every line starts with word 0\n#define ADC_DATA_LEN\t(RBUF_SLICE_LEN - 3)\n\n// Same here for 2x32 bit words\n#define AUDIO_DATA_LEN\t\t(RBUF_SLICE_LEN - 4)\n#define AUDIO_RBUF_SLICES\t8\n\n// ADC is attached to GP0 - GP11 with clock on GP20\n#define PIO_INPUT_PIN_BASE 0\n#define PIO_OUTPUT_CLK_PIN 20\n\n#define DMACH_PIO_PING 0\n#define DMACH_PIO_PONG 1\n\nstatic bool pio_dma_pong = false;\nuint16_t ringbuffer[RBUF_DEFAULT_TOTAL_LEN];\nint ringbuf_head = 2;\n\nvoid __scratch_y(\"\") pio_dma_irq_handler()\n{\n\tuint ch_num = pio_dma_pong ? DMACH_PIO_PONG : DMACH_PIO_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\tpio_dma_pong = !pio_dma_pong;\n\n\tringbuf_head = (ringbuf_head + 1) % RBUF_DEFAULT_SLICES;\n\n\tch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = ADC_DATA_LEN;\n\n\thsdaoh_update_head(0, ringbuf_head);\n}\n\nvoid init_pio_input(void)\n{\n\tPIO pio = pio0;\n\tuint offset = pio_add_program(pio, &adc_12bit_input_program);\n\tuint sm_data = pio_claim_unused_sm(pio, true);\n\tadc_12bit_input_program_init(pio, sm_data, offset, PIO_INPUT_PIN_BASE, PIO_OUTPUT_CLK_PIN);\n\n\tdma_channel_config c;\n\tc = dma_channel_get_default_config(DMACH_PIO_PING);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PONG);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PING,\n\t\t&c,\n\t\t&ringbuffer[0 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tADC_DATA_LEN,\n\t\tfalse\n\t);\n\tc = dma_channel_get_default_config(DMACH_PIO_PONG);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PING);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PONG,\n\t\t&c,\n\t\t&ringbuffer[1 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tADC_DATA_LEN,\n\t\tfalse\n\t);\n\n\tdma_hw->ints0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tdma_hw->inte0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tirq_set_exclusive_handler(DMA_IRQ_0, pio_dma_irq_handler);\n\tirq_set_enabled(DMA_IRQ_0, true);\n\n\tdma_channel_start(DMACH_PIO_PING);\n}\n\n#define PCM1802_DATA_PIN\t22\n\n#define DMACH_AUDIO_PIO_PING 2\n#define DMACH_AUDIO_PIO_PONG 3\n\nstatic bool audio_pio_dma_pong = false;\nuint16_t audio_ringbuffer[AUDIO_RBUF_SLICES * RBUF_SLICE_LEN];\nint audio_ringbuf_head = 2;\n\nvoid __scratch_y(\"\") audio_pio_dma_irq_handler()\n{\n\tuint ch_num = audio_pio_dma_pong ? DMACH_AUDIO_PIO_PONG : DMACH_AUDIO_PIO_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\taudio_pio_dma_pong = !audio_pio_dma_pong;\n\n\taudio_ringbuf_head = (audio_ringbuf_head + 1) % AUDIO_RBUF_SLICES;\n\n\tch->write_addr = (uintptr_t)&audio_ringbuffer[audio_ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = AUDIO_DATA_LEN/2;\n\n\thsdaoh_update_head(2, audio_ringbuf_head);\n}\n\nvoid init_audio_pio_input(void)\n{\n\tPIO pio = pio0;\n\tuint offset = pio_add_program(pio, &pcm1802_fmt00_program);\n\tuint sm_data = pio_claim_unused_sm(pio, true);\n\tpcm1802_fmt00_program_init(pio, sm_data, offset, PCM1802_DATA_PIN);\n\n\tdma_channel_config c;\n\tc = dma_channel_get_default_config(DMACH_AUDIO_PIO_PING);\n\tchannel_config_set_chain_to(&c, DMACH_AUDIO_PIO_PONG);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_32);\n\n\tdma_channel_configure(\n\t\tDMACH_AUDIO_PIO_PING,\n\t\t&c,\n\t\t&audio_ringbuffer[0 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tAUDIO_DATA_LEN/2,\n\t\tfalse\n\t);\n\tc = dma_channel_get_default_config(DMACH_AUDIO_PIO_PONG);\n\tchannel_config_set_chain_to(&c, DMACH_AUDIO_PIO_PING);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_32);\n\n\tdma_channel_configure(\n\t\tDMACH_AUDIO_PIO_PONG,\n\t\t&c,\n\t\t&audio_ringbuffer[1 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tAUDIO_DATA_LEN/2,\n\t\tfalse\n\t);\n\n\tdma_hw->ints1 |= (1u << DMACH_AUDIO_PIO_PING) | (1u << DMACH_AUDIO_PIO_PONG);\n\tdma_hw->inte1 |= (1u << DMACH_AUDIO_PIO_PING) | (1u << DMACH_AUDIO_PIO_PONG);\n\tirq_set_exclusive_handler(DMA_IRQ_1, audio_pio_dma_irq_handler);\n\tirq_set_enabled(DMA_IRQ_1, true);\n\n\tdma_channel_start(DMACH_AUDIO_PIO_PING);\n}\n\nint main()\n{\n#ifdef OVERVOLT\n\t/* set maximum 'allowed' voltage without voiding warranty */\n\tvreg_set_voltage(VREG_VOLTAGE_MAX);\n\tsleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US);\n#endif\n\n\thsdaoh_set_sys_clock_khz(SYS_CLK);\n\n\t/* set HSTX clock to sysclk/1 */\n\thw_write_masked(\n\t\t&clocks_hw->clk[clk_hstx].div,\n\t\t1 << CLOCKS_CLK_HSTX_DIV_INT_LSB,\n\t\tCLOCKS_CLK_HSTX_DIV_INT_BITS\n\t);\n\n#ifdef GPOUT_AUDIO_CLOCK\n\tpll_init(pll_usb, 1, 1536 * MHZ, 4, 2);\n\n\t/* set USB clock to clk_usb/4 */\n\thw_write_masked(&clocks_hw->clk[clk_usb].div, 4 << CLOCKS_CLK_USB_DIV_INT_LSB, CLOCKS_CLK_USB_DIV_INT_BITS);\n\n\t/* set GPOUT0 clock to USB PLL/10 -> 19.2 MHz, resulting in 75 kHz ADC sample rate (19.2M/256) */\n\tclock_gpio_init(21, CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 10);\n#endif\n\n\tstdio_init_all();\n\n\thsdaoh_init(GPIO_DRIVE_STRENGTH_4MA, GPIO_SLEW_RATE_SLOW);\n\thsdaoh_add_stream(0, PIO_12BIT, (SYS_CLK/4) * 1000, ADC_DATA_LEN, RBUF_DEFAULT_SLICES, ringbuffer);\n\thsdaoh_add_stream(2, PIO_PCM1802_AUDIO, 78125, AUDIO_DATA_LEN, AUDIO_RBUF_SLICES, audio_ringbuffer);\n\thsdaoh_start();\n\tinit_pio_input();\n\tinit_audio_pio_input();\n\n\t/* synchronously start data input */\n\tpio_set_sm_mask_enabled(pio0, 3, true);\n\n\twhile (1)\n\t\t__wfi();\n}\n"
  },
  {
    "path": "apps/external_adc/pcm1802_fmt00.pio",
    "content": ";\n; Copyright (c) 2023 Rene Wolf\n;\n; adapted for hsdaoh-rp2350:\n; Copyright (c) 2024 Steve Markgraf <steve@steve-m.de>\n;\n; SPDX-License-Identifier: BSD-3-Clause\n;\n; See PCM1802 data sheet, figure 25, format 00\n; This output technically has 4 wires, however we don't need the frame sync:\n; - Data is sampled on a positive edge of the bit clock\n; - LRClk changes on the preceeding  falling  edge of bit clock\n; - So we wait for an edge on LRClk which tells us\n;   - the next falling edge on bit clock is MSB of a channel\n;   - what channel follows determined by eitehr positive (left) or negative (right) edge\n; - Frame sync would tell us how many bits are valid, but we  know its 24\n\n\n.define PUBLIC pcm1802_index_data   0\n.define PUBLIC pcm1802_index_bitclk 4\n.define PUBLIC pcm1802_index_lrclk  5\n.define PUBLIC pcm1802_index_auxin  6\n.define polarity_left  1\n.define polarity_right 0\n.program pcm1802_fmt00\n\npublic entry_point:\n\nright_ch:\n\tset x, 1 ; set jump to left next\n\twait polarity_right pin pcm1802_index_lrclk ; right channel started, next rising edge on bitclk is msb\n\tjmp read_sample\nleft_ch:\n\tset x, 0 ; set jump to right next\n\twait polarity_left pin pcm1802_index_lrclk ; left channel started, next rising edge on bitclk is msb\n\tjmp read_sample\n\nread_sample:\n\tin x, 1\n\tset y, 23 ; load 24-1 into y, because jmp is a pre-decrement check\nread_bit:\n\twait 0 pin pcm1802_index_bitclk\n\twait 1 pin pcm1802_index_bitclk ; positive edge -> data valid now\n\tin pins, 1       ; read one bit from data pin into ISR\n\tjmp y-- read_bit ; jump if Y!=0 && decrement Y if jump taken\n\tpush\n\n\t; sample the auxilary input pin (e.g. for headswitch signal)\n\tmov osr, pins\n\tout null, pcm1802_index_auxin\n\tin osr, 1\n\n\tjmp !x right_ch  ; jmp to right_ch if X==0\n\tjmp left_ch      ; go for next sample start\n\n% c-sdk {\nstatic inline void pcm1802_fmt00_program_init(PIO pio, uint sm, uint offset, uint pin)\n{\n\tpio_sm_config c = pcm1802_fmt00_program_get_default_config(offset);\n\n\t// Set the IN base pin to the provided `pin` parameter.\n\tsm_config_set_in_pins(&c, pin);\n\n\tuint32_t pin_mask = (1 << pcm1802_index_auxin) |\n\t\t\t    (1 << pcm1802_index_lrclk) |\n\t\t\t    (1 << pcm1802_index_bitclk) |\n\t\t\t    (1 << pcm1802_index_data);\n\n\tpio_sm_set_pindirs_with_mask (pio, sm, 0, pin_mask << pin);\n\n\tsm_config_set_jmp_pin(&c, pin + pcm1802_index_data);\n\n\t// Connect these GPIOs to this PIO block\n\tpio_gpio_init(pio, pin + pcm1802_index_data);\n\tpio_gpio_init(pio, pin + pcm1802_index_bitclk);\n\tpio_gpio_init(pio, pin + pcm1802_index_lrclk);\n\tpio_gpio_init(pio, pin + pcm1802_index_auxin);\n\n\tsm_config_set_in_shift(\n\t\t&c,\n\t\tfalse,\t// Shift-to-right = false (i.e. shift to left)\n\t\tfalse,\t// Autopush disabled\n\t\t32\t// Autopush threshold: ignored\n\t);\n\n\t// required in order to set shift-to-right to true (for the out, null ops)\n\tsm_config_set_out_shift(\n\t\t&c,\n\t\ttrue,\t// Shift-to-right = true\n\t\tfalse,\t// Autopush disabled\n\t\t1\t// Autopush threshold: ignored\n\t);\n\n\t// We only receive, so disable the TX FIFO to make the RX FIFO deeper.\n\tsm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);\n\n\tsm_config_set_clkdiv(&c, 2.f);\n\n\t// Load our configuration, and start the program from the beginning\n\tpio_sm_init(pio, sm, offset, &c);\n//\tpio_sm_set_enabled(pio, sm, true);\n}\n%}\n"
  },
  {
    "path": "apps/external_dualchan_adc/CMakeLists.txt",
    "content": "add_executable(external_dualchan_adc\n\texternal_dualchan_adc.c\n)\n\ntarget_compile_options(external_dualchan_adc PRIVATE -Wall)\n\ntarget_link_libraries(external_dualchan_adc\n\tpico_stdlib\n\tpico_util\n\thardware_pio\n\thardware_dma\n\tlibpicohsdaoh\n)\npico_generate_pio_header(external_dualchan_adc ${CMAKE_CURRENT_LIST_DIR}/dualchan_adc_12bit_input.pio)\npico_generate_pio_header(external_dualchan_adc ${CMAKE_CURRENT_LIST_DIR}/../external_adc/pcm1802_fmt00.pio)\n\n# enable usb output, disable uart output\npico_enable_stdio_usb(external_dualchan_adc 1)\n\n# create map/bin/hex file etc.\npico_add_extra_outputs(external_dualchan_adc)\n"
  },
  {
    "path": "apps/external_dualchan_adc/dualchan_adc_12bit_input.pio",
    "content": ";\n; Copyright (c) 2024-2025 Steve Markgraf <steve@steve-m.de>\n;\n; SPDX-License-Identifier: BSD-3-Clause\n;\n; Sample 12 bit dualchannel parallel ADC (AD9238) every 2 PIO cycles on both\n; clock edges (DDR), pack four 12 bit samples in three 16 bit words\n; ADC clock output as side-set\n;\n; Data being pushed to the FIFO, four 12 bit samples A-D\n; First word:  A03 A02 A01 A00 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00\n; Second word: A07 A06 A05 A04 C11 C10 C09 C08 C07 C06 C05 C04 C03 C02 C01 C00\n; Third word:  A11 A10 A09 A08 D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00\n\n.pio_version 1\n.program dualchan_adc_12bit_input\n.side_set 2\n\npublic entry_point:\n\n.wrap_target\n\tmov osr, pins\tside 3\t; sample A\n\tout isr, 4\tside 0\n\n\tin pins, 12\tside 0\t; sample B, autopush\n\tout isr, 4\tside 3\n\n\tin pins, 12\tside 3\t; sample C, autopush\n\tout isr, 4\tside 0\n\n\tin pins, 12\tside 0\t; sample D, autopush\n\tnop\t\tside 3\n.wrap\n\n% c-sdk {\nstatic inline void dualchan_adc_12bit_input_program_init(PIO pio, uint sm, uint offset, uint pin, uint clk_pin)\n{\n\tpio_sm_config c = dualchan_adc_12bit_input_program_get_default_config(offset);\n\n\t// Set the IN base pin to the provided `pin` parameter.\n\tsm_config_set_in_pins(&c, pin);\n\n\t// configure CLK pin for side-set\n\tsm_config_set_sideset_pins(&c, clk_pin);\n\tsm_config_set_sideset(&c, 2, false, false);\n\n\tpio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 2, true);\n\tpio_gpio_init(pio, clk_pin);\n\tpio_gpio_init(pio, clk_pin+1);\n\n\t// Set the pin directions to input at the PIO\n\t// Set D0-D11 of the ADC as input\n\tpio_sm_set_consecutive_pindirs(pio, sm, pin, 12, false);\n\n\t// Connect these GPIOs to this PIO block\n\tfor (int i = pin; i < (pin+12); i++)\n\t\tpio_gpio_init(pio, i);\n\n\tsm_config_set_in_shift(\n\t\t&c,\n\t\tfalse,\t// Shift-to-right = false (i.e. shift to left)\n\t\ttrue,\t// Autopush enabled\n\t\t16\t// Autopush threshold = 16\n\t);\n\n\t// required in order to set shift-to-right to true (for the out, null ops)\n\tsm_config_set_out_shift(\n\t\t&c,\n\t\ttrue,\t// Shift-to-right = true\n\t\tfalse,\t// Autopush disabled\n\t\t1\t// Autopush threshold: ignored\n\t);\n\n\t// We only receive, so disable the TX FIFO to make the RX FIFO deeper.\n\tsm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);\n\n\tsm_config_set_clkdiv(&c, 2.f);\n\n\t// Load our configuration, and start the program from the beginning\n\tpio_sm_init(pio, sm, offset, &c);\n//\tpio_sm_set_enabled(pio, sm, true);\n}\n%}\n"
  },
  {
    "path": "apps/external_dualchan_adc/external_dualchan_adc.c",
    "content": "/*\n * hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks\n * Implementation for the Raspberry Pi RP2350 HSTX peripheral\n *\n * External dualchannel 12-bit ADC example, connected to the PIO\n *\n * Copyright (c) 2024-2025 by Steve Markgraf <steve@steve-m.de>\n *\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the author nor the names of its contributors may\n *    be used to endorse or promote products derived from this software\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"pico/stdlib.h\"\n#include \"hardware/clocks.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/sync.h\"\n#include \"hardware/vreg.h\"\n#include \"hardware/pll.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/pio.h\"\n\n#include \"picohsdaoh.h\"\n#include \"dualchan_adc_12bit_input.pio.h\"\n#include \"pcm1802_fmt00.pio.h\"\n\n/* The PIO is running with sys_clk/1, and needs 4 cycles per sample,\n * so the ADC clock is sys_clk/4 */\n#define SYS_CLK\t\t320000\t// 40 MHz ADC clock\n\n// For alignment of 3x16 bit words in the payload, so that every line starts with word 0\n#define ADC_DATA_LEN\t(RBUF_SLICE_LEN - 3)\n\n// Same here for 2x32 bit words\n#define AUDIO_DATA_LEN\t\t(RBUF_SLICE_LEN - 4)\n#define AUDIO_RBUF_SLICES\t8\n\n// ADC is attached to GP0 - GP11 with clock on GP20\n#define PIO_INPUT_PIN_BASE 0\n#define PIO_OUTPUT_CLK_PIN 20\n\n#define DMACH_PIO_PING 0\n#define DMACH_PIO_PONG 1\n\nstatic bool pio_dma_pong = false;\nuint16_t ringbuffer[RBUF_DEFAULT_TOTAL_LEN];\nint ringbuf_head = 2;\n\nvoid __scratch_y(\"\") pio_dma_irq_handler()\n{\n\tuint ch_num = pio_dma_pong ? DMACH_PIO_PONG : DMACH_PIO_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\tpio_dma_pong = !pio_dma_pong;\n\n\tringbuf_head = (ringbuf_head + 1) % RBUF_DEFAULT_SLICES;\n\n\tch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = ADC_DATA_LEN;\n\n\thsdaoh_update_head(0, ringbuf_head);\n}\n\nvoid init_pio_input(void)\n{\n\tPIO pio = pio0;\n\tuint offset = pio_add_program(pio, &dualchan_adc_12bit_input_program);\n\tuint sm_data = pio_claim_unused_sm(pio, true);\n\tdualchan_adc_12bit_input_program_init(pio, sm_data, offset, PIO_INPUT_PIN_BASE, PIO_OUTPUT_CLK_PIN);\n\n\tdma_channel_config c;\n\tc = dma_channel_get_default_config(DMACH_PIO_PING);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PONG);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PING,\n\t\t&c,\n\t\t&ringbuffer[0 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tADC_DATA_LEN,\n\t\tfalse\n\t);\n\tc = dma_channel_get_default_config(DMACH_PIO_PONG);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PING);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PONG,\n\t\t&c,\n\t\t&ringbuffer[1 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tADC_DATA_LEN,\n\t\tfalse\n\t);\n\n\tdma_hw->ints0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tdma_hw->inte0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tirq_set_exclusive_handler(DMA_IRQ_0, pio_dma_irq_handler);\n\tirq_set_enabled(DMA_IRQ_0, true);\n\n\tdma_channel_start(DMACH_PIO_PING);\n}\n\n#define PCM1802_DATA_PIN\t22\n\n#define DMACH_AUDIO_PIO_PING 2\n#define DMACH_AUDIO_PIO_PONG 3\n\nstatic bool audio_pio_dma_pong = false;\nuint16_t audio_ringbuffer[AUDIO_RBUF_SLICES * RBUF_SLICE_LEN];\nint audio_ringbuf_head = 2;\n\nvoid __scratch_y(\"\") audio_pio_dma_irq_handler()\n{\n\tuint ch_num = audio_pio_dma_pong ? DMACH_AUDIO_PIO_PONG : DMACH_AUDIO_PIO_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\taudio_pio_dma_pong = !audio_pio_dma_pong;\n\n\taudio_ringbuf_head = (audio_ringbuf_head + 1) % AUDIO_RBUF_SLICES;\n\n\tch->write_addr = (uintptr_t)&audio_ringbuffer[audio_ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = AUDIO_DATA_LEN/2;\n\n\thsdaoh_update_head(2, audio_ringbuf_head);\n}\n\nvoid init_audio_pio_input(void)\n{\n\tPIO pio = pio0;\n\tuint offset = pio_add_program(pio, &pcm1802_fmt00_program);\n\tuint sm_data = pio_claim_unused_sm(pio, true);\n\tpcm1802_fmt00_program_init(pio, sm_data, offset, PCM1802_DATA_PIN);\n\n\tdma_channel_config c;\n\tc = dma_channel_get_default_config(DMACH_AUDIO_PIO_PING);\n\tchannel_config_set_chain_to(&c, DMACH_AUDIO_PIO_PONG);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_32);\n\n\tdma_channel_configure(\n\t\tDMACH_AUDIO_PIO_PING,\n\t\t&c,\n\t\t&audio_ringbuffer[0 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tAUDIO_DATA_LEN/2,\n\t\tfalse\n\t);\n\tc = dma_channel_get_default_config(DMACH_AUDIO_PIO_PONG);\n\tchannel_config_set_chain_to(&c, DMACH_AUDIO_PIO_PING);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_32);\n\n\tdma_channel_configure(\n\t\tDMACH_AUDIO_PIO_PONG,\n\t\t&c,\n\t\t&audio_ringbuffer[1 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tAUDIO_DATA_LEN/2,\n\t\tfalse\n\t);\n\n\tdma_hw->ints1 |= (1u << DMACH_AUDIO_PIO_PING) | (1u << DMACH_AUDIO_PIO_PONG);\n\tdma_hw->inte1 |= (1u << DMACH_AUDIO_PIO_PING) | (1u << DMACH_AUDIO_PIO_PONG);\n\tirq_set_exclusive_handler(DMA_IRQ_1, audio_pio_dma_irq_handler);\n\tirq_set_enabled(DMA_IRQ_1, true);\n\n\tdma_channel_start(DMACH_AUDIO_PIO_PING);\n}\n\n#define OVERVOLT 1\n\nint main()\n{\n#ifdef OVERVOLT\n\t/* set maximum 'allowed' voltage without voiding warranty */\n\tvreg_set_voltage(VREG_VOLTAGE_MAX);\n\tsleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US);\n#endif\n\n\thsdaoh_set_sys_clock_khz(SYS_CLK);\n\n\t/* set HSTX clock to sysclk/1 */\n\thw_write_masked(\n\t\t&clocks_hw->clk[clk_hstx].div,\n\t\t1 << CLOCKS_CLK_HSTX_DIV_INT_LSB,\n\t\tCLOCKS_CLK_HSTX_DIV_INT_BITS\n\t);\n\n\tstdio_init_all();\n\n\thsdaoh_init(GPIO_DRIVE_STRENGTH_12MA, GPIO_SLEW_RATE_FAST);\n\thsdaoh_add_stream(0, PIO_DUALCHAN_12BIT, (SYS_CLK/4) * 1000, ADC_DATA_LEN, RBUF_DEFAULT_SLICES, ringbuffer);\n\thsdaoh_add_stream(2, PIO_PCM1802_AUDIO, 78125, AUDIO_DATA_LEN, AUDIO_RBUF_SLICES, audio_ringbuffer);\n\thsdaoh_start();\n\tinit_pio_input();\n\tinit_audio_pio_input();\n\n\t/* synchronously start data input */\n\tpio_set_sm_mask_enabled(pio0, 3, true);\n\n\twhile (1)\n\t\t__wfi();\n}\n"
  },
  {
    "path": "apps/internal_adc/CMakeLists.txt",
    "content": "add_executable(internal_adc\n\tinternal_adc.c\n)\n\ntarget_compile_options(internal_adc PRIVATE -Wall)\n\ntarget_link_libraries(internal_adc\n\tpico_stdlib\n\thardware_adc\n\thardware_dma\n\tpico_util\n\tlibpicohsdaoh\n)\n\n# enable usb output, disable uart output\npico_enable_stdio_usb(internal_adc 1)\n\n# create map/bin/hex file etc.\npico_add_extra_outputs(internal_adc)\n"
  },
  {
    "path": "apps/internal_adc/internal_adc.c",
    "content": "/*\n * hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks\n * Implementation for the Raspberry Pi RP2350 HSTX peripheral\n *\n * Internal ADC example, overclocked to 3,33 MHz sample rate\n * When using the USB PLL for the ADC, almost 8 MHz sample rate\n * can be achieved!\n *\n * Copyright (c) 2024 by Steve Markgraf <steve@steve-m.de>\n *\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the author nor the names of its contributors may\n *    be used to endorse or promote products derived from this software\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"pico/stdlib.h\"\n#include \"hardware/clocks.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/sync.h\"\n#include \"hardware/vreg.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/adc.h\"\n#include \"hardware/pll.h\"\n\n#include \"picohsdaoh.h\"\n\n#define SYS_CLK\t\t320000\n\n// Channel 0 is GPIO26\n#define CAPTURE_CHANNEL 1\n\n#define DMACH_ADC_PING 0\n#define DMACH_ADC_PONG 1\n\nstatic bool dma_adc_pong = false;\nuint16_t ringbuffer[RBUF_DEFAULT_TOTAL_LEN];\nint ringbuf_head = 2;\n\nvoid __scratch_y(\"\") adc_dma_irq_handler()\n{\n\tuint ch_num = dma_adc_pong ? DMACH_ADC_PONG : DMACH_ADC_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\tdma_adc_pong = !dma_adc_pong;\n\n\tringbuf_head = (ringbuf_head + 1) % RBUF_DEFAULT_SLICES;\n\n\tch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = RBUF_MAX_DATA_LEN;\n\n\thsdaoh_update_head(0, ringbuf_head);\n}\n\nvoid init_adc_input(void)\n{\n\tadc_init();\n\tadc_select_input(CAPTURE_CHANNEL);\n\n\tadc_fifo_setup(\n\t\ttrue,\t// Write each completed conversion to the sample FIFO\n\t\ttrue,\t// Enable DMA data request (DREQ)\n\t\t1,\t// DREQ (and IRQ) asserted when at least 1 sample present\n\t\tfalse,\t// Disable the ERR bit\n\t\tfalse\t// No shift of samples, use full 12 bit resolution\n\t);\n\n\tadc_set_clkdiv(0);\n\n\tdma_channel_config c;\n\tc = dma_channel_get_default_config(DMACH_ADC_PING);\n\tchannel_config_set_chain_to(&c, DMACH_ADC_PONG);\n\tchannel_config_set_dreq(&c, DREQ_ADC);\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_ADC_PING,\n\t\t&c,\n\t\t&ringbuffer[0 * RBUF_SLICE_LEN],\n\t\t&adc_hw->fifo,\n\t\tRBUF_MAX_DATA_LEN,\n\t\tfalse\n\t);\n\tc = dma_channel_get_default_config(DMACH_ADC_PONG);\n\tchannel_config_set_chain_to(&c, DMACH_ADC_PING);\n\tchannel_config_set_dreq(&c, DREQ_ADC);\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_ADC_PONG,\n\t\t&c,\n\t\t&ringbuffer[1 * RBUF_SLICE_LEN],\n\t\t&adc_hw->fifo,\n\t\tRBUF_MAX_DATA_LEN,\n\t\tfalse\n\t);\n\n\tdma_hw->ints0 |= (1u << DMACH_ADC_PING) | (1u << DMACH_ADC_PONG);\n\tdma_hw->inte0 |= (1u << DMACH_ADC_PING) | (1u << DMACH_ADC_PONG);\n\tirq_set_exclusive_handler(DMA_IRQ_0, adc_dma_irq_handler);\n\tirq_set_enabled(DMA_IRQ_0, true);\n\n\tdma_channel_start(DMACH_ADC_PING);\n\tadc_run(true);\n}\n\nint main()\n{\n\t/* set maximum 'allowed' voltage without voiding warranty */\n\tvreg_set_voltage(VREG_VOLTAGE_MAX);\n\tsleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US);\n\n\thsdaoh_set_sys_clock_khz(SYS_CLK);\n\n\t/* set HSTX clock to sysclk/3 */\n\thw_write_masked(\n\t\t&clocks_hw->clk[clk_hstx].div,\n\t\t3 << CLOCKS_CLK_HSTX_DIV_INT_LSB,\n\t\tCLOCKS_CLK_HSTX_DIV_INT_BITS\n\t);\n\n\t/* switch ADC clock source to sys_clk */\n\thw_write_masked(\n\t\t&clocks_hw->clk[clk_adc].ctrl,\n\t\tCLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS << CLOCKS_CLK_ADC_CTRL_AUXSRC_LSB,\n\t\tCLOCKS_CLK_ADC_CTRL_AUXSRC_BITS\n\t);\n\n\tstdio_init_all();\n\n\thsdaoh_init(GPIO_DRIVE_STRENGTH_4MA, GPIO_SLEW_RATE_SLOW);\n\thsdaoh_add_stream(0, 1, (SYS_CLK/8) * 1000, RBUF_MAX_DATA_LEN, RBUF_DEFAULT_SLICES, ringbuffer);\n\thsdaoh_start();\n\tinit_adc_input();\n\n\twhile (1)\n\t\t__wfi();\n}\n"
  },
  {
    "path": "apps/logic_analyzer/16bit_input.pio",
    "content": ";\n; Copyright (c) 2024 Steve Markgraf <steve@steve-m.de>\n;\n; SPDX-License-Identifier: BSD-3-Clause\n;\n; 16 bit logic analyzer\n;\n\n.pio_version 0\n.program la_16bit_input\n\npublic entry_point:\n\n.wrap_target\n\t; GP0 - GP11 are contiguous, then come the HSTX pins which are already in use\n\t; Next pins we can use are 20, 21, 22 and then 26, 27, 28\n\tmov osr, pins\t; Sample all 32 pins\n\tin osr, 12\t; shift values of GP0-GP11 to ISR\n\tout null, 20\t; shift out GP0-GP11 and discard GP12-GP19\n\tin osr, 3\t; shift in GP20 - GP22\n\tout null, 6\t; drop GP20 - GP25\n\tin osr, 1\t; shift in GP26 - now the ISR is full and the autopush is happening\n\tin null, 16\t; fill with zeroes so that data is in lower 16 bits\n\tnop [3]\n.wrap\n\n% c-sdk {\nstatic inline void la_16bit_input_program_init(PIO pio, uint sm, uint offset, uint pin)\n{\n\tpio_sm_config c = la_16bit_input_program_get_default_config(offset);\n\n\t// Set the IN base pin to the provided `pin` parameter.\n\tsm_config_set_in_pins(&c, pin);\n\n\t// Set the pin directions to input at the PIO\n\t// Set D0-D11 of the ADC as input\n\tpio_sm_set_consecutive_pindirs(pio, sm, pin, 27, false);\n\n\t// Connect these GPIOs to this PIO block\n\tfor (int i = pin; i < (pin+12); i++) {\n\t\tpio_gpio_init(pio, pin + i);\n\t\tgpio_set_pulls(pin + i, false, false);\n\t}\n\n\t// Connect these GPIOs to this PIO block\n\tfor (int i = 20; i < 23; i++) {\n\t\tpio_gpio_init(pio, i);\n\t\tgpio_set_pulls(i, false, false);\n\t}\n\n\tpio_gpio_init(pio, 26);\n\tgpio_set_pulls(26, false, false);\n\n\tsm_config_set_in_shift(\n\t\t&c,\n\t\ttrue,\t// Shift-to-right = true\n\t\ttrue,\t// Autopush enabled\n\t\t32\t// Autopush threshold = 32\n\t);\n\n\t// required in order to set shift-to-right to true (for the out, null ops)\n\tsm_config_set_out_shift(\n\t\t&c,\n\t\ttrue,\t// Shift-to-right = true\n\t\tfalse,\t// Autopush disabled\n\t\t1\t// Autopush threshold: ignored\n\t);\n\n\t// We only receive, so disable the TX FIFO to make the RX FIFO deeper.\n\tsm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);\n\n\tsm_config_set_clkdiv(&c, 1.f);\n\n\t// Load our configuration, and start the program from the beginning\n\tpio_sm_init(pio, sm, offset, &c);\n\tpio_sm_set_enabled(pio, sm, true);\n}\n%}\n"
  },
  {
    "path": "apps/logic_analyzer/CMakeLists.txt",
    "content": "add_executable(logic_analyzer\n\tlogic_analyzer.c\n)\n\ntarget_compile_options(logic_analyzer PRIVATE -Wall)\n\ntarget_link_libraries(logic_analyzer\n\tpico_stdlib\n\tpico_util\n\thardware_pio\n\thardware_dma\n\tlibpicohsdaoh\n)\npico_generate_pio_header(logic_analyzer ${CMAKE_CURRENT_LIST_DIR}/16bit_input.pio)\n\n# enable usb output, disable uart output\npico_enable_stdio_usb(logic_analyzer 1)\n\n# create map/bin/hex file etc.\npico_add_extra_outputs(logic_analyzer)\n"
  },
  {
    "path": "apps/logic_analyzer/logic_analyzer.c",
    "content": "/*\n * hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks\n * Implementation for the Raspberry Pi RP2350 HSTX peripheral\n *\n * 16 bit logic analyzer example\n *\n * Copyright (c) 2024 by Steve Markgraf <steve@steve-m.de>\n *\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the author nor the names of its contributors may\n *    be used to endorse or promote products derived from this software\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"pico/stdlib.h\"\n#include \"hardware/clocks.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/sync.h\"\n#include \"hardware/vreg.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/pio.h\"\n\n#include \"picohsdaoh.h\"\n#include \"16bit_input.pio.h\"\n\n/* The PIO is running with sys_clk, and needs 10 cycles per sample,\n * so the LA is sampling with 32 MHz */\n#define SYS_CLK\t\t320000\n\n#define PIO_INPUT_PIN_BASE 0\n\n#define DMACH_PIO_PING 0\n#define DMACH_PIO_PONG 1\n\nstatic bool pio_dma_pong = false;\nuint16_t ringbuffer[RBUF_DEFAULT_TOTAL_LEN];\nint ringbuf_head = 2;\n\nvoid __scratch_y(\"\") pio_dma_irq_handler()\n{\n\tuint ch_num = pio_dma_pong ? DMACH_PIO_PONG : DMACH_PIO_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\tpio_dma_pong = !pio_dma_pong;\n\n\tringbuf_head = (ringbuf_head + 1) % RBUF_DEFAULT_SLICES;\n\n\tch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = RBUF_MAX_DATA_LEN;\n\n\thsdaoh_update_head(0, ringbuf_head);\n}\n\nvoid init_pio_input(void)\n{\n\tPIO pio = pio0;\n\tuint offset = pio_add_program(pio, &la_16bit_input_program);\n\tuint sm_data = pio_claim_unused_sm(pio, true);\n\tla_16bit_input_program_init(pio, sm_data, offset, PIO_INPUT_PIN_BASE);\n\n\tdma_channel_config c;\n\tc = dma_channel_get_default_config(DMACH_PIO_PING);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PONG);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PING,\n\t\t&c,\n\t\t&ringbuffer[0 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tRBUF_MAX_DATA_LEN,\n\t\tfalse\n\t);\n\tc = dma_channel_get_default_config(DMACH_PIO_PONG);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PING);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PONG,\n\t\t&c,\n\t\t&ringbuffer[1 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tRBUF_MAX_DATA_LEN,\n\t\tfalse\n\t);\n\n\tdma_hw->ints0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tdma_hw->inte0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tirq_set_exclusive_handler(DMA_IRQ_0, pio_dma_irq_handler);\n\tirq_set_enabled(DMA_IRQ_0, true);\n\n\tdma_channel_start(DMACH_PIO_PING);\n}\n\nint main()\n{\n\t/* set maximum 'allowed' voltage without voiding warranty */\n\tvreg_set_voltage(VREG_VOLTAGE_MAX);\n\tsleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US);\n\n\thsdaoh_set_sys_clock_khz(SYS_CLK);\n\n\t/* set HSTX clock to sysclk/2 */\n\thw_write_masked(\n\t\t&clocks_hw->clk[clk_hstx].div,\n\t\t2 << CLOCKS_CLK_HSTX_DIV_INT_LSB,\n\t\tCLOCKS_CLK_HSTX_DIV_INT_BITS\n\t);\n\n\tstdio_init_all();\n\n\thsdaoh_init(GPIO_DRIVE_STRENGTH_4MA, GPIO_SLEW_RATE_SLOW);\n\thsdaoh_add_stream(0, 1, (SYS_CLK/8) * 1000, RBUF_MAX_DATA_LEN, RBUF_DEFAULT_SLICES, ringbuffer);\n\thsdaoh_start();\n\tinit_pio_input();\n\n\twhile (1)\n\t\t__wfi();\n}\n"
  },
  {
    "path": "apps/sdr/CMakeLists.txt",
    "content": "add_executable(sdr\n\tsdr.c\n)\n\ntarget_compile_options(sdr PRIVATE -Wall)\n\ntarget_link_libraries(sdr\n\tpico_stdlib\n\tpico_util\n\thardware_pio\n\thardware_dma\n\thardware_i2c\n\tlibpicohsdaoh\n)\npico_generate_pio_header(sdr ${CMAKE_CURRENT_LIST_DIR}/adc_16bit_input.pio)\npico_generate_pio_header(sdr ${CMAKE_CURRENT_LIST_DIR}/adc_20bit_input.pio)\n\n# enable usb output, disable uart output\npico_enable_stdio_usb(sdr 1)\n\n# create map/bin/hex file etc.\npico_add_extra_outputs(sdr)\n"
  },
  {
    "path": "apps/sdr/adc_16bit_input.pio",
    "content": ";\n; Copyright (c) 2025 Steve Markgraf <steve@steve-m.de>\n;\n; SPDX-License-Identifier: BSD-3-Clause\n;\n; Sample 2x10 bit parallel ADC (AD9218) every 4 PIO cycles on rising clock edge,\n; pack four 20 bit samples in five 16 bit words\n; ADC clock output as side-set\n;\n; Data being pushed to the FIFO, four 20 bit samples A-D\n; First word:  A15 A14 A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00\n; Second word: A19 A18 A17 A16 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00\n; Third word:  B19 B18 B17 B16 B15 B14 B13 B12 C07 C06 C05 C04 C03 C02 C01 C00\n; Fourth word: C19 C18 C17 C16 C15 C14 C13 C12 C11 C10 C09 C08 D03 D02 D01 D00\n; Fifth word:  D19 D18 D17 D16 D15 D14 D13 D12 D11 D10 D09 D08 D07 D06 D05 D04\n\n.pio_version 1\n.program adc_16bit_input\n.side_set 1\n\npublic entry_point:\n\n.wrap_target\n\tin pins, 16\tside 1\n\tnop\t\tside 0\n\tnop\t\tside 0\n\tnop\t\tside 1\n.wrap\n\n% c-sdk {\nstatic inline void adc_16bit_input_program_init(PIO pio, uint sm, uint offset, uint pin, uint clk_pin)\n{\n\tpio_sm_config c = adc_16bit_input_program_get_default_config(offset);\n\n\t// Set the IN base pin to the provided `pin` parameter.\n\tsm_config_set_in_pins(&c, pin);\n\n\t// configure CLK pin for side-set\n\tsm_config_set_sideset_pins(&c, clk_pin);\n\tsm_config_set_sideset(&c, 1, false, false);\n\n\tpio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 1, true);\n\tpio_gpio_init(pio, clk_pin);\n\n\tgpio_disable_pulls(clk_pin);\n//\tgpio_set_drive_strength(clk_pin, GPIO_DRIVE_STRENGTH_12MA);\n//\tgpio_set_slew_rate(clk_pin, GPIO_SLEW_RATE_FAST);\n\n\t// Set the pin directions to input at the PIO\n\t// Set D0-D19 of the ADC(s) as input\n\tpio_sm_set_consecutive_pindirs(pio, sm, pin, 20, false);\n\n\t// Connect these GPIOs to this PIO block\n\tfor (int i = pin; i < (pin+20); i++) {\n\t\t//gpio_pull_down(i);\n\t\tpio_gpio_init(pio, i);\n\t}\n\n\tsm_config_set_in_shift(\n\t\t&c,\n\t\tfalse,\t// Shift-to-right = false (i.e. shift to left)\n\t\ttrue,\t// Autopush enabled\n\t\t16\t// Autopush threshold = 16\n\t);\n\n\t// required in order to set shift-to-right to true (for the out, null ops)\n\tsm_config_set_out_shift(\n\t\t&c,\n\t\ttrue,\t// Shift-to-right = true\n\t\tfalse,\t// Autopush disabled\n\t\t1\t// Autopush threshold: ignored\n\t);\n\n\t// We only receive, so disable the TX FIFO to make the RX FIFO deeper.\n\tsm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);\n\n\tsm_config_set_clkdiv(&c, 1.00f);\n\n\t// Load our configuration, and start the program from the beginning\n\tpio_sm_init(pio, sm, offset, &c);\n\tpio_sm_set_enabled(pio, sm, true);\n}\n%}\n"
  },
  {
    "path": "apps/sdr/adc_20bit_input.pio",
    "content": ";\n; Copyright (c) 2025 Steve Markgraf <steve@steve-m.de>\n;\n; SPDX-License-Identifier: BSD-3-Clause\n;\n; Sample 2x10 bit parallel ADC (AD9218) every 4 PIO cycles on rising clock edge,\n; pack four 20 bit samples in five 16 bit words\n; ADC clock output as side-set\n;\n; Data being pushed to the FIFO, four 20 bit samples A-D\n; First word:  A15 A14 A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00\n; Second word: A19 A18 A17 A16 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00\n; Third word:  B19 B18 B17 B16 B15 B14 B13 B12 C07 C06 C05 C04 C03 C02 C01 C00\n; Fourth word: C19 C18 C17 C16 C15 C14 C13 C12 C11 C10 C09 C08 D03 D02 D01 D00\n; Fifth word:  D19 D18 D17 D16 D15 D14 D13 D12 D11 D10 D09 D08 D07 D06 D05 D04\n\n.pio_version 1\n.program adc_20bit_input\n.side_set 1\n\npublic entry_point:\n\n.wrap_target\n\t;----------------------------------------------------------------------------------------\n\tmov osr, pins\tside 0\t\t\t; SAMP A\n\n\t; OSR is now A19 A18 A17 A16 A15 A14 A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00\n\n\tin osr, 16\tside 1\t\t\t; AUTOPUSH: A15 A14 A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00\n\tout null, 16\tside 1\n\tin osr, 4\tside 0\t\t\t; ISR is now A19 A18 A17 A16\n\t;----------------------------------------------------------------------------------------\n\tmov osr, pins\tside 0\t\t\t; SAMP B\n\n\tin osr, 12\tside 1\t\t\t; AUTOPUSH: A19 A18 A17 A16 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00\n\tout null, 12\tside 1\n\tin osr, 8\tside 0\t\t\t; ISR is now B19 B18 B17 B16 B15 B14 B13 B12\n\t;----------------------------------------------------------------------------------------\n\tmov osr, pins\tside 0\t\t\t; SAMP C\n\n\tin osr, 8\tside 1\t\t\t; AUTOPUSH: B19 B18 B17 B16 B15 B14 B13 B12 C07 C06 C05 C04 C03 C02 C01 C00\n\tout null, 8\tside 1\n\tin osr, 12\tside 0\t\t\t; ISR is now C19 C18 C17 C16 C15 C14 C13 C12 C11 C10 C09 C08\n\t;----------------------------------------------------------------------------------------\n\tmov osr, pins\tside 0\t\t\t; SAMP D\n\n\tin osr, 4\tside 1\t\t\t; AUTOPUSH: C19 C18 C17 C16 C15 C14 C13 C12 C11 C10 C09 C08 D03 D02 D01 D00\n\tout null, 4\tside 1\n\tin osr, 16\tside 0\t\t\t; AUTOPUSH: D19 D18 D17 D16 D15 D14 D13 D12 D11 D10 D09 D08 D07 D06 D05 D04\n\t;----------------------------------------------------------------------------------------\n.wrap\n\n% c-sdk {\nstatic inline void adc_20bit_input_program_init(PIO pio, uint sm, uint offset, uint pin, uint clk_pin)\n{\n\tpio_sm_config c = adc_20bit_input_program_get_default_config(offset);\n\n\t// Set the IN base pin to the provided `pin` parameter.\n\tsm_config_set_in_pins(&c, pin);\n\n\t// configure CLK pin for side-set\n\tsm_config_set_sideset_pins(&c, clk_pin);\n\tsm_config_set_sideset(&c, 1, false, false);\n\n\tpio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 1, true);\n\tpio_gpio_init(pio, clk_pin);\n\n\tgpio_disable_pulls(clk_pin);\n//\tgpio_set_drive_strength(clk_pin, GPIO_DRIVE_STRENGTH_12MA);\n//\tgpio_set_slew_rate(clk_pin, GPIO_SLEW_RATE_FAST);\n\n\t// Set the pin directions to input at the PIO\n\t// Set D0-D19 of the ADC(s) as input\n\tpio_sm_set_consecutive_pindirs(pio, sm, pin, 20, false);\n\n\t// Connect these GPIOs to this PIO block\n\tfor (int i = pin; i < (pin+20); i++) {\n\t\t//gpio_pull_down(i);\n\t\tpio_gpio_init(pio, i);\n\t}\n\n\tsm_config_set_in_shift(\n\t\t&c,\n\t\tfalse,\t// Shift-to-right = false (i.e. shift to left)\n\t\ttrue,\t// Autopush enabled\n\t\t16\t// Autopush threshold = 16\n\t);\n\n\t// required in order to set shift-to-right to true (for the out, null ops)\n\tsm_config_set_out_shift(\n\t\t&c,\n\t\ttrue,\t// Shift-to-right = true\n\t\tfalse,\t// Autopush disabled\n\t\t1\t// Autopush threshold: ignored\n\t);\n\n\t// We only receive, so disable the TX FIFO to make the RX FIFO deeper.\n\tsm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);\n\n\tsm_config_set_clkdiv(&c, 1.00f);\n\n\t// Load our configuration, and start the program from the beginning\n\tpio_sm_init(pio, sm, offset, &c);\n\tpio_sm_set_enabled(pio, sm, true);\n}\n%}\n"
  },
  {
    "path": "apps/sdr/sdr.c",
    "content": "/*\n * hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks\n * Implementation for the Raspberry Pi RP2350 HSTX peripheral\n *\n * External dual 10-bit ADC example, connected to the PIO\n * used for hsdaohSDR\n *\n * Copyright (c) 2024-2025 by Steve Markgraf <steve@steve-m.de>\n *\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the author nor the names of its contributors may\n *    be used to endorse or promote products derived from this software\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"pico/stdlib.h\"\n#include \"hardware/clocks.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/sync.h\"\n#include \"hardware/vreg.h\"\n#include \"hardware/pll.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/i2c.h\"\n\n#include <stdio.h>\n\n#include \"picohsdaoh.h\"\n#include \"adc_16bit_input.pio.h\"\n#include \"adc_20bit_input.pio.h\"\n\n#define I2C_TIMEOUT 50000\t/* 50 ms */\n\n/* The PIO is running with sys_clk/1, and needs 4 cycles per sample,\n * so the ADC clock is sys_clk/4 */\n#define SYS_CLK_8BIT\t\t328000\t// 82 MHz ADC clock\n#define SYS_CLK_10BIT\t\t264000\t// 66 MHz ADC clock\n#define HSTX_CLK_MHZ\t\t432\n\n// If we want to push everything to the limits:\n#define SYS_CLK_MAX_SRATE\t336000 // 84 MHz ADC clock @ 8 bit IQ\n#define HSTX_CLK_MAX_SRATE\t440\n\n// For alignment of 5x16 bit words in the payload, so that every line starts with word 0\n#define ADC_DATA_LEN_10_BIT\t(RBUF_SLICE_LEN - 5)\n#define ADC_DATA_LEN_8_BIT\t(RBUF_SLICE_LEN - 3)\n\n#define PIO_INPUT_PIN_BASE 27\n#define PIO_OUTPUT_CLK_PIN 26\n\n#define DMACH_PIO_PING 0\n#define DMACH_PIO_PONG 1\n\nbool enable_8bit_mode = false;\n\nstatic bool pio_dma_pong = false;\nuint16_t ringbuffer[RBUF_DEFAULT_TOTAL_LEN];\nint ringbuf_head = 2;\n\nvoid __scratch_y(\"\") pio_dma_irq_handler()\n{\n\tuint ch_num = pio_dma_pong ? DMACH_PIO_PONG : DMACH_PIO_PING;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\tpio_dma_pong = !pio_dma_pong;\n\n\tringbuf_head = (ringbuf_head + 1) % RBUF_DEFAULT_SLICES;\n\n\tch->write_addr = (uintptr_t)&ringbuffer[ringbuf_head * RBUF_SLICE_LEN];\n\tch->transfer_count = enable_8bit_mode ? ADC_DATA_LEN_8_BIT : ADC_DATA_LEN_10_BIT;\n\n\thsdaoh_update_head(0, ringbuf_head);\n}\n\nuint offset;\nuint offset2;\nuint sm_data;\nPIO pio = pio0;\n\nvoid init_pio(void)\n{\n\t/* move up GPIO base of PIO to access all ADC pins */\n\tpio_set_gpio_base(pio, 16);\n\n\toffset = pio_add_program(pio, &adc_16bit_input_program);\n\toffset2 = pio_add_program(pio, &adc_20bit_input_program);\n\n\tsm_data = pio_claim_unused_sm(pio, true);\n}\n\nvoid __no_inline_not_in_flash_func(init_pio_dma)(void)\n{\n\tringbuf_head = 2;\n\tpio_dma_pong = false;\n\n\tdma_channel_config c;\n\tc = dma_channel_get_default_config(DMACH_PIO_PING);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PONG);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PING,\n\t\t&c,\n\t\t&ringbuffer[0 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tenable_8bit_mode ? ADC_DATA_LEN_8_BIT : ADC_DATA_LEN_10_BIT,\n\t\tfalse\n\t);\n\tc = dma_channel_get_default_config(DMACH_PIO_PONG);\n\tchannel_config_set_chain_to(&c, DMACH_PIO_PING);\n\tchannel_config_set_dreq(&c, pio_get_dreq(pio, sm_data, false));\n\tchannel_config_set_read_increment(&c, false);\n\tchannel_config_set_write_increment(&c, true);\n\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_16);\n\n\tdma_channel_configure(\n\t\tDMACH_PIO_PONG,\n\t\t&c,\n\t\t&ringbuffer[1 * RBUF_SLICE_LEN],\n\t\t&pio->rxf[sm_data],\n\t\tenable_8bit_mode ? ADC_DATA_LEN_8_BIT : ADC_DATA_LEN_10_BIT,\n\t\tfalse\n\t);\n\n\tdma_hw->ints0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tdma_hw->inte0 |= (1u << DMACH_PIO_PING) | (1u << DMACH_PIO_PONG);\n\tirq_set_exclusive_handler(DMA_IRQ_0, pio_dma_irq_handler);\n\tirq_set_enabled(DMA_IRQ_0, true);\n\n\tdma_channel_start(DMACH_PIO_PING);\n}\n\nvoid __no_inline_not_in_flash_func(init_pio_input)(void)\n{\n//\tirq_set_enabled(DMA_IRQ_0, false);\n//\tdma_channel_start(DMACH_PIO_PING);\n//\tdma_channel_start(DMACH_PIO_PONG);\n//\tpio_sm_set_enabled(pio, sm_data, false);\n\n\tset_sys_clock_khz(enable_8bit_mode ? SYS_CLK_8BIT : SYS_CLK_10BIT, true);\n\n\tinit_pio_dma();\n\n\tif (enable_8bit_mode) {\n\t\thsdaoh_add_stream(0, PIO_8BIT_IQ, (SYS_CLK_8BIT/4) * 1000, ADC_DATA_LEN_8_BIT, RBUF_DEFAULT_SLICES, ringbuffer);\n\t\tadc_16bit_input_program_init(pio, sm_data, offset, PIO_INPUT_PIN_BASE, PIO_OUTPUT_CLK_PIN);\n\n\t} else {\n\t\thsdaoh_add_stream(0, PIO_10BIT_IQ, (SYS_CLK_10BIT/4) * 1000, ADC_DATA_LEN_10_BIT, RBUF_DEFAULT_SLICES, ringbuffer);\n\t\tadc_20bit_input_program_init(pio, sm_data, offset2, PIO_INPUT_PIN_BASE, PIO_OUTPUT_CLK_PIN);\n\t}\n}\n\nvoid __no_inline_not_in_flash_func(handle_tuner_interface)(void)\n{\n\t/* handle tty -> I2C interface for I2C tuner access */\n\tuint8_t i2c_addr, reg, val;\n\tint ret;\n\n\twhile (1) {\n\t\tchar c = getchar();\n\n\t\tswitch (c) {\n\t\tcase 'a':\n\t\t\thsdaoh_remove_stream(0);\n\t\t\tpio_sm_set_enabled(pio, sm_data, false);\n\t\t\tirq_set_enabled(DMA_IRQ_0, false);\n\t\t\tdma_channel_abort(DMACH_PIO_PING);\n\t\t\tdma_channel_abort(DMACH_PIO_PONG);\n\t\t\tpio_sm_clear_fifos(pio, sm_data);\n\t\t\tenable_8bit_mode = !enable_8bit_mode;\n\t\t\tinit_pio_input();\n\t\t\tbreak;\n\t\tcase 'r':\n\t\t\ti2c_addr = getchar();\n\t\t\treg = getchar();\n\n\t\t\t// special handling for RT7x0 read\n\t\t\tif (i2c_addr == 0x7a) {\n\t\t\t\tuint8_t regnull = 0;\n\t\t\t\tuint8_t bytes_to_read = reg + 1;\n\t\t\t\tuint8_t response[256];\n\t\t\t\tret = i2c_write_timeout_us(i2c_default, i2c_addr, &regnull, 1, false, I2C_TIMEOUT);\n\t\t\t\tif (ret == 1) {\n\t\t\t\t\tret = i2c_read_timeout_us(i2c_default, i2c_addr, response, bytes_to_read, false, I2C_TIMEOUT);\n\t\t\t\t\tif (ret == bytes_to_read)\n\t\t\t\t\t\tret = 1;\n\n\t\t\t\t\tval = response[reg];\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tret = i2c_write_timeout_us(i2c_default, i2c_addr, &reg, 1, false, I2C_TIMEOUT);\n\t\t\t\tif (ret == 1)\n\t\t\t\t\tret = i2c_read_timeout_us(i2c_default, i2c_addr, &val, 1, false, I2C_TIMEOUT);\n\t\t\t}\n\n\t\t\tputchar_raw((ret == 1) ? 1 : 0); // I2C ACK\n\t\t\tputchar_raw(val);\n\n\t\t\tbreak;\n\t\tcase 'w':\n\t\t\ti2c_addr = getchar();\n\t\t\tval = getchar();\n\t\t\treg = getchar();\n\t\t\tuint8_t wr[2] = { reg, val };\n\t\t\tret = i2c_write_timeout_us(i2c_default, i2c_addr, wr, sizeof(wr), false, I2C_TIMEOUT);\n\t\t\tputchar_raw((ret == sizeof(wr)) ? 1 : 0);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t}\n}\n\n#define OVERVOLT 1\n\nint main()\n{\n#ifdef OVERVOLT\n\t/* set maximum 'allowed' voltage without voiding warranty */\n\t//vreg_set_voltage(VREG_VOLTAGE_MAX);\n\tvreg_disable_voltage_limit();\n\tvreg_set_voltage(VREG_VOLTAGE_1_65);\n\tsleep_us(SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST_DELAY_US);\n#endif\n\thsdaoh_set_sys_clock_khz(enable_8bit_mode ? SYS_CLK_8BIT : SYS_CLK_10BIT);\n\tint usbdiv = HSTX_CLK_MHZ / 48;\n\n#ifdef USE_SYS_CLK_FOR_USB\n\t/* set USB clock to clk_sys/n */\n\tusbdiv = SYS_CLK/48000;\n\thw_write_masked(&clocks_hw->clk[clk_usb].ctrl,\n\t\t\tCLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS << CLOCKS_CLK_USB_CTRL_AUXSRC_LSB,\n\t\t\tCLOCKS_CLK_USB_CTRL_AUXSRC_BITS);\n#endif\n\n\thw_write_masked(&clocks_hw->clk[clk_usb].div,\n\t\t\tusbdiv << CLOCKS_CLK_USB_DIV_INT_LSB,\n\t\t\tCLOCKS_CLK_USB_DIV_INT_BITS);\n\n\t/* Initialize USB PLL for HSTX clock */\n\tpll_init(pll_usb, 1, HSTX_CLK_MHZ * 4 * MHZ, 2, 2);\n\n\t/* set HSTX divider to 1 */\n\thw_write_masked(\n\t\t&clocks_hw->clk[clk_hstx].div,\n\t\t1 << CLOCKS_CLK_HSTX_DIV_INT_LSB,\n\t\tCLOCKS_CLK_HSTX_DIV_INT_BITS\n\t);\n\n\t/* set HSTX clock source to PLL_USB */\n\thw_write_masked(&clocks_hw->clk[clk_hstx].ctrl,\n\t\t\tCLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB << CLOCKS_CLK_HSTX_CTRL_AUXSRC_LSB,\n\t\t\tCLOCKS_CLK_HSTX_CTRL_AUXSRC_BITS);\n\n\tstdio_init_all();\n\n\thsdaoh_init(GPIO_DRIVE_STRENGTH_12MA, GPIO_SLEW_RATE_FAST);\n\thsdaoh_start();\n\n\tinit_pio();\n\tinit_pio_input();\n\n\ti2c_init(i2c_default, 100 * 1000);\n\tgpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);\n\tgpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);\n\tgpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);\n\tgpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);\n\n\thandle_tuner_interface();\n}\n"
  },
  {
    "path": "libpicohsdaoh/CMakeLists.txt",
    "content": "# Note we are using INTERFACE so that the library can be configured per-app\n# with compile-time defines\n\nadd_library(libpicohsdaoh INTERFACE)\n\ntarget_sources(libpicohsdaoh INTERFACE\n\t${CMAKE_CURRENT_LIST_DIR}/picohsdaoh.c\n\t${CMAKE_CURRENT_LIST_DIR}/picohsdaoh.h\n\t${CMAKE_CURRENT_LIST_DIR}/data_packet.c\n\t${CMAKE_CURRENT_LIST_DIR}/data_packet.h\n\t)\n\ntarget_include_directories(libpicohsdaoh INTERFACE ${CMAKE_CURRENT_LIST_DIR})\ntarget_link_libraries(libpicohsdaoh INTERFACE\n\tpico_base_headers\n\tpico_util\n\tpico_multicore\n\thardware_dma\n\t)\n"
  },
  {
    "path": "libpicohsdaoh/data_packet.c",
    "content": "/*\n * Implementation of HDMI data packet and info frame encoding\n * (removed the audio frame encoding not needed by hsdaoh)\n *\n * Copyright (c) 2021-2022 by Shuichi Takano\n * https://github.com/shuichitakano/pico_lib/blob/master/dvi/data_packet.cpp\n *\n * ported to C by Marcelo Lorenzati:\n * https://github.com/mlorenzati/PicoDVI/blob/master/software/libdvi/data_packet.c\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"data_packet.h\"\n#include <string.h>\n\n// Compute 8 Parity Start\n// Parity table is build statically with the following code\n// for (int i = 0; i < 256; ++i){v_[i] = (i ^ (i >> 1) ^ (i >> 2) ^ (i >> 3) ^ (i >> 4) ^ (i >> 5) ^ (i >> 6) ^ (i >> 7)) & 1;}\nconst uint8_t parityTable[32] = { 0x96, 0x69, 0x69, 0x96, 0x69, 0x96, 0x96, 0x69,\n                                  0x69, 0x96, 0x96, 0x69, 0x96, 0x69, 0x69, 0x96,\n                                  0x69, 0x96, 0x96, 0x69, 0x96, 0x69, 0x69, 0x96,\n                                  0x96, 0x69, 0x69, 0x96, 0x69, 0x96, 0x96, 0x69 };\nbool compute8(uint8_t index) {\n    return (parityTable[index / 8] >> (index % 8)) & 0x01; \n}\nbool compute8_2(uint8_t index1, uint8_t index2) {\n    return compute8(index1) ^ compute8(index2);\n}\nbool compute8_3(uint8_t index1, uint8_t index2, uint8_t index3) {\n     return compute8(index1) ^ compute8(index2) ^ compute8(index3);\n}\n// Compute 8 Parity End\n\n// BCH Encoding Start\nconst uint8_t bchTable_[256] = {\n    0x00, 0xd9, 0xb5, 0x6c, 0x6d, 0xb4, 0xd8, 0x01, \n    0xda, 0x03, 0x6f, 0xb6, 0xb7, 0x6e, 0x02, 0xdb, \n    0xb3, 0x6a, 0x06, 0xdf, 0xde, 0x07, 0x6b, 0xb2, \n    0x69, 0xb0, 0xdc, 0x05, 0x04, 0xdd, 0xb1, 0x68, \n    0x61, 0xb8, 0xd4, 0x0d, 0x0c, 0xd5, 0xb9, 0x60, \n    0xbb, 0x62, 0x0e, 0xd7, 0xd6, 0x0f, 0x63, 0xba, \n    0xd2, 0x0b, 0x67, 0xbe, 0xbf, 0x66, 0x0a, 0xd3, \n    0x08, 0xd1, 0xbd, 0x64, 0x65, 0xbc, 0xd0, 0x09, \n    0xc2, 0x1b, 0x77, 0xae, 0xaf, 0x76, 0x1a, 0xc3, \n    0x18, 0xc1, 0xad, 0x74, 0x75, 0xac, 0xc0, 0x19, \n    0x71, 0xa8, 0xc4, 0x1d, 0x1c, 0xc5, 0xa9, 0x70, \n    0xab, 0x72, 0x1e, 0xc7, 0xc6, 0x1f, 0x73, 0xaa, \n    0xa3, 0x7a, 0x16, 0xcf, 0xce, 0x17, 0x7b, 0xa2, \n    0x79, 0xa0, 0xcc, 0x15, 0x14, 0xcd, 0xa1, 0x78, \n    0x10, 0xc9, 0xa5, 0x7c, 0x7d, 0xa4, 0xc8, 0x11, \n    0xca, 0x13, 0x7f, 0xa6, 0xa7, 0x7e, 0x12, 0xcb, \n    0x83, 0x5a, 0x36, 0xef, 0xee, 0x37, 0x5b, 0x82, \n    0x59, 0x80, 0xec, 0x35, 0x34, 0xed, 0x81, 0x58, \n    0x30, 0xe9, 0x85, 0x5c, 0x5d, 0x84, 0xe8, 0x31, \n    0xea, 0x33, 0x5f, 0x86, 0x87, 0x5e, 0x32, 0xeb, \n    0xe2, 0x3b, 0x57, 0x8e, 0x8f, 0x56, 0x3a, 0xe3, \n    0x38, 0xe1, 0x8d, 0x54, 0x55, 0x8c, 0xe0, 0x39, \n    0x51, 0x88, 0xe4, 0x3d, 0x3c, 0xe5, 0x89, 0x50, \n    0x8b, 0x52, 0x3e, 0xe7, 0xe6, 0x3f, 0x53, 0x8a, \n    0x41, 0x98, 0xf4, 0x2d, 0x2c, 0xf5, 0x99, 0x40, \n    0x9b, 0x42, 0x2e, 0xf7, 0xf6, 0x2f, 0x43, 0x9a, \n    0xf2, 0x2b, 0x47, 0x9e, 0x9f, 0x46, 0x2a, 0xf3, \n    0x28, 0xf1, 0x9d, 0x44, 0x45, 0x9c, 0xf0, 0x29, \n    0x20, 0xf9, 0x95, 0x4c, 0x4d, 0x94, 0xf8, 0x21, \n    0xfa, 0x23, 0x4f, 0x96, 0x97, 0x4e, 0x22, 0xfb, \n    0x93, 0x4a, 0x26, 0xff, 0xfe, 0x27, 0x4b, 0x92, \n    0x49, 0x90, 0xfc, 0x25, 0x24, 0xfd, 0x91, 0x48, \n};\n\nint encode_BCH_3(const uint8_t *p) {\n    uint8_t v = bchTable_[p[0]];\n    v = bchTable_[p[1] ^ v];\n    v = bchTable_[p[2] ^ v];\n    return v;\n}\n\nint encode_BCH_7(const uint8_t *p) {\n    uint8_t v = bchTable_[p[0]];\n    v = bchTable_[p[1] ^ v];\n    v = bchTable_[p[2] ^ v];\n    v = bchTable_[p[3] ^ v];\n    v = bchTable_[p[4] ^ v];\n    v = bchTable_[p[5] ^ v];\n    v = bchTable_[p[6] ^ v];\n    return v;\n}\n// BCH Encoding End\n\n// TERC4 Start\nuint16_t TERC4Syms_[16] = {\n    0b1010011100,\n    0b1001100011,\n    0b1011100100,\n    0b1011100010,\n    0b0101110001,\n    0b0100011110,\n    0b0110001110,\n    0b0100111100,\n    0b1011001100,\n    0b0100111001,\n    0b0110011100,\n    0b1011000110,\n    0b1010001110,\n    0b1001110001,\n    0b0101100011,\n    0b1011000011,\n};\n\nuint32_t makeTERC4x2Char(int i) { return TERC4Syms_[i] | (TERC4Syms_[i] << 10); }\nuint32_t makeTERC4x2Char_2(int i0, int i1) { return TERC4Syms_[i0] | (TERC4Syms_[i1] << 10); }\n#define TERC4_0x2CharSym_ 0x000A729C // Build time generated -> makeTERC4x2Char(0);\n#define dataGaurdbandSym_ 0x0004CD33 // Build time generated -> 0b0100110011'0100110011;\nuint32_t defaultDataPacket12_[N_DATA_ISLAND_WORDS] = {\n    dataGaurdbandSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    TERC4_0x2CharSym_,\n    dataGaurdbandSym_,\n};\n\n// This table is built in compilation time from a function that uses makeTERC4x2Char \nuint32_t defaultDataPackets0_[4][N_DATA_ISLAND_WORDS] = {\n    { 0xa3a8e, 0xa729c, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xb32cc, 0xa3a8e},\n    { 0x9c671, 0x98e63, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x4e539, 0x9c671}, \n    { 0x58d63, 0xb92e4, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x6719c, 0x58d63}, \n    { 0xb0ec3, 0xb8ae2, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb1ac6, 0xb0ec3}\n};\n\nuint32_t *getDefaultDataPacket0(bool vsync, bool hsync) {\n    return defaultDataPackets0_[(vsync << 1) | hsync];\n}\n\n// TERC4 End\n\nvoid compute_header_parity(data_packet_t *data_packet) {\n    data_packet->header[3] = encode_BCH_3(data_packet->header);\n}\n\nvoid compute_subpacket_parity(data_packet_t *data_packet, int i) {\n    data_packet->subpacket[i][7] = encode_BCH_7(data_packet->subpacket[i]);\n}\n\nvoid compute_parity(data_packet_t *data_packet) {\n    compute_header_parity(data_packet);\n    compute_subpacket_parity(data_packet, 0);\n    compute_subpacket_parity(data_packet, 1);\n    compute_subpacket_parity(data_packet, 2);\n    compute_subpacket_parity(data_packet, 3);\n}\n\nvoid compute_info_frame_checkSum(data_packet_t *data_packet) {\n    int s = 0;\n    for (int i = 0; i < 3; ++i)\n    {\n        s += data_packet->header[i];\n    }\n    int n = data_packet->header[2] + 1;\n    for (int j = 0; j < 4; ++j)\n    {\n        for (int i = 0; i < 7 && n; ++i, --n)\n        {\n            s += data_packet->subpacket[j][i];\n        }\n    }\n    data_packet->subpacket[0][0] = -s;\n}\n\nvoid encode_header(const data_packet_t *data_packet, uint32_t *dst, int hv, bool firstPacket) {\n    int hv1 = hv | 8;\n    if (!firstPacket) { \n        hv = hv1;\n    }\n    for (int i = 0; i < 4; ++i) {\n        uint8_t h = data_packet->header[i];\n        dst[0] = makeTERC4x2Char_2(((h << 2) & 4) | hv,  ((h << 1) & 4) | hv1);\n        dst[1] = makeTERC4x2Char_2((h & 4) | hv1,        ((h >> 1) & 4) | hv1);\n        dst[2] = makeTERC4x2Char_2(((h >> 2) & 4) | hv1, ((h >> 3) & 4) | hv1);\n        dst[3] = makeTERC4x2Char_2(((h >> 4) & 4) | hv1, ((h >> 5) & 4) | hv1);\n        dst += 4;\n        hv = hv1;\n    }\n}\n\nvoid encode_subpacket(const data_packet_t *data_packet, uint32_t *dst1, uint32_t *dst2) {\n    for (int i = 0; i < 8; ++i) {\n        uint32_t v = (data_packet->subpacket[0][i] << 0)  | (data_packet->subpacket[1][i] << 8) |\n                     (data_packet->subpacket[2][i] << 16) | (data_packet->subpacket[3][i] << 24);\n        uint32_t t = (v ^ (v >> 7)) & 0x00aa00aa;\n        v = v ^ t ^ (t << 7);\n        t = (v ^ (v >> 14)) & 0x0000cccc;\n        v = v ^ t ^ (t << 14);\n        // 01234567 89abcdef ghijklmn opqrstuv\n        // 08go4cks 19hp5dlt 2aiq6emu 3bjr7fnv\n        dst1[0] = makeTERC4x2Char_2((v >> 0) & 15,  (v >> 16) & 15);\n        dst1[1] = makeTERC4x2Char_2((v >> 4) & 15,  (v >> 20) & 15);\n        dst2[0] = makeTERC4x2Char_2((v >> 8) & 15,  (v >> 24) & 15);\n        dst2[1] = makeTERC4x2Char_2((v >> 12) & 15, (v >> 28) & 15);\n        dst1 += 2;\n        dst2 += 2;\n    }\n}\n\nvoid set_null(data_packet_t *data_packet) {\n    memset(data_packet, 0, sizeof(data_packet_t));\n}\n\nvoid set_AVI_info_frame(data_packet_t *data_packet, scan_info s, pixel_format y, colorimetry c, picture_aspect_ratio m,\n    active_format_aspect_ratio r, RGB_quantization_range q, video_code vic) {\n    set_null(data_packet);\n    data_packet->header[0] = 0x82;\n    data_packet->header[1] = 2;  // version\n    data_packet->header[2] = 13; // len\n\n    int sc = 0;\n    // int sc = 3; // scaled hv\n\n    data_packet->subpacket[0][1] = (int)(s) | (r == ACTIVE_FORMAT_ASPECT_RATIO_NO_DATA ? 0 : 16) | ((int)(y) << 5);\n    data_packet->subpacket[0][2] = (int)(r) | ((int)(m) << 4) | ((int)(c) << 6);\n    data_packet->subpacket[0][3] = sc | ((int)(q) << 2);\n    data_packet->subpacket[0][4] = (int)(vic);\n\n    compute_info_frame_checkSum(data_packet);\n    compute_parity(data_packet);\n}\n\nvoid encode_data_island(data_island_stream_t *dst, const data_packet_t *packet, bool vsync, bool hsync) {\n    int hv = (vsync ? 2 : 0) | (hsync ? 1 : 0);\n    dst->data[0][0] = makeTERC4x2Char(0b1100 | hv);\n    dst->data[1][0] = dataGaurdbandSym_;\n    dst->data[2][0] = dataGaurdbandSym_;\n\n    encode_header(packet, &dst->data[0][1], hv, true);\n    encode_subpacket(packet, &dst->data[1][1], &dst->data[2][1]);\n\n    dst->data[0][N_DATA_ISLAND_WORDS - 1] = makeTERC4x2Char(0b1100 | hv);\n    dst->data[1][N_DATA_ISLAND_WORDS - 1] = dataGaurdbandSym_;\n    dst->data[2][N_DATA_ISLAND_WORDS - 1] = dataGaurdbandSym_;\n}\n"
  },
  {
    "path": "libpicohsdaoh/data_packet.h",
    "content": "#ifndef DATA_PACKET_H\n#define DATA_PACKET_H\n#include \"pico.h\"\n\n#define TMDS_CHANNELS        3\n#define N_LINE_PER_DATA      2\n#define W_GUARDBAND          2\n#define W_PREAMBLE           8\n#define W_DATA_PACKET        32\n\n#ifndef DVI_SYMBOLS_PER_WORD\n#define DVI_SYMBOLS_PER_WORD 2\n#endif\n\n#if DVI_SYMBOLS_PER_WORD != 1 && DVI_SYMBOLS_PER_WORD !=2\n#error \"Unsupported value for DVI_SYMBOLS_PER_WORD\"\n#endif\n\n\n#define W_DATA_ISLAND        (W_GUARDBAND * 2 + W_DATA_PACKET)\n#define N_DATA_ISLAND_WORDS  (W_DATA_ISLAND / DVI_SYMBOLS_PER_WORD)\n\ntypedef enum {\n    SCAN_INFO_NO_DATA,\n    OVERSCAN,\n    UNDERSCAN\n} scan_info;\n\ntypedef enum {\n    RGB,\n    YCBCR422,\n    YCBCR444\n} pixel_format;\n\ntypedef enum {\n    COLORIMETRY_NO_DATA,\n    ITU601,\n    ITU709,\n    EXTENDED\n} colorimetry;\n\ntypedef enum {\n    PIC_ASPECT_RATIO_NO_DATA,\n    PIC_ASPECT_RATIO_4_3,\n    PIC_ASPECT_RATIO_16_9\n} picture_aspect_ratio;\n\ntypedef enum {\n    ACTIVE_FORMAT_ASPECT_RATIO_NO_DATA = -1,\n    SAME_AS_PAR = 8,\n    ACTIVE_FORMAT_ASPECT_RATIO_4_3,\n    ACTIVE_FORMAT_ASPECT_RATIO_16_9,\n    ACTIVE_FORMAT_ASPECT_RATIO_14_9\n} active_format_aspect_ratio;\n\ntypedef enum {\n    DEFAULT,\n    LIMITED,\n    FULL\n} RGB_quantization_range;\n\ntypedef enum {\n    _640x480P60 = 1,\n    _720x480P60 = 2,\n    _1280x720P60 = 4,\n    _1920x1080I60 = 5,\n    _1920x1080P60 = 16,\n} video_code;\n\ntypedef struct data_packet {\n    uint8_t header[4];\n    uint8_t subpacket[4][8];\n} data_packet_t;\n\ntypedef struct data_island_stream {\n    uint32_t data[TMDS_CHANNELS][N_DATA_ISLAND_WORDS];\n} data_island_stream_t;\n\n// Functions related to the data_packet (requires a data_packet instance)\nvoid compute_header_parity(data_packet_t *data_packet);\nvoid compute_subpacket_parity(data_packet_t *data_packet, int i);\nvoid compute_parity(data_packet_t *data_packet);\nvoid compute_info_frame_checkSum(data_packet_t *data_packet);\nvoid encode_header(const data_packet_t *data_packet, uint32_t *dst, int hv, bool firstPacket);\nvoid encode_subpacket(const data_packet_t *data_packet, uint32_t *dst1, uint32_t *dst2);\nvoid set_null(data_packet_t *data_packet);\nvoid set_AVI_info_frame(data_packet_t *data_packet, scan_info s, pixel_format y, colorimetry c, picture_aspect_ratio m,\n    active_format_aspect_ratio r, RGB_quantization_range q, video_code vic);\n\n// Public Functions\nextern uint32_t defaultDataPacket12_[N_DATA_ISLAND_WORDS];\ninline uint32_t *getDefaultDataPacket12() {\n    return defaultDataPacket12_;\n}\nuint32_t *getDefaultDataPacket0(bool vsync, bool hsync);\nvoid encode_data_island(data_island_stream_t *dst, const data_packet_t *packet, bool vsync, bool hsync);\n#endif\n"
  },
  {
    "path": "libpicohsdaoh/picohsdaoh.c",
    "content": "/*\n * hsdaoh - High Speed Data Acquisition over MS213x USB3 HDMI capture sticks\n * Implementation for the Raspberry Pi RP2350 HSTX peripheral\n *\n * Copyright (c) 2024-2025 by Steve Markgraf <steve@steve-m.de>\n *\n * based on the pico-examples/hstx/dvi_out_hstx_encoder example:\n * Copyright (c) 2024 Raspberry Pi (Trading) Ltd.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the author nor the names of its contributors may\n *    be used to endorse or promote products derived from this software\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"hardware/dma.h\"\n#include \"hardware/gpio.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/clocks.h\"\n#include \"hardware/structs/bus_ctrl.h\"\n#include \"hardware/structs/hstx_ctrl.h\"\n#include \"hardware/structs/hstx_fifo.h\"\n#include \"hardware/structs/qmi.h\"\n#include \"pico/multicore.h\"\n#include \"pico/stdlib.h\"\n#include \"data_packet.h\"\n#include \"picohsdaoh.h\"\n\n// Section 5.4.2\n#define TMDS_CTRL_00 0x354u\n#define TMDS_CTRL_01 0x0abu\n#define TMDS_CTRL_10 0x154u\n#define TMDS_CTRL_11 0x2abu\n\n#define SYNC_V0_H0 (TMDS_CTRL_00 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))\n#define SYNC_V0_H1 (TMDS_CTRL_01 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))\n#define SYNC_V1_H0 (TMDS_CTRL_10 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))\n#define SYNC_V1_H1 (TMDS_CTRL_11 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))\n#define SYNC_V1_H1_WITH_PREAMBLE (TMDS_CTRL_11 | (TMDS_CTRL_01 << 10) | (TMDS_CTRL_00 << 20))\n#define SYNC_V0_H0_WITH_DATA_ISLAND_PREAMBLE (TMDS_CTRL_00 | (TMDS_CTRL_01 << 10) | (TMDS_CTRL_01 << 20))\n#define VIDEO_LEADING_GUARD_BAND (0x2ccu | (0x133u << 10) | (0x2ccu << 20))\n\n#define HSTX_CMD_RAW\t\t(0x0u << 12)\n#define HSTX_CMD_RAW_REPEAT\t(0x1u << 12)\n#define HSTX_CMD_TMDS\t\t(0x2u << 12)\n#define HSTX_CMD_TMDS_REPEAT\t(0x3u << 12)\n#define HSTX_CMD_NOP\t\t(0xfu << 12)\n\nuint16_t idle_line_buf1[MODE_H_ACTIVE_PIXELS];\nuint16_t idle_line_buf2[MODE_H_ACTIVE_PIXELS];\nuint32_t info_p[64];\nuint32_t info_len;\n\ntypedef struct\n{\n\tbool active;\n\tuint16_t *rbuf;\n\tuint tail;\n\tuint head;\n\tuint rbuf_slices;\n\tuint16_t format;\n\tuint32_t srate;\n\tuint16_t len;\n\tuint64_t data_cnt;\n\tbool overflow;\n} stream_t;\n\nstream_t streams[MAX_STREAMS];\n\nstatic uint32_t vblank_line_vsync_off[] = {\n\tHSTX_CMD_RAW_REPEAT | MODE_H_FRONT_PORCH,\n\tSYNC_V1_H1,\n\tHSTX_CMD_RAW_REPEAT | MODE_H_SYNC_WIDTH,\n\tSYNC_V1_H0,\n\tHSTX_CMD_RAW_REPEAT | (MODE_H_BACK_PORCH + MODE_H_ACTIVE_PIXELS),\n\tSYNC_V1_H1,\n\tHSTX_CMD_NOP\n};\n\nstatic uint32_t vblank_line_vsync_on[] = {\n\tHSTX_CMD_RAW_REPEAT | MODE_H_FRONT_PORCH,\n\tSYNC_V0_H1,\n\tHSTX_CMD_RAW_REPEAT | MODE_H_SYNC_WIDTH,\n\tSYNC_V0_H0,\n\tHSTX_CMD_RAW_REPEAT | (MODE_H_BACK_PORCH + MODE_H_ACTIVE_PIXELS),\n\tSYNC_V0_H1,\n\tHSTX_CMD_NOP\n};\n\nstatic uint32_t vactive_line[] = {\n\tHSTX_CMD_RAW_REPEAT | (MODE_H_FRONT_PORCH),\n\tSYNC_V1_H1,\n\tHSTX_CMD_RAW_REPEAT | (MODE_H_SYNC_WIDTH),\n\tSYNC_V1_H0,\n\tHSTX_CMD_RAW_REPEAT | (MODE_H_BACK_PORCH-W_PREAMBLE-W_GUARDBAND),\n\tSYNC_V1_H1,\n\tHSTX_CMD_RAW_REPEAT | W_PREAMBLE,\n\tSYNC_V1_H1_WITH_PREAMBLE,\n\tHSTX_CMD_RAW_REPEAT | W_GUARDBAND,\n\tVIDEO_LEADING_GUARD_BAND,\n\tHSTX_CMD_TMDS | MODE_H_ACTIVE_PIXELS\n};\n\n/* Pre-compute the HDMI info packet that is required to switch the MS2130 to YCbCr422 mode */\nvoid init_info_packet(void)\n{\n\tint len = 0;\n\n\tdata_packet_t avi_info_frame;\n\tdata_island_stream_t di_str;\n\n\tset_AVI_info_frame(&avi_info_frame, SCAN_INFO_NO_DATA, YCBCR422,\n\t\t\t   ITU601, PIC_ASPECT_RATIO_16_9, SAME_AS_PAR, FULL, _1920x1080P60);\n\tencode_data_island(&di_str, &avi_info_frame, 0, 0);\n\n\tinfo_p[len++] = HSTX_CMD_RAW_REPEAT | MODE_H_FRONT_PORCH;\n\tinfo_p[len++] = SYNC_V0_H1;\n\tinfo_p[len++] = HSTX_CMD_RAW_REPEAT | (MODE_H_SYNC_WIDTH - W_DATA_ISLAND - W_PREAMBLE);\n\tinfo_p[len++] = SYNC_V0_H0;\n\tinfo_p[len++] = HSTX_CMD_RAW_REPEAT | W_PREAMBLE;\n\tinfo_p[len++] = SYNC_V0_H0_WITH_DATA_ISLAND_PREAMBLE;\n\tinfo_p[len++] = HSTX_CMD_RAW | W_DATA_ISLAND;\n\n\t/* convert from the two symbols per word for each channel to one\n\t * symbol per word containing all three channels format */\n\tfor (int i = 0; i < N_DATA_ISLAND_WORDS; i++) {\n\t\tinfo_p[len++] = (di_str.data[0][i] & 0x3ff) |\n\t\t\t       ((di_str.data[1][i] & 0x3ff) << 10) |\n\t\t\t       ((di_str.data[2][i] & 0x3ff) << 20);\n\n\t\tinfo_p[len++] = (di_str.data[0][i] >> 10) |\n\t\t\t       ((di_str.data[1][i] >> 10) << 10) |\n\t\t\t       ((di_str.data[2][i] >> 10) << 20);\n\t}\n\n\tinfo_p[len++] = HSTX_CMD_RAW_REPEAT | (MODE_H_BACK_PORCH + MODE_H_ACTIVE_PIXELS);\n\tinfo_p[len++] = SYNC_V0_H1;\n\n\tinfo_len = len;\n}\n\nvoid __scratch_y(\"\") hsdaoh_update_head(int stream_id, int head)\n{\n\tif (streams[stream_id].tail == head)\n\t\tstreams[stream_id].overflow = true;\n\telse\n\t\tstreams[stream_id].head = head;\n}\n\n#define DMACH_HSTX_START\t13\n#define DMACH_HSTX_COUNT\t3\n#define CRC16_INIT\t\t0xffff\n\nstatic uint8_t hstx_dma_curchan = 0;\nstatic uint16_t saved_crc;\nstatic uint v_scanline = 3;\nstatic bool vactive_cmdlist_posted = false;\nstatic uint8_t dma_sniff_pipelined_ch = 0;\nstatic bool dma_sniff_pipelined_disable = false;\n\nmetadata_t metadata = (metadata_t) { .magic = 0xda7acab1, .crc_config = CRC16_2_LINE, .version = 1,\n\t\t\t\t     .flags = FLAG_STREAM_ID_PRESENT | FLAG_FORMAT_ID_PRESENT,\n\t\t\t\t     .max_streamid = MAX_STREAMS\n};\n\n/* HSTX DMA IRQ handler, reconfigures the channel that just completed while\n * ther other channel is currently busy */\nvoid __scratch_x(\"\") hstx_dma_irq_handler()\n{\n\t/* This is a bit tricky and time critical, we pipeline three DMA transfers to avoid an\n\t * underrun, but the DMA sniffer that is used to calculate the CRC cannot be pipelined\n\t * and needs to be reconfigured right before the DMA transfer starts - so we have to\n\t * do that as fast as possible during blanking, before the next DMA transfer with\n\t * active video data, which is right about to start. */\n\tif (dma_sniff_pipelined_ch) {\n\t\t/* (re)initialize DMA CRC sniffer */\n\t\tsaved_crc = dma_sniffer_get_data_accumulator() & 0xffff;\n\t\tdma_sniffer_set_data_accumulator(CRC16_INIT);\n\t\tdma_sniffer_enable(dma_sniff_pipelined_ch, DMA_SNIFF_CTRL_CALC_VALUE_CRC16, true);\n\t\tdma_sniff_pipelined_ch = 0;\n\t} else if (dma_sniff_pipelined_disable) {\n\t\tdma_sniffer_disable();\n\t\tdma_sniff_pipelined_disable = false;\n\t}\n\n\tuint ch_num = hstx_dma_curchan + DMACH_HSTX_START;\n\thstx_dma_curchan = (hstx_dma_curchan + 1) % DMACH_HSTX_COUNT;\n\tdma_channel_hw_t *ch = &dma_hw->ch[ch_num];\n\tdma_hw->intr = 1u << ch_num;\n\n\tif (v_scanline >= MODE_V_FRONT_PORCH && v_scanline < (MODE_V_FRONT_PORCH + MODE_V_SYNC_WIDTH)) {\n\t\t/* on first line of actual VSYNC, output data packet */\n\t\tif (v_scanline == MODE_V_FRONT_PORCH) {\n\t\t\tdma_sniff_pipelined_disable = true;\n\t\t\tch->read_addr = (uintptr_t)info_p;\n\t\t\tch->transfer_count = info_len;\n\n\t\t\t/* increment framecounter*/\n\t\t\tmetadata.framecounter++;\n\n\t\t\t/* latch data counters into metadata at beginning of frame */\n\t\t\tfor (uint i = 0; i < MAX_STREAMS; i++)\n\t\t\t\tmetadata.stream_info[i].data_cnt = streams[i].data_cnt;\n\t\t} else {\n\t\t\tch->read_addr = (uintptr_t)vblank_line_vsync_on;\n\t\t\tch->transfer_count = count_of(vblank_line_vsync_on);\n\t\t}\n\n\t} else if (v_scanline < MODE_V_FRONT_PORCH + MODE_V_SYNC_WIDTH + MODE_V_BACK_PORCH) {\n\t\tch->read_addr = (uintptr_t)vblank_line_vsync_off;\n\t\tch->transfer_count = count_of(vblank_line_vsync_off);\n\t} else if (!vactive_cmdlist_posted) {\n\t\tch->read_addr = (uintptr_t)vactive_line;\n\t\tch->transfer_count = count_of(vactive_line);\n\t\tvactive_cmdlist_posted = true;\n\t} else {\n\t\t/* Output of actual data in active video lines */\n\t\tuint16_t cur_active_line = v_scanline - (MODE_V_TOTAL_LINES - MODE_V_ACTIVE_LINES);\n\t\tuint16_t *next_line = (cur_active_line % 2) ? idle_line_buf1 : idle_line_buf2;\n\t\tnext_line[RBUF_SLICE_LEN - 1] = 0;\n\n\t\tfor (uint i = 0; i < MAX_STREAMS; i++) {\n\t\t\tstream_t *stream = &streams[i];\n\n\t\t\tif (!stream->active)\n\t\t\t\tcontinue;\n\n\t\t\tint next_tail = (stream->tail + 1) % stream->rbuf_slices;\n\t\t\tif (stream->head != next_tail) {\n\t\t\t\tnext_line = &stream->rbuf[stream->tail * RBUF_SLICE_LEN];\n\t\t\t\tstream->tail = next_tail;\n\t\t\t\tstream->data_cnt += stream->len;\n\t\t\t\tif (stream->overflow) {\n\t\t\t\t\t/* signal that there has been an overflow */\n\t\t\t\t\tnext_line[RBUF_SLICE_LEN - 1] = 0x0fff;\n\t\t\t\t\tstream->overflow = false;\n\t\t\t\t} else\n\t\t\t\t\tnext_line[RBUF_SLICE_LEN - 1] = stream->len;\n\n\t\t\t\tnext_line[RBUF_SLICE_LEN - 3] = (stream->format << 6) | (i & 0x3f); // stream and format ID\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/* fill in metadata word (last word of line) */\n\t\tif (cur_active_line < (sizeof(metadata_t) * 2)) {\n\t\t\tuint8_t *met_p = (uint8_t *)&metadata;\n\t\t\tif (cur_active_line % 2)\n\t\t\t\tnext_line[RBUF_SLICE_LEN - 1] |= ((met_p[cur_active_line/2] & 0xf0) << 8);\n\t\t\telse\n\t\t\t\tnext_line[RBUF_SLICE_LEN - 1] |= ((met_p[cur_active_line/2] & 0x0f) << 12);\n\t\t}\n\n\t\t/* on the second last word of the line, insert the CRC16 of the entire line before the last line */\n\t\tnext_line[RBUF_SLICE_LEN - 2] = saved_crc;\n\t\tdma_sniff_pipelined_ch = ch_num;\n\t\tch->read_addr = (uintptr_t)next_line;\n\t\tch->transfer_count = MODE_H_ACTIVE_PIXELS/2;\n\t\tvactive_cmdlist_posted = false;\n\t}\n\n\tif (!vactive_cmdlist_posted)\n\t\tv_scanline = (v_scanline + 1) % MODE_V_TOTAL_LINES;\n}\n\nvoid core1_entry()\n{\n\tirq_set_exclusive_handler(DMA_IRQ_3, hstx_dma_irq_handler);\n\tirq_set_enabled(DMA_IRQ_3, true);\n\n\twhile (1)\n\t\t__wfi();\n}\n\nvoid hsdaoh_start(void)\n{\n\tmulticore_launch_core1(core1_entry);\n\tdma_channel_start(DMACH_HSTX_START);\n}\n\nint hsdaoh_add_stream(uint16_t stream_id, uint16_t format, uint32_t samplerate, uint length, uint slices, uint16_t *ringbuf)\n{\n\tif (stream_id >= MAX_STREAMS)\n\t\treturn -1;\n\n\tstream_t *stream = &streams[stream_id];\n\tstream->active = false;\n\tstream->rbuf = ringbuf;\n\tstream->format = format;\n\tstream->srate = samplerate;\n\tstream->len = length;\n\tstream->rbuf_slices = slices;\n\tstream->tail = slices-1;\n\tstream->head = 0;\n\tstream->data_cnt = 0;\n\tstream->active = true;\n\n\tmetadata.stream_info[stream_id].srate = samplerate;\n\n\tif (stream_id == 0)\n\t\tmetadata.stream0_format = format;\n\n\treturn 0;\n}\n\nint hsdaoh_remove_stream(uint16_t stream_id)\n{\n\tif (stream_id >= MAX_STREAMS)\n\t\treturn -1;\n\n\tstreams[stream_id].active = false;\n\n\treturn 0;\n}\n\n/* set system clock, without overclocking the QSPI flash */\nvoid hsdaoh_set_sys_clock_khz(uint32_t freq_khz)\n{\n\tuint clkdiv = freq_khz/40000;\n\tif (freq_khz % 40000)\n\t\tclkdiv++;\n\n\tif (clkdiv < 4)\n\t\tclkdiv = 4;\n\telse if (clkdiv > 8)\n\t\tclkdiv = 8;\n\n\t/* set QSPI clock divider */\n\thw_write_masked(&qmi_hw->m[0].timing,\n\t\tclkdiv << QMI_M0_TIMING_CLKDIV_LSB,\n\t\tQMI_M0_TIMING_CLKDIV_BITS\n\t);\n\n\t__asm__ __volatile__(\"dmb sy\");\n\tset_sys_clock_khz(freq_khz, true);\n}\n\nvoid hsdaoh_init(int dstrength, int slewrate)\n{\n\tfor (uint i = 0; i < MAX_STREAMS; i++)\n\t\tstreams[i].active = false;\n\n\tinit_info_packet();\n\n\t/* Configure HSTX's TMDS encoder for YCbCr422 stream: L0 is unused in this\n\t * mode on the MS2130 and carries the same data as L1. This way we can\n\t * conveniently use 32-bit DMA transfers to transparently transfer data to\n\t * libhsdaoh on the host */\n\thstx_ctrl_hw->expand_tmds =\n\t\t\t7 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |\n\t\t\t8 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB   |\n\t\t\t7 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |\n\t\t\t0 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB   |\n\t\t\t7 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |\n\t\t\t0 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB;\n\n\t/* Both to-be TMDS encoded pixels and raw control words arrive as 32-bit\n\t * words. While for the raw control words this means they carry the 3x 10\n\t * bit data for the three lanes (1 word per symbol), the actual data\n\t * contains two times 16-bit data per 32-bit word */\n\thstx_ctrl_hw->expand_shift =\n\t\t2  << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |\n\t\t16 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB |\n\t\t1  << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB |\n\t\t0  << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB;\n\n\t/* Serial output config: clock period of 5 cycles, pop from command\n\t * expander every 5 cycles, shift the output shiftreg by 2 every cycle. */\n\thstx_ctrl_hw->csr = 0;\n\thstx_ctrl_hw->csr =\n\t\tHSTX_CTRL_CSR_EXPAND_EN_BITS |\n\t\t5u << HSTX_CTRL_CSR_CLKDIV_LSB |\n\t\t5u << HSTX_CTRL_CSR_N_SHIFTS_LSB |\n\t\t2u << HSTX_CTRL_CSR_SHIFT_LSB |\n\t\tHSTX_CTRL_CSR_EN_BITS;\n\n\n\t// HSTX outputs 0 through 7 appear on GPIO 12 through 19.\n\t// Pinout on Pico DVI sock:\n\t//\n\t//   GP12 D0+  GP13 D0-\n\t//   GP14 CK+  GP15 CK-\n\t//   GP16 D2+  GP17 D2-\n\t//   GP18 D1+  GP19 D1-\n\n\t// Assign clock pair to two neighbouring pins:\n\thstx_ctrl_hw->bit[2] = HSTX_CTRL_BIT0_CLK_BITS;\n\thstx_ctrl_hw->bit[3] = HSTX_CTRL_BIT0_CLK_BITS | HSTX_CTRL_BIT0_INV_BITS;\n\tfor (uint lane = 0; lane < 3; ++lane) {\n\t\t// For each TMDS lane, assign it to the correct GPIO pair based on the\n\t\t// desired pinout:\n\t\tstatic const int lane_to_output_bit[3] = {0, 6, 4};\n\t\tint bit = lane_to_output_bit[lane];\n\t\t// Output even bits during first half of each HSTX cycle, and odd bits\n\t\t// during second half. The shifter advances by two bits each cycle.\n\t\tuint32_t lane_data_sel_bits =\n\t\t\t(lane * 10\t) << HSTX_CTRL_BIT0_SEL_P_LSB |\n\t\t\t(lane * 10 + 1) << HSTX_CTRL_BIT0_SEL_N_LSB;\n\t\t// The two halves of each pair get identical data, but one pin is inverted.\n\t\thstx_ctrl_hw->bit[bit\t] = lane_data_sel_bits;\n\t\thstx_ctrl_hw->bit[bit + 1] = lane_data_sel_bits | HSTX_CTRL_BIT0_INV_BITS;\n\t}\n\n\tfor (int i = 12; i <= 19; ++i) {\n\t\tgpio_set_input_enabled(i, false);\n\t\tgpio_disable_pulls(i);\n\t\tgpio_set_drive_strength(i, dstrength);\n\t\tgpio_set_slew_rate(i, slewrate);\n\t\tgpio_set_function(i, 0); // HSTX\n\t}\n\n\t/* All channels are set up identically, to transfer a whole scanline and\n\t * then chain to the next channel. Each time a channel finishes, we\n\t * reconfigure the one that just finished, meanwhile another channel\n\t * is already making progress. */\n\tfor (int i = 0; i < DMACH_HSTX_COUNT; i++) {\n\t\tdma_channel_config c;\n\t\tc = dma_channel_get_default_config(DMACH_HSTX_START + i);\n\t\tint chain_to_ch = DMACH_HSTX_START + ((i + 1) % DMACH_HSTX_COUNT);\n\t\tchannel_config_set_chain_to(&c, chain_to_ch);\n\t\tchannel_config_set_dreq(&c, DREQ_HSTX);\n\t\tchannel_config_set_sniff_enable(&c, true);\n\t\tchannel_config_set_transfer_data_size(&c, DMA_SIZE_32);\n\t\tdma_channel_configure(\n\t\t\tDMACH_HSTX_START + i,\n\t\t\t&c,\n\t\t\t&hstx_fifo_hw->fifo,\n\t\t\tvblank_line_vsync_off,\n\t\t\tcount_of(vblank_line_vsync_off),\n\t\t\tfalse\n\t\t);\n\t\tdma_hw->ints3 |= 1u << (DMACH_HSTX_START + i);\n\t\tdma_hw->inte3 |= 1u << (DMACH_HSTX_START + i);\n\t}\n\n\t/* give the DMA the priority over the CPU on the bus */\n\tbus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS;\n}\n"
  },
  {
    "path": "libpicohsdaoh/picohsdaoh.h",
    "content": "#ifndef _PICOHSDAOH_H\n#define _PICOHSDAOH_H\n\n// trimmed to absolute minimum timings the MS2130 accepts\n#define MODE_H_FRONT_PORCH\t6\n#define MODE_H_SYNC_WIDTH\t45\n#define MODE_H_BACK_PORCH\t11\n#define MODE_H_ACTIVE_PIXELS\t1920\n\n#define MODE_V_FRONT_PORCH\t1\n#define MODE_V_SYNC_WIDTH\t2\n#define MODE_V_BACK_PORCH\t1\n#define MODE_V_ACTIVE_LINES\t1080\n\n#define MODE_H_TOTAL_PIXELS ( \\\n\tMODE_H_FRONT_PORCH + MODE_H_SYNC_WIDTH + \\\n\tMODE_H_BACK_PORCH  + MODE_H_ACTIVE_PIXELS \\\n)\n#define MODE_V_TOTAL_LINES  ( \\\n\tMODE_V_FRONT_PORCH + MODE_V_SYNC_WIDTH + \\\n\tMODE_V_BACK_PORCH  + MODE_V_ACTIVE_LINES \\\n)\n\n// stream ID, CRC word and length/metadata word, so 3 reserved words\n#define NUM_RESERVED_WORDS\t3\n#define RBUF_DEFAULT_SLICES\t16\n#define RBUF_SLICE_LEN\t\tMODE_H_ACTIVE_PIXELS\n#define RBUF_MAX_DATA_LEN\t(RBUF_SLICE_LEN - NUM_RESERVED_WORDS)\n#define RBUF_DEFAULT_TOTAL_LEN\t(RBUF_SLICE_LEN * RBUF_DEFAULT_SLICES)\n\n#define MAX_STREAMS\t\t4\n\nenum crc_config {\n\tCRC_NONE,\t\t/* No CRC, just 16 bit idle counter */\n\tCRC16_1_LINE,\t\t/* Line contains CRC of the last line */\n\tCRC16_2_LINE\t\t/* Line contains CRC of the line before the last line */\n};\n\ntypedef struct\n{\n\tuint64_t data_cnt;\n\tuint32_t srate;\n\tuint32_t reserved1;\n\tchar reserved2[16];\n} __attribute__((packed, aligned(1))) stream_info_t;\n\ntypedef struct\n{\n\tuint32_t magic;\n\tuint16_t framecounter;\n\tuint8_t  reserved1;\n\tuint8_t  crc_config;\n\tuint16_t version;\n\tuint32_t flags;\n\tuint32_t reserved2[8];\n\tuint16_t stream0_format;\n\tuint16_t max_streamid;\n\tstream_info_t stream_info[MAX_STREAMS];\n} __attribute__((packed, aligned(1))) metadata_t;\n\n#define FLAG_STREAM_ID_PRESENT\t(1 << 0)\n#define FLAG_FORMAT_ID_PRESENT\t(1 << 1)\n\nenum\n{\n\tRAW_8BIT,\n\tRAW_16BIT,\n\tRAW_24BIT,\n\tRAW_32BIT,\n\tRAW_64BIT,\n\tPIO_1BIT,\n\tPIO_2BIT,\n\tPIO_3BIT,\n\tPIO_4BIT,\n\tPIO_5BIT,\n\tPIO_6BIT,\n\tPIO_7BIT,\n\tPIO_8BIT,\n\tPIO_8BIT_DUAL,\n\tPIO_8BIT_IQ,\n\tPIO_9BIT,\n\tPIO_10BIT,\n\tPIO_10BIT_DUAL,\n\tPIO_10BIT_IQ,\n\tPIO_11BIT,\n\tPIO_12BIT,\n\tPIO_12BIT_DUAL,\n\tPIO_12BIT_IQ,\n\tPIO_13BIT,\n\tPIO_14BIT,\n\tPIO_14BIT_DUAL,\n\tPIO_14BIT_IQ,\n\tPIO_15BIT,\n\tPIO_16BIT,\n\tPIO_16BIT_DUAL,\n\tPIO_16BIT_IQ,\n\tPIO_17BIT,\n\tPIO_18BIT,\n\tPIO_19BIT,\n\tPIO_20BIT,\n\tPIO_24BIT,\n\tPIO_24BIT_IQ,\n\tPIO_28BIT,\n\tPIO_32BIT,\n\tPIO_32BIT_IQ,\n\tPIO_PCM1802_AUDIO,\n\tPIO_AUDIO_PLACEHOLDER1,\n\tPIO_AUDIO_PLACEHOLDER2,\n\tPIO_AUDIO_PLACEHOLDER3,\n\tPIO_DUALCHAN_1BIT,\n\tPIO_DUALCHAN_1BIT_IQ,\n\tPIO_DUALCHAN_2BIT,\n\tPIO_DUALCHAN_2BIT_IQ,\n\tPIO_DUALCHAN_4BIT,\n\tPIO_DUALCHAN_4BIT_IQ,\n\tPIO_DUALCHAN_8BIT,\n\tPIO_DUALCHAN_8BIT_IQ,\n\tPIO_DUALCHAN_10BIT,\n\tPIO_DUALCHAN_10BIT_IQ,\n\tPIO_DUALCHAN_12BIT,\n\tPIO_DUALCHAN_12BIT_IQ,\n\tPIO_DUALCHAN_14BIT,\n\tPIO_DUALCHAN_14BIT_IQ,\n\tPIO_DUALCHAN_16BIT,\n\tPIO_DUALCHAN_16BIT_IQ,\n\tPIO_DUALCHAN_24BIT,\n\tPIO_DUALCHAN_24BIT_IQ,\n\tPIO_DUALCHAN_32BIT,\n\tPIO_DUALCHAN_32BIT_IQ,\n};\n\nvoid hsdaoh_start(void);\nvoid hsdaoh_update_head(int stream_id, int head);\nint hsdaoh_add_stream(uint16_t stream_id, uint16_t format, uint32_t samplerate, uint length, uint slices, uint16_t *ringbuf);\nint hsdaoh_remove_stream(uint16_t stream_id);\nvoid hsdaoh_set_sys_clock_khz(uint32_t freq_khz);\nvoid hsdaoh_init(int dstrength, int slewrate);\n#endif\n"
  },
  {
    "path": "pico_sdk_import.cmake",
    "content": "# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake\n\n# This can be dropped into an external project to help locate this SDK\n# It should be include()ed prior to project()\n\nif (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))\n    set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})\n    message(\"Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')\")\nendif ()\n\nif (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))\n    set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})\n    message(\"Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')\")\nendif ()\n\nif (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))\n    set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})\n    message(\"Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')\")\nendif ()\n\nif (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))\n    set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})\n    message(\"Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')\")\nendif ()\n\nif (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)\n  set(PICO_SDK_FETCH_FROM_GIT_TAG \"master\")\n  message(\"Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG\")\nendif()\n\nset(PICO_SDK_PATH \"${PICO_SDK_PATH}\" CACHE PATH \"Path to the Raspberry Pi Pico SDK\")\nset(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\")\nset(PICO_SDK_FETCH_FROM_GIT_PATH \"${PICO_SDK_FETCH_FROM_GIT_PATH}\" CACHE FILEPATH \"location to download SDK\")\nset(PICO_SDK_FETCH_FROM_GIT_TAG \"${PICO_SDK_FETCH_FROM_GIT_TAG}\" CACHE FILEPATH \"release tag for SDK\")\n\nif (NOT PICO_SDK_PATH)\n    if (PICO_SDK_FETCH_FROM_GIT)\n        include(FetchContent)\n        set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})\n        if (PICO_SDK_FETCH_FROM_GIT_PATH)\n            get_filename_component(FETCHCONTENT_BASE_DIR \"${PICO_SDK_FETCH_FROM_GIT_PATH}\" REALPATH BASE_DIR \"${CMAKE_SOURCE_DIR}\")\n        endif ()\n        # GIT_SUBMODULES_RECURSE was added in 3.17\n        if (${CMAKE_VERSION} VERSION_GREATER_EQUAL \"3.17.0\")\n            FetchContent_Declare(\n                    pico_sdk\n                    GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk\n                    GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}\n                    GIT_SUBMODULES_RECURSE FALSE\n            )\n        else ()\n            FetchContent_Declare(\n                    pico_sdk\n                    GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk\n                    GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}\n            )\n        endif ()\n\n        if (NOT pico_sdk)\n            message(\"Downloading Raspberry Pi Pico SDK\")\n            FetchContent_Populate(pico_sdk)\n            set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})\n        endif ()\n        set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})\n    else ()\n        message(FATAL_ERROR\n                \"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git.\"\n                )\n    endif ()\nendif ()\n\nget_filename_component(PICO_SDK_PATH \"${PICO_SDK_PATH}\" REALPATH BASE_DIR \"${CMAKE_BINARY_DIR}\")\nif (NOT EXISTS ${PICO_SDK_PATH})\n    message(FATAL_ERROR \"Directory '${PICO_SDK_PATH}' not found\")\nendif ()\n\nset(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)\nif (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})\n    message(FATAL_ERROR \"Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK\")\nendif ()\n\nset(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH \"Path to the Raspberry Pi Pico SDK\" FORCE)\n\ninclude(${PICO_SDK_INIT_CMAKE_FILE})\n"
  }
]