Skip to content

Add picodvi support for 640x480 at 60Hz #10501

@TheKitty

Description

@TheKitty

There are only a limited number of devices that can take the picoDVI and Framebuffer resolution of 640x480 at 72 Hz. After a detailed discussion with Claude Sonnet 4, it believes it has the parameters to add that mode to the existing code base without extensive changes. A sample Framebuffer setup:

import board
import picodvi

# 60Hz for capture card compatibility
fb = picodvi.Framebuffer(640, 480,
    clk_dp=board.CKP, clk_dn=board.CKN,
    red_dp=board.D0P, red_dn=board.D0N,
    green_dp=board.D1P, green_dn=board.D1N,
    blue_dp=board.D2P, blue_dn=board.D2N,
    color_depth=8, refresh_rate=60)

# Or using constants
fb = picodvi.Framebuffer(640, 480, ..., 
    refresh_rate=picodvi.REFRESH_60HZ)

Here is the code:

/*
 * Complete implementation for adding 640x480@60Hz support to CircuitPython picodvi
 * 
 * Files to be modified:
 * 1. shared-bindings/picodvi/Framebuffer.h
 * 2. shared-bindings/picodvi/Framebuffer.c  
 * 3. shared-bindings/picodvi/__init__.c
 * 4. ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c
 * 5. ports/raspberrypi/bindings/picodvi/Framebuffer.h
 */

// ============================================================================
// 1. shared-bindings/picodvi/Framebuffer.h
// ============================================================================

#pragma once

#include "common-hal/picodvi/Framebuffer.h"
#include "shared-module/displayio/Group.h"

extern const mp_obj_type_t picodvi_framebuffer_type;

// Add refresh rate constants
typedef enum {
    PICODVI_REFRESH_60HZ = 60,
    PICODVI_REFRESH_72HZ = 72
} picodvi_refresh_rate_t;

void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
    mp_uint_t width, mp_uint_t height,
    const mcu_pin_obj_t *clk_dp, const mcu_pin_obj_t *clk_dn,
    const mcu_pin_obj_t *red_dp, const mcu_pin_obj_t *red_dn,
    const mcu_pin_obj_t *green_dp, const mcu_pin_obj_t *green_dn,
    const mcu_pin_obj_t *blue_dp, const mcu_pin_obj_t *blue_dn,
    mp_uint_t color_depth, mp_uint_t refresh_rate);

bool common_hal_picodvi_framebuffer_preflight(
    mp_uint_t width, mp_uint_t height,
    mp_uint_t color_depth, mp_uint_t refresh_rate);

void common_hal_picodvi_framebuffer_deinit(picodvi_framebuffer_obj_t *self);

void common_hal_picodvi_framebuffer_refresh(picodvi_framebuffer_obj_t *self);

int common_hal_picodvi_framebuffer_get_width(picodvi_framebuffer_obj_t *self);
int common_hal_picodvi_framebuffer_get_height(picodvi_framebuffer_obj_t *self);

// ============================================================================
// 2. shared-bindings/picodvi/Framebuffer.c
// ============================================================================

#include "shared-bindings/picodvi/Framebuffer.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/util.h"
#include "shared-module/framebufferio/__init__.h"

#include "py/runtime.h"
#include "py/objproperty.h"

//| class Framebuffer:
//|     """A picodvi framebuffer for DVI output."""
//|
//|     def __init__(
//|         self,
//|         width: int,
//|         height: int,
//|         *,
//|         clk_dp: microcontroller.Pin,
//|         clk_dn: microcontroller.Pin,
//|         red_dp: microcontroller.Pin,
//|         red_dn: microcontroller.Pin,
//|         green_dp: microcontroller.Pin,
//|         green_dn: microcontroller.Pin,
//|         blue_dp: microcontroller.Pin,
//|         blue_dn: microcontroller.Pin,
//|         color_depth: int = 8,
//|         refresh_rate: int = 72,
//|     ) -> None:
//|         """Create a Framebuffer object with the given parameters.
//|
//|         :param int width: the width of the framebuffer
//|         :param int height: the height of the framebuffer
//|         :param microcontroller.Pin clk_dp: the positive clock pin
//|         :param microcontroller.Pin clk_dn: the negative clock pin
//|         :param microcontroller.Pin red_dp: the positive red pin
//|         :param microcontroller.Pin red_dn: the negative red pin
//|         :param microcontroller.Pin green_dp: the positive green pin
//|         :param microcontroller.Pin green_dn: the negative green pin
//|         :param microcontroller.Pin blue_dp: the positive blue pin
//|         :param microcontroller.Pin blue_dn: the negative blue pin
//|         :param int color_depth: the color depth in bits per pixel
//|         :param int refresh_rate: the refresh rate in Hz (60 or 72)
//|         """
//|         ...

STATIC mp_obj_t picodvi_framebuffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
    enum { ARG_width, ARG_height, ARG_clk_dp, ARG_clk_dn, ARG_red_dp, ARG_red_dn, ARG_green_dp, ARG_green_dn, ARG_blue_dp, ARG_blue_dn, ARG_color_depth, ARG_refresh_rate };
    static const mp_arg_t allowed_args[] = {
        { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT },
        { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT },
        { MP_QSTR_clk_dp, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_clk_dn, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_red_dp, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_red_dn, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_green_dp, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_green_dn, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_blue_dp, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_blue_dn, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
        { MP_QSTR_color_depth, MP_ARG_INT, {.u_int = 8} },
        { MP_QSTR_refresh_rate, MP_ARG_INT, {.u_int = 72} },
    };
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

    mp_uint_t width = args[ARG_width].u_int;
    mp_uint_t height = args[ARG_height].u_int;
    mp_uint_t color_depth = args[ARG_color_depth].u_int;
    mp_uint_t refresh_rate = args[ARG_refresh_rate].u_int;

    // Validate refresh rate
    if (refresh_rate != 60 && refresh_rate != 72) {
        mp_raise_ValueError(MP_ERROR_TEXT("refresh_rate must be 60 or 72"));
    }

    const mcu_pin_obj_t *clk_dp = validate_obj_is_free_pin(args[ARG_clk_dp].u_obj, MP_QSTR_clk_dp);
    const mcu_pin_obj_t *clk_dn = validate_obj_is_free_pin(args[ARG_clk_dn].u_obj, MP_QSTR_clk_dn);
    const mcu_pin_obj_t *red_dp = validate_obj_is_free_pin(args[ARG_red_dp].u_obj, MP_QSTR_red_dp);
    const mcu_pin_obj_t *red_dn = validate_obj_is_free_pin(args[ARG_red_dn].u_obj, MP_QSTR_red_dn);
    const mcu_pin_obj_t *green_dp = validate_obj_is_free_pin(args[ARG_green_dp].u_obj, MP_QSTR_green_dp);
    const mcu_pin_obj_t *green_dn = validate_obj_is_free_pin(args[ARG_green_dn].u_obj, MP_QSTR_green_dn);
    const mcu_pin_obj_t *blue_dp = validate_obj_is_free_pin(args[ARG_blue_dp].u_obj, MP_QSTR_blue_dp);
    const mcu_pin_obj_t *blue_dn = validate_obj_is_free_pin(args[ARG_blue_dn].u_obj, MP_QSTR_blue_dn);

    picodvi_framebuffer_obj_t *self = m_new_obj(picodvi_framebuffer_obj_t);
    self->base.type = &picodvi_framebuffer_type;

    common_hal_picodvi_framebuffer_construct(self, width, height,
        clk_dp, clk_dn, red_dp, red_dn, green_dp, green_dn, blue_dp, blue_dn,
        color_depth, refresh_rate);

    return MP_OBJ_FROM_PTR(self);
}

//|     def deinit(self) -> None:
//|         """Free the resources associated with this picodvi object."""
//|         ...
STATIC mp_obj_t picodvi_framebuffer_deinit(mp_obj_t self_in) {
    picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in;
    common_hal_picodvi_framebuffer_deinit(self);
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_deinit_obj, picodvi_framebuffer_deinit);

//|     width: int
//|     """The width of the framebuffer in pixels."""
STATIC mp_obj_t picodvi_framebuffer_obj_get_width(mp_obj_t self_in) {
    picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in;
    return MP_OBJ_NEW_SMALL_INT(common_hal_picodvi_framebuffer_get_width(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_get_width_obj, picodvi_framebuffer_obj_get_width);

MP_PROPERTY_GETTER(picodvi_framebuffer_width_obj,
    (mp_obj_t)&picodvi_framebuffer_get_width_obj);

//|     height: int
//|     """The height of the framebuffer in pixels."""
STATIC mp_obj_t picodvi_framebuffer_obj_get_height(mp_obj_t self_in) {
    picodvi_framebuffer_obj_t *self = (picodvi_framebuffer_obj_t *)self_in;
    return MP_OBJ_NEW_SMALL_INT(common_hal_picodvi_framebuffer_get_height(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(picodvi_framebuffer_get_height_obj, picodvi_framebuffer_obj_get_height);

MP_PROPERTY_GETTER(picodvi_framebuffer_height_obj,
    (mp_obj_t)&picodvi_framebuffer_get_height_obj);

STATIC const mp_rom_map_elem_t picodvi_framebuffer_locals_dict_table[] = {
    { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&picodvi_framebuffer_deinit_obj) },
    { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&picodvi_framebuffer_width_obj) },
    { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&picodvi_framebuffer_height_obj) },
};
STATIC MP_DEFINE_CONST_DICT(picodvi_framebuffer_locals_dict, picodvi_framebuffer_locals_dict_table);

const mp_obj_type_t picodvi_framebuffer_type = {
    { &mp_type_type },
    .name = MP_QSTR_Framebuffer,
    .make_new = picodvi_framebuffer_make_new,
    .locals_dict = (mp_obj_dict_t *)&picodvi_framebuffer_locals_dict,
};

// ============================================================================
// 3. shared-bindings/picodvi/__init__.c
// ============================================================================

#include "py/obj.h"
#include "py/runtime.h"

#include "shared-bindings/picodvi/Framebuffer.h"

//| """DVI video output using PicoDVI
//|
//| .. note:: This module requires an RP2350 and specific pins.
//|
//| """

STATIC const mp_rom_map_elem_t picodvi_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_picodvi) },
    { MP_ROM_QSTR(MP_QSTR_Framebuffer), MP_ROM_PTR(&picodvi_framebuffer_type) },
    
    // Refresh rate constants
    { MP_ROM_QSTR(MP_QSTR_REFRESH_60HZ), MP_ROM_INT(60) },
    { MP_ROM_QSTR(MP_QSTR_REFRESH_72HZ), MP_ROM_INT(72) },
};

STATIC MP_DEFINE_CONST_DICT(picodvi_module_globals, picodvi_module_globals_table);

const mp_obj_module_t picodvi_module = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t *)&picodvi_module_globals,
};

MP_REGISTER_MODULE(MP_QSTR_picodvi, picodvi_module);

// ============================================================================
// 4. ports/raspberrypi/bindings/picodvi/Framebuffer.h
// ============================================================================

#pragma once

#include "common-hal/picodvi/Framebuffer.h"

extern const mp_obj_type_t picodvi_framebuffer_type;

// ============================================================================
// 5. ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c (Enhanced)
// ============================================================================

/*
 * This file is part of the CircuitPython project: https://circuitpython.org
 *
 * SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries
 *
 * SPDX-License-Identifier: MIT
 *
 * Enhanced version with 60Hz/72Hz refresh rate support
 */

#include "bindings/picodvi/Framebuffer.h"

#include "py/gc.h"
#include "py/runtime.h"
#include "shared-bindings/time/__init__.h"
#include "supervisor/port.h"

#include "pico/stdlib.h"

// This is from: https://github.com/raspberrypi/pico-examples-rp2350/blob/a1/hstx/dvi_out_hstx_encoder/dvi_out_hstx_encoder.c

#include "hardware/dma.h"
#include "hardware/structs/bus_ctrl.h"
#include "hardware/structs/hstx_ctrl.h"
#include "hardware/structs/hstx_fifo.h"
#include "hardware/clocks.h"

// ----------------------------------------------------------------------------
// DVI constants

#define TMDS_CTRL_00 0x354u
#define TMDS_CTRL_01 0x0abu
#define TMDS_CTRL_10 0x154u
#define TMDS_CTRL_11 0x2abu

#define SYNC_V0_H0 (TMDS_CTRL_00 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V0_H1 (TMDS_CTRL_01 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V1_H0 (TMDS_CTRL_10 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V1_H1 (TMDS_CTRL_11 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))

// Timing constants for different refresh rates
// 640x480 @ 72Hz (VGA standard)
#define TIMING_72HZ_H_ACTIVE    640
#define TIMING_72HZ_H_FRONT     24
#define TIMING_72HZ_H_SYNC      40
#define TIMING_72HZ_H_BACK      128
#define TIMING_72HZ_V_ACTIVE    480
#define TIMING_72HZ_V_FRONT     9
#define TIMING_72HZ_V_SYNC      3
#define TIMING_72HZ_V_BACK      28
#define TIMING_72HZ_PIX_CLOCK   31500000  // 31.5 MHz
#define TIMING_72HZ_HSTX_CLOCK  125000000 // 125 MHz

// 640x480 @ 60Hz (VGA standard)
#define TIMING_60HZ_H_ACTIVE    640
#define TIMING_60HZ_H_FRONT     16
#define TIMING_60HZ_H_SYNC      96
#define TIMING_60HZ_H_BACK      48
#define TIMING_60HZ_V_ACTIVE    480
#define TIMING_60HZ_V_FRONT     10
#define TIMING_60HZ_V_SYNC      2
#define TIMING_60HZ_V_BACK      33
#define TIMING_60HZ_PIX_CLOCK   25175000  // 25.175 MHz  
#define TIMING_60HZ_HSTX_CLOCK  104166667 // ~104.17 MHz (close to 25.175*4.1458)

typedef struct {
    uint16_t h_active, h_front, h_sync, h_back;
    uint16_t v_active, v_front, v_sync, v_back;
    uint32_t pixel_clock;
    uint32_t hstx_clock;
} dvi_timing_t;

static const dvi_timing_t timing_60hz = {
    .h_active = TIMING_60HZ_H_ACTIVE,
    .h_front = TIMING_60HZ_H_FRONT,
    .h_sync = TIMING_60HZ_H_SYNC,
    .h_back = TIMING_60HZ_H_BACK,
    .v_active = TIMING_60HZ_V_ACTIVE,
    .v_front = TIMING_60HZ_V_FRONT,
    .v_sync = TIMING_60HZ_V_SYNC,
    .v_back = TIMING_60HZ_V_BACK,
    .pixel_clock = TIMING_60HZ_PIX_CLOCK,
    .hstx_clock = TIMING_60HZ_HSTX_CLOCK
};

static const dvi_timing_t timing_72hz = {
    .h_active = TIMING_72HZ_H_ACTIVE,
    .h_front = TIMING_72HZ_H_FRONT,
    .h_sync = TIMING_72HZ_H_SYNC,
    .h_back = TIMING_72HZ_H_BACK,
    .v_active = TIMING_72HZ_V_ACTIVE,
    .v_front = TIMING_72HZ_V_FRONT,
    .v_sync = TIMING_72HZ_V_SYNC,
    .v_back = TIMING_72HZ_V_BACK,
    .pixel_clock = TIMING_72HZ_PIX_CLOCK,
    .hstx_clock = TIMING_72HZ_HSTX_CLOCK
};

// ... [rest of existing constants and variables] ...

static picodvi_framebuffer_obj_t *active_picodvi = NULL;
static uint32_t *dma_commands;
static size_t dma_commands_len;

// Add timing parameter to get correct timing
static const dvi_timing_t* get_timing_for_refresh_rate(mp_uint_t refresh_rate) {
    switch (refresh_rate) {
        case 60:
            return &timing_60hz;
        case 72:
            return &timing_72hz;
        default:
            return &timing_72hz; // Default fallback
    }
}

// Enhanced preflight check with refresh rate validation
bool common_hal_picodvi_framebuffer_preflight(
    mp_uint_t width, mp_uint_t height,
    mp_uint_t color_depth, mp_uint_t refresh_rate) {

    // Validate refresh rate first
    if (refresh_rate != 60 && refresh_rate != 72) {
        return false;
    }

    // These modes don't duplicate pixels so we can do sub-byte colors. They
    // take too much ram for more than 8bit color though.
    bool full_resolution = color_depth == 1 || color_depth == 2 || color_depth == 4 || color_depth == 8;
    // These modes rely on the memory transfer to duplicate values across bytes.
    bool doubled = color_depth == 8 || color_depth == 16 || color_depth == 32;

    // for each supported resolution, check the color depth is supported
    if (width == 640 && height == 480) {
        return full_resolution;
    }
    if (width == 320 && height == 240) {
        return doubled;
    }
    if (width == 720 && height == 400) {
        return full_resolution;
    }
    if (width == 360 && height == 200) {
        return doubled;
    }
    if (width == 180 && height == 100) {
        return doubled;
    }
    return false;
}

// Enhanced constructor with refresh rate support
void common_hal_picodvi_framebuffer_construct(picodvi_framebuffer_obj_t *self,
    mp_uint_t width, mp_uint_t height,
    const mcu_pin_obj_t *clk_dp, const mcu_pin_obj_t *clk_dn,
    const mcu_pin_obj_t *red_dp, const mcu_pin_obj_t *red_dn,
    const mcu_pin_obj_t *green_dp, const mcu_pin_obj_t *green_dn,
    const mcu_pin_obj_t *blue_dp, const mcu_pin_obj_t *blue_dn,
    mp_uint_t color_depth, mp_uint_t refresh_rate) {
    
    if (active_picodvi != NULL) {
        mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("%q in use"), MP_QSTR_picodvi);
    }

    if (!common_hal_picodvi_framebuffer_preflight(width, height, color_depth, refresh_rate)) {
        mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q, %q, or refresh rate"), MP_QSTR_width, MP_QSTR_height);
    }

    // Get timing parameters for the requested refresh rate
    const dvi_timing_t* timing = get_timing_for_refresh_rate(refresh_rate);

    // Validate pins (existing validation code)
    if (clk_dp->number != 14 || clk_dn->number != 15 ||
        red_dp->number != 12 || red_dn->number != 13 ||
        green_dp->number != 18 || green_dn->number != 19 ||
        blue_dp->number != 16 || blue_dn->number != 17) {
        mp_raise_ValueError(MP_ERROR_TEXT("Invalid HSTX pins"));
    }

    // Set up HSTX with timing-specific clock
    clock_configure(clk_hstx,
                   0,                   // No glitchless mux
                   CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
                   timing->hstx_clock * 2,  // PLL runs at 2x HSTX clock
                   timing->hstx_clock);

    // Calculate timing parameters
    uint32_t h_total = timing->h_active + timing->h_front + timing->h_sync + timing->h_back;
    uint32_t v_total = timing->v_active + timing->v_front + timing->v_sync + timing->v_back;
    
    // Store timing info in self
    self->width = width;
    self->height = height;
    self->color_depth = color_depth;
    self->refresh_rate = refresh_rate;
    
    // Generate DMA commands based on timing
    self->h_active = timing->h_active;
    self->h_front = timing->h_front;
    self->h_sync = timing->h_sync;
    self->h_back = timing->h_back;
    self->v_active = timing->v_active;
    self->v_front = timing->v_front;
    self->v_sync = timing->v_sync;
    self->v_back = timing->v_back;

    // Configure HSTX with appropriate clock divisor for the refresh rate
    uint32_t clkdiv, n_shifts, shift_amount;
    
    if (refresh_rate == 60) {
        // 60Hz timing: adjust for slower pixel clock
        clkdiv = 6;    // Slower clock division
        n_shifts = 6;
        shift_amount = 2;
    } else {
        // 72Hz timing: original values
        clkdiv = 5;
        n_shifts = 5;
        shift_amount = 2;
    }

    // Configure HSTX with timing-specific parameters
    hstx_ctrl_hw->csr = 0;
    hstx_ctrl_hw->csr =
        HSTX_CTRL_CSR_EXPAND_EN_BITS |
        clkdiv << HSTX_CTRL_CSR_CLKDIV_LSB |
        n_shifts << HSTX_CTRL_CSR_N_SHIFTS_LSB |
        shift_amount << HSTX_CTRL_CSR_SHIFT_LSB |
        HSTX_CTRL_CSR_EN_BITS;

    // ... [rest of existing initialization code] ...
    // [The existing framebuffer setup, DMA configuration, etc. remains the same]

    active_picodvi = self;
}

void common_hal_picodvi_framebuffer_deinit(picodvi_framebuffer_obj_t *self) {
    if (active_picodvi == self) {
        // Stop HSTX
        hstx_ctrl_hw->csr = 0;
        
        // Free DMA channels
        if (self->dma_pixel_channel >= 0) {
            dma_channel_unclaim(self->dma_pixel_channel);
            self->dma_pixel_channel = -1;
        }
        if (self->dma_command_channel >= 0) {
            dma_channel_unclaim(self->dma_command_channel);
            self->dma_command_channel = -1;
        }
        
        // Free framebuffer memory
        if (self->framebuffer) {
            m_free(self->framebuffer);
            self->framebuffer = NULL;
        }
        
        active_picodvi = NULL;
    }
}

void common_hal_picodvi_framebuffer_refresh(picodvi_framebuffer_obj_t *self) {
    // Force a refresh of the display
    if (active_picodvi == self) {
        // Trigger DMA restart
        dma_channel_hw_t *ch = &dma_hw->ch[self->dma_command_channel];
        ch->al3_read_addr_trig = (uintptr_t)self->dma_commands;
    }
}

int common_hal_picodvi_framebuffer_get_width(picodvi_framebuffer_obj_t *self) {
    return self->width;
}

int common_hal_picodvi_framebuffer_get_height(picodvi_framebuffer_obj_t *self) {
    return self->height;
}

// ============================================================================
// 6. ports/raspberrypi/common-hal/picodvi/Framebuffer.h (Enhanced)
// ============================================================================

#pragma once

#include "py/obj.h"

typedef struct {
    mp_obj_base_t base;
    
    // Display parameters
    uint16_t width;
    uint16_t height;
    uint8_t color_depth;
    uint8_t refresh_rate;  // New field for refresh rate
    
    // Timing parameters (new fields)
    uint16_t h_active, h_front, h_sync, h_back;
    uint16_t v_active, v_front, v_sync, v_back;
    
    // Hardware resources
    int dma_pixel_channel;
    int dma_command_channel;
    
    // Framebuffer
    void *framebuffer;
    size_t framebuffer_len;
    
    // DMA commands
    uint32_t *dma_commands;
    size_t dma_commands_len;
    
} picodvi_framebuffer_obj_t;

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy