; Copyright 2009 Castle Technology Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;

        GET     Hdr:ListOpts
        GET     Hdr:Macros
        GET     Hdr:System
        GET     Hdr:Machine.<Machine>
        GET     Hdr:ImageSize.<ImageSize>
        $GetIO
        GET     Hdr:Proc

        GET     Hdr:OSEntries
        GET     Hdr:HALEntries

        GET     hdr.omap3530
        GET     hdr.StaticWS
        GET     hdr.Video
        GET     hdr.PRCM
        GET     hdr.GPIO
        GET     hdr.CoPro15ops

        AREA    |Asm$$Code|, CODE, READONLY, PIC

        EXPORT  Video_init
        EXPORT  HAL_VideoFlybackDevice
        EXPORT  HAL_Video_SetMode
        EXPORT  HAL_Video_WritePaletteEntry
        EXPORT  HAL_Video_WritePaletteEntries
        EXPORT  HAL_Video_ReadPaletteEntry
        EXPORT  HAL_Video_SetInterlace
        EXPORT  HAL_Video_SetBlank
        EXPORT  HAL_Video_SetPowerSave
        EXPORT  HAL_Video_UpdatePointer
        EXPORT  HAL_Video_SetDAG
        EXPORT  HAL_Video_VetMode
        EXPORT  HAL_Video_PixelFormats
        EXPORT  HAL_Video_Features
        EXPORT  HAL_Video_BufferAlignment
        EXPORT  HAL_Video_OutputFormat

        IMPORT  vtophys
        IMPORT  |__rt_udiv|
        IMPORT  DebugHALPrint
        IMPORT  DebugHALPrintReg
        IMPORT  PRCM_GetFreqSel
        IMPORT  HAL_CounterDelay
        IMPORT  DebugMemDump

; A brief rundown of OMAP3530 video output modes:
; RFBI - Parallel interface using MIPI DBI protocol. Supports two RFBI-compliant LCD panels
; RFBI bypass - Parallel interface using MIPI DPI protocol
; SDI - Serial interface for TI FlatLink 3G panels (and for those alone?)
; DSI - Serial interface for other types of LCD panel. Supports multiple panels via mix of video & command modes.
; TV out - Supports composite & S-Video, PAL & NTSC

; Currently this code only supports the RFBI bypass mode, as used to interface with the TFP410 DVI framer used on the beagleboard. Luckily this is also the simplest interface of the lot.

; A note on pixel clock sources:
; There are two pixel clock sources - one from DPLL4 in the PRCM, and the DSI PLL. DPLL4 provides the source for multiple clocks, so changing its frequency at runtime would cause many problems. So we use the DSI PLL instead, as its clock output only affects the pixel clock in the display controller (which we do use) and the DSI protocol engine (which we don't use for RFBI bypass mode)

DSI_BPP     *   24 ; BPP of the data sent out by DSI
DSI_LANES   *   3  ; Number of data lanes used by DSI

; TFP410 power is controlled via GPIO 170
TFP410_GPIO * 170
TFP410_Ptr  * L4_GPIO_Table+4*(TFP410_GPIO>>5)
TFP410_Pin  * 1 :SHL: (TFP410_GPIO :AND: 31)

; Max pixel rate:
; - DSI1_PLL_FCLK is limited to 173MHz (spruf98b p2113). Combine with minimum LCD & PCD values and you get a max pixel rate of 86.5MHz (173/2)
; - However spruf98b p2008 says the max pixel rate is 75MHz
; - Beagleboard ref. manual says 65MHz (TFP410 limitation?)

; For the moment, limit the pixel rate to 75MHz. This allows us to generate 1280x1024 @ 50Hz (86MHz was also tried, but resulted in signal breakup when dragging windows round the screen)
MAX_PIXEL_RATE * 75000

; Clock flow:
; sys_clk -> DSS2_ALWON_FCLK -> DSI PLL -> DSI1_PLL_FCLK, DSI2_PLL_FCLK (173MHz max)
; DSI1_PLL_FCLK -> DISPC_FCLK -> LCD -> PCD (75MHz max)
; DSI2_PLL_FCLK -> DSI_FCLK. Must be > byte clock(?), L4 interface clock, video port clock. But we don't care for RFBI bypass.
; -----------------------------------------------------------------------------------

        MACRO
        SetRegBits $r,$bits
        LDR     a2, [a1, #$r]
        ORR     a2, a2, #$bits
        STR     a2, [a1, #$r]
        MEND

        MACRO
        DumpReg $r
        DebugTX "$r"
        LDR     a1, =$r
        LDR     a1, [a3, a1]
        DebugReg a1
        MEND

                GBLL    VideoDebug
VideoDebug      SETL    {TRUE}

                GBLL    ExtraVideoDebug
ExtraVideoDebug SETL    {FALSE}

                GBLL    VetModeDebug
VetModeDebug    SETL    {FALSE}

        MACRO
        SetGoBits $base,$temp,$temp2,$str
;        MOV     $temp2, #1024*1024 ; How long is typical wait time? Do we need to wait for vsync or what?
;5
;        LDR     $temp, [$base, #DISPC_CONTROL]
;        TST     $temp, #&1:SHL:5 ; Only test GOLCD, since EVSYNC doesn't happen often enough on beagleboard?
;        BEQ     %FT10
;        SUBS    $temp2, $temp2, #1
;        BNE     %BT5
; [ VideoDebug
;        Push "lr" ; Just in case
;        DebugTX $str
;        DebugReg $temp
;        Pull "lr"
; ]
;10
;        ORR     $temp, $temp, #&3:SHL:5
;        STR     $temp, [$base, #DISPC_CONTROL]
        ; New SetGoBits: Rather than blocking on the update and clashing with UpdatePointer, we just set a flag which UpdatePointer will monitor
        ; Only downside is that if interrupts are disabled for a long time, nothing will happen. But long stretches without interrupts is generally a bad thing anyway.
        MOV     $temp, #3:SHL:5
        STR     $temp, video_gobits
        MEND


 [ ExtraVideoDebug
        LTORG

DumpDispcRegs
      Push    "a1-a3,lr"
      LDR     a3, L4_Display_Log
      DumpReg DSS_SYSCONFIG
      DumpReg DSS_SYSSTATUS
      DumpReg DSS_IRQSTATUS
      DumpReg DSS_CONTROL
      DumpReg DSS_SDI_CONTROL
      DumpReg DSS_PLL_CONTROL
      DumpReg DSS_SDI_STATUS
      DumpReg DISPC_SYSCONFIG
      DumpReg DISPC_SYSTATUS
      DumpReg DISPC_IRQSTATUS
      DumpReg DISPC_IRQENABLE
      DumpReg DISPC_CONTROL
      DumpReg DISPC_CONFIG
      DumpReg DISPC_DEFAULT_COLOR0
      DumpReg DISPC_DEFAULT_COLOR1
      DumpReg DISPC_TRANS_COLOR0
      DumpReg DISPC_TRANS_COLOR1
      DumpReg DISPC_LINE_STATUS
      DumpReg DISPC_LINE_NUMBER
      DumpReg DISPC_TIMING_H
      DumpReg DISPC_TIMING_V
      DumpReg DISPC_POL_FREQ
      DumpReg DISPC_DIVISOR
      DumpReg DISPC_GLOBAL_ALPHA
      DumpReg DISPC_SIZE_DIG
      DumpReg DISPC_SIZE_LCD
      DumpReg DISPC_GFX_BA0
      DumpReg DISPC_GFX_BA1
      DumpReg DISPC_GFX_POSITION
      DumpReg DISPC_GFX_SIZE
      DumpReg DISPC_GFX_ATTRIBUTES
      DumpReg DISPC_GFX_FIFO_THRESHOLD
      DumpReg DISPC_GFX_FIFO_SIZE_STATUS
      DumpReg DISPC_GFX_ROW_INC
      DumpReg DISPC_GFX_PIXEL_INC
      DumpReg DISPC_GFX_WINDOW_SKIP
      DumpReg DISPC_GFX_TABLE_BA
      DumpReg DISPC_VID1+DISPC_VIDn_BA0
      DumpReg DISPC_VID1+DISPC_VIDn_BA1
      DumpReg DISPC_VID1+DISPC_VIDn_POSITION
      DumpReg DISPC_VID1+DISPC_VIDn_SIZE
      DumpReg DISPC_VID1+DISPC_VIDn_ATTRIBUTES
      DumpReg DISPC_VID1+DISPC_VIDn_FIFO_THRESHOLD
      DumpReg DISPC_VID1+DISPC_VIDn_FIFO_SIZE_STATUS
      DumpReg DISPC_VID1+DISPC_VIDn_ROW_INC
      DumpReg DISPC_VID1+DISPC_VIDn_PIXEL_INC
      DumpReg DISPC_VID1+DISPC_VIDn_FIR
      DumpReg DISPC_VID1+DISPC_VIDn_PICTURE_SIZE
      DumpReg DISPC_VID1+DISPC_VIDn_ACCU0
      DumpReg DISPC_VID1+DISPC_VIDn_ACCU1
      DumpReg DISPC_VID1+DISPC_VIDn_FIR_COEF_H0
      DumpReg DISPC_VID1+DISPC_VIDn_FIR_COEF_HV0
      DumpReg DISPC_VID1+DISPC_VIDn_FIR_COEF_H1
      DumpReg DISPC_VID1+DISPC_VIDn_FIR_COEF_HV1
      DumpReg DISPC_VID1+DISPC_VIDn_FIR_COEF_H2
      DumpReg DISPC_VID1+DISPC_VIDn_FIR_COEF_HV2
      DumpReg DISPC_VID1+DISPC_VIDn_FIR_COEF_H3
      DumpReg DISPC_VID1+DISPC_VIDn_FIR_COEF_HV3
      DumpReg DISPC_VID1+DISPC_VIDn_CONV_COEF0
      DumpReg DISPC_VID1+DISPC_VIDn_CONV_COEF1
      DumpReg DISPC_VID1+DISPC_VIDn_CONV_COEF2
      DumpReg DISPC_VID1+DISPC_VIDn_CONV_COEF3
      DumpReg DISPC_VID1+DISPC_VIDn_CONV_COEF4
      B %FT10
      LTORG
10
      DumpReg DISPC_VID2+DISPC_VIDn_BA0
      DumpReg DISPC_VID2+DISPC_VIDn_BA1
      DumpReg DISPC_VID2+DISPC_VIDn_POSITION
      DumpReg DISPC_VID2+DISPC_VIDn_SIZE
      DumpReg DISPC_VID2+DISPC_VIDn_ATTRIBUTES
      DumpReg DISPC_VID2+DISPC_VIDn_FIFO_THRESHOLD
      DumpReg DISPC_VID2+DISPC_VIDn_FIFO_SIZE_STATUS
      DumpReg DISPC_VID2+DISPC_VIDn_ROW_INC
      DumpReg DISPC_VID2+DISPC_VIDn_PIXEL_INC
      DumpReg DISPC_VID2+DISPC_VIDn_FIR
      DumpReg DISPC_VID2+DISPC_VIDn_PICTURE_SIZE
      DumpReg DISPC_VID2+DISPC_VIDn_ACCU0
      DumpReg DISPC_VID2+DISPC_VIDn_ACCU1
      DumpReg DISPC_VID2+DISPC_VIDn_FIR_COEF_H0
      DumpReg DISPC_VID2+DISPC_VIDn_FIR_COEF_HV0
      DumpReg DISPC_VID2+DISPC_VIDn_FIR_COEF_H1
      DumpReg DISPC_VID2+DISPC_VIDn_FIR_COEF_HV1
      DumpReg DISPC_VID2+DISPC_VIDn_FIR_COEF_H2
      DumpReg DISPC_VID2+DISPC_VIDn_FIR_COEF_HV2
      DumpReg DISPC_VID2+DISPC_VIDn_FIR_COEF_H3
      DumpReg DISPC_VID2+DISPC_VIDn_FIR_COEF_HV3
      DumpReg DISPC_VID2+DISPC_VIDn_CONV_COEF0
      DumpReg DISPC_VID2+DISPC_VIDn_CONV_COEF1
      DumpReg DISPC_VID2+DISPC_VIDn_CONV_COEF2
      DumpReg DISPC_VID2+DISPC_VIDn_CONV_COEF3
      DumpReg DISPC_VID2+DISPC_VIDn_CONV_COEF4
      DumpReg DISPC_DATA_CYCLE0
      DumpReg DISPC_VID0_FIR_COEF_V
      DumpReg DISPC_VID1_FIR_COEF_V
      DumpReg DISPC_CPR_COEF_R
      DumpReg DISPC_CPR_COEF_G
      DumpReg DISPC_CPR_COEF_B
      DumpReg DISPC_GFX_PRELOAD
      DumpReg DISPC_VID0_PRELOAD
      DumpReg DISPC_VID1_PRELOAD
      Pull    "a1-a3,pc"

      LTORG
 ]

Video_init
        Push    "v1,lr"
        ; omap_dss_probe()
        ; - dss_clk_enable_all_no_ctx()
        LDR     a1, L4_ClockMan_Log
        LDR     a2, [a1, #CM_ICLKEN_DSS]
        ORR     a2, a2, #1 ; EN_DSS (correct to enable iclk before fclk?)
        STR     a2, [a1, #CM_ICLKEN_DSS]
        LDR     a2, [a1, #CM_FCLKEN_DSS]
        ORR     a2, a2, #7 ; EN_DSS1, EN_DSS2, EN_TV (required for reset)
        STR     a2, [a1, #CM_FCLKEN_DSS]
 [ ExtraVideoDebug
        DebugTX "Original regs"
        BL DumpDispcRegs
 ]
        ; - dss_init()
        LDR     v1, L4_Display_Log
        LDR     a2, [v1, #DISPC_CONTROL]
        TST     a2, #1 ; LCD already enabled?
        BEQ     %FT10 ; Skip init
        ; Apparently, bad things happen if you reset the DSS immediately after enabling the clocks
        DebugTX "Performing DSS reset"
        MOV     a1, #50*1024 ; 50msec ish
        BL      HAL_CounterDelay ; Wait (timer should already running)
        ; -- omap_dss_reset()
        LDR     a1, [v1, #DSS_SYSCONFIG]
        ORR     a1, a1, #2 ; SOFTRESET
        STR     a1, [v1, #DSS_SYSCONFIG]
        ; Wait for completion
5
        LDR     a1, [v1, #DSS_SYSSTATUS]
        TST     a1, #1
        BEQ     %BT5
10
        DebugTX "DSS init"
        ; Enable autoidle mode
        LDR     a1, [v1, #DSS_SYSCONFIG]
        ORR     a1, a1, #1
        STR     a1, [v1, #DSS_SYSCONFIG]
        ; Select DSS1_ALWON_FCLK as DISPC fclk
        LDR     a1, [v1, #DSS_CONTROL]
        BIC     a1, a1, #1
        STR     a1, [v1, #DSS_CONTROL]
        ; - dpi_init()
        ;   (nothing)
        ; - dispc_init()
        ; -- _omap_dispc_initial_config()
        LDR     a1, =&2015 ; AUTOIDLE, ENWAKEUP, SIDLEMODE=2, MIDLEMODE=2 (smart idle modes)
        STR     a1, [v1, #DISPC_SYSCONFIG]
        LDR     a1, [v1, #DISPC_CONFIG]
        ORR     a1, a1, #1:SHL:9 ; Gate functional clocks
        STR     a1, [v1, #DISPC_CONFIG]
        ; - dss_init_displays()
        ; -- dpi_init_display()
        ;    (nothing)
        ; - dss_init_overlay_managers()
        ;   (nothing)
        ; - dss_init_overlays()
        ;   (nothing)
        ; Other old bits:
        ; Configure GPIO pins so we can turn the DVI framer on/off
        LDR     a3, TFP410_Ptr
        LDR     a1, [a3, #GPIO_OE]
        MOV     a2, #TFP410_Pin
        BIC     a1, a1, a2
        STR     a1, [a3, #GPIO_OE] ; Set to output
        STR     a2, [a3, #GPIO_CLEARDATAOUT] ; Turn it off
        ; Configure FIFO thresholds. these don't match u-boot, but they do match some linux code, so I'll stick with this for now.
        LDR     a3, [v1, #DISPC_GFX_FIFO_SIZE_STATUS]
        MOV     a2, a3, LSR #2 ; low threshold = 1/4 size
        SUB     a3, a3, a2 ; High threshold = 3/4 size
        ORR     a3, a2, a3, LSL #16
        STR     a3, [v1, #DISPC_GFX_FIFO_THRESHOLD]
        ADD     a4, v1, #DISPC_VID1:AND:&FF
        ADD     a4, a4, #DISPC_VID1:AND:&FF00
        LDR     a3, [a4, #DISPC_VIDn_FIFO_SIZE_STATUS]
        MOV     a2, a3, LSR #2 ; low threshold = 1/4 size
        SUB     a3, a3, a2 ; High threshold = 3/4 size
        ORR     a3, a2, a3, LSL #16
        STR     a3, [a4, #DISPC_VIDn_FIFO_THRESHOLD]
        ADD     a4, v1, #DISPC_VID2:AND:&FF
        ADD     a4, a4, #DISPC_VID2:AND:&FF00
        LDR     a3, [a4, #DISPC_VIDn_FIFO_SIZE_STATUS]
        MOV     a2, a3, LSR #2 ; low threshold = 1/4 size
        SUB     a3, a3, a2 ; High threshold = 3/4 size
        ORR     a3, a2, a3, LSL #16
        STR     a3, [a4, #DISPC_VIDn_FIFO_THRESHOLD]
        ; todo - set DMA burst sizes
        ; Allocate palette space
        LDR     a1, NCNBAllocNext
        STR     a1, video_palette
        ADD     a1, a1, #4*256
        ; Allocate hardware cursor space
        STR     a1, cursor_image
        ADD     a1, a1, #HW_CURSOR_WIDTH*HW_CURSOR_HEIGHT*4
        STR     a1, NCNBAllocNext
        MOV     a1, #0 ; Pixel rate isn't set yet
        STR     a1, pixel_rate
        STR     a1, video_gobits
        ; Get the OMAP revision so we can find out how large the timing registers are
;        LDR     a1, L4_Wakeup_Log
;        ADD     a1, a1, #(L4_CONTROL_IDCODE-L4_Wakeup) :AND: &FF00
;        LDR     a1, [a1, #(L4_CONTROL_IDCODE-L4_Wakeup) :AND: &00FF]
;        DebugReg a1
;        MOV     a1, a1, LSR #28 ; Get revision number
;        CMP     a1, #3
;        MOVGT   a1, #1:SHL:12
;        MOVLE   a1, #1:SHL:8
;        STR     a1, video_maxporch
;        MOVGT   a1, #1:SHL:8
;        MOVLE   a1, #1:SHL:6
;        STR     a1, video_maxsync
        ; Limit to the safe values for now until we can be certain what revision the timing registers have been fixed in
        MOV     a1, #1:SHL:8
        STR     a1, video_maxporch
        MOV     a1, #1:SHL:6
        STR     a1, video_maxsync
        Pull    "v1,pc"

; -----------------------------------------------------------------------------------

HAL_VideoFlybackDevice
        MOV     a1, #VIDEO_IRQ ; DSS IRQ
        MOV     pc, lr

; -------------------------------------------------------------------------

;
;  void HAL_Video_SetMode(const void *VIDCList3)
;
;  program DSS registers from VIDCList3 specification
;
;  in: VIDClist -> video mode list (in VIDCList type 3 format)
;

HAL_Video_SetMode ROUT
        Push    "v1-v4,lr"
 [ VideoDebug
        DebugTX "HAL_Video_SetMode"
 ]
        ; Most of these registers are programmed with (VIDC value)-1
        LDR     v1, L4_Display_Log
        ; Start off by programming pixel clock, so we can exit cleanly if we've been asked to change to a mode with a bad clock rate
        ; Program CM_CLKSEL2_PLL
        LDR     a4, [a1, #VIDCList3_PixelRate] ; Desired pixel rate, kHz
 [ VideoDebug
        DebugTX "Pixel rate:"
        DebugReg a4
        LDR a2, sys_clk
        DebugTX "sys_clk"
        DebugReg a2
 ]
        LDR     a2, =MAX_PIXEL_RATE
        CMP     a4, a2
        BLS     %FT10
        DebugTX "HAL_Video_SetMode: Pixel rate too high (>65MHz)"
        Pull    "v1-v4,pc"
10
        ; Check if we're already programmed to this pixel rate
        LDR     a2, pixel_rate
        CMP     a2, a4
        BEQ     %FT20
        ; Else we need to reprogram the DSI PLL
        ; Use the sequence indicated in the OMAP TRM (spruf98b, fig 15-135), tweaked a bit to avoid writing values until we know the desired pixel rate is possible
        ; We also follow figures 15-142 and 15-143, to ensure video output is disabled & re-enabled in the correct manner before we go tweaking the clock frequencies (although those two figures reference the SDI PLL, not the DSI PLL) although this is wrong now that we're using RFBI? just need to follow dpi_set_mode()
        ; todo - investigate what the role of the sys_clk divider is (i.e. whether it really does need to be on for some frequencies)

        ; Calculate REGM, REGN
        MOV     a2, #1000
        Push    "a1"
        MUL     a1,a2,a4 ; Pixel rate, Hz
        BL      calculate_DSS_clock_divider
        CMP     a1, #2048
        CMPLO   a2, #128
        BLO     %FT10
        DebugTX "HAL_Video_SetMode: Bad DSS clock divider"
        DebugReg a4
        DebugReg a1
        DebugReg a2
        Pull    "a1,v1-v4,pc"
10
        ; Calculate FREQSEL
        MOV     v2, a1 ; REGM
        MOV     v3, a2 ; REGN
        ; First, calculate Fint
        ADD     a1, a2, #1
        LDR     a2, sys_clk
        BL      |__rt_udiv|
        BL      PRCM_GetFreqSel
        SUB     a2, a1, #3
        CMP     a2, #4
        BLS     %FT10
        DebugTX "HAL_Video_SetMode: Bad Fint"
        DebugReg a4
        DebugReg v2
        DebugReg v3
        Pull    "a1,v1-v4,pc"
10
        MOV     v4, a1 ; FREQSEL

        ; Disable Vsync IRQ to avoid pointer corruption issues
        LDR     a1, [v1, #DISPC_IRQENABLE]
        MOV     a1, #2
        STR     a1, [v1, #DISPC_IRQENABLE]

        ; Data synchronisation barrier
        MOV     a1, #0
        MCR     ARM_config_cp, 0, a1, C7, C10, 4

        ; New pixel rate is good, shutdown DISPC while we update the DSI PLL
        LDR     a1, pixel_rate
        CMP     a1, #0
        BEQ     %FT30 ; Skip DISPC/PLL shutdown sequence
 [ VideoDebug
        DebugTX "Performing DISPC shutdown"
 ]
        ; Should we turn off the DVI framer here?

        ; Wait for any pending GOLCD/GODIGITAL to complete
10
        LDR     a1, [v1, #DISPC_CONTROL]
        TST     a1, #3:SHL:5
        BNE     %BT10

        ; Now we turn off the DISPC outputs
        ; Just gating the functional clocks should work?
        LDR     a1, [v1, #DISPC_CONFIG]
        ORR     a1, a1, #1:SHL:9
        STR     a1, [v1, #DISPC_CONFIG]
30
        ; DISPC is turned off, now we can program the new DSI PLL value
        ; v2 = REGM
        ; v3 = REGN
        ; v4 = FREQSEL
        ; dpi_display_enable()
        ; - dpi_basic_init()
        ; -- dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS)
        LDR     a1, [v1, #DISPC_CONTROL]
        BIC     a1, a1, #1:SHL:11 ; STALLMODE=0
        ORR     a1, a1, #3:SHL:15 ; GPOUT0=1,GPOUT1=1
        ; -- dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT)
        ORR     a1, a1, #1:SHL:3
        ; -- dispc_set_tft_data_lines(DSI_LINES)
        ORR     a1, a1, #3:SHL:8
        STR     a1, [v1, #DISPC_CONTROL]
        ; - dsi_pll_init(0,1)
        ; -- dss_dsi_power_up()
        ;    (nothing?)
        ; -- dispc_pck_free_enable(1)
        LDR     a1, [v1, #DISPC_CONTROL]
        ORR     a1, a1, #1:SHL:27 ; PCKFREEENABLE - PLL doesn't come out of reset without this, according to some linux code
        STR     a1, [v1, #DISPC_CONTROL]
 [ :LNOT: QEMU
        ; -- wait_for_bit_up(DSI_PLL_STATUS,0)
  [ VideoDebug
        DebugTX "Wait for DSI_PLLCTRL_RESET_DONE"
  ]
10
        LDR     a1, [v1, #DSI_PLL_STATUS]
        TST     a1, #1 ; DSI_PLLCTRL_RESET_DONE? (surely this assumes that it's just been reset?)
        BEQ     %BT10
        ; -- dispc_pck_free_enable(0)
        LDR     a1, [v1, #DISPC_CONTROL]
        BIC     a1, a1, #1:SHL:27 ; Disable PCKFREEENABLE because it causes problems with video planes? (more linux stuff)
        STR     a1, [v1, #DISPC_CONTROL]
        ; -- dsi_pll_power(DSI_PLL_POWER_ON_DIV)
        LDR     a1, [v1, #DSI_CLK_CTRL]
        ORR     a1, a1, #3:SHL:30 ; PLL on, HSDIVIDER on, but no output to DSI complex I/O
        STR     a1, [v1, #DSI_CLK_CTRL]
  [ VideoDebug
        DebugTX "Wait for DSI_PLL_POWER_ON_HSCLK"
  ]
10
        LDR     a1, [v1, #DSI_CLK_CTRL]
        AND     a1, a1, #3:SHL:28
        CMP     a1, #3:SHL:28
        BNE     %BT10 ; Wait for requested state to be reached

        ; - dpi_set_mode()
        ; -- dispc_set_pol_freq() - ok to delay until later?
        ; -- dpi_set_dsi_clk()
  [ VideoDebug
        DebugTX "Programming DSI PLL"
  ]
        ; --- dsi_pll_program() - what we do here:
        LDR     a1, [v1, #DSI_PLL_CONTROL]
        BIC     a1, a1, #1 ; DSI_PLL_AUTOMODE = manual (apparently)
        STR     a1, [v1, #DSI_PLL_CONTROL]
        ; Calculate DSI_PLL_CONFIGURATION1
        LDR     a1, [v1, #DSI_PLL_CONFIGURATION1]
        BIC     a1, a1, #&FE
        ORR     a1, a1, v3, LSL #1 ; REGN
        BIC     a1, a1, #&70000
        BIC     a1, a1, #&0FF00
        ORR     a1, a1, v2, LSL #8 ; REGM
        ORR     a1, a1, #1 ; STOPMODE selected (why?)
        ; REGM3, REGM4
        LDR     a2, =&7F80000
        BIC     a1, a1, a2 ; Clear old values
        ; We leave REGM4 as 0, since we don't use it
        ; But REGM3+1 = frequency on data lane/(pixel rate*2)
        ; REMG3+1 = (2*pixel rate*bpp)/(pixel rate*2*lanes) = DSI_BPP/DSI_LANES
        ASSERT  (DSI_BPP :MOD: DSI_LANES)=0
        ORR     a1, a1, #((DSI_BPP/DSI_LANES)-1):SHL:19
        ORR     a1, a1, #7:SHL:23 ; Set REGM4?
        STR     a1, [v1, #DSI_PLL_CONFIGURATION1]
        ; Calculate DSI_PLL_CONFIGURATION2
        LDR     a2, [v1, #DSI_PLL_CONFIGURATION2]
        BIC     a2, a2, #3:SHL:11 ; Select sys_clk, clear HIGHFREQ
        BIC     a2, a2, #&1E
        ORR     a2, a2, v4, LSL #1 ; FREQSEL (angstrom always uses 7?)
        ORR     a2, a2, #1:SHL:13 ; Enable reference clock
        BIC     a2, a2, #1:SHL:14 ; Disable DSIPHY clock
        ORR     a2, a2, #1:SHL:20 ; HSDIVIDER in bypass mode
        STR     a2, [v1, #DSI_PLL_CONFIGURATION2]
        ; Set DSI_PLL_GO
        MOV     a1, #1
        STR     a1, [v1, #DSI_PLL_GO]
        ; Wait for completion
  [ VideoDebug
        DebugTX "Wait for DSI_PLL_GO"
  ]
10
        LDR     a1, [v1, #DSI_PLL_GO]
        TST     a1, #1 ; GO bit still set?
        BNE     %BT10
  [ VideoDebug
        DebugTX "Wait for DSI PLL lock"
  ]
        MOV     a3, #2*1024*1024
10
        LDR     a1, [v1, #DSI_PLL_STATUS]
        TST     a1, #2 ; DSI PLL locked?
;        BEQ     %BT10
        BNE     %FT5
        SUBS    a3, a3, #1
        BNE     %BT10
  [ VideoDebug
        DebugTX "DSI PLL lock timeout"
        LDR     a1, [v1, #DSI_PLL_CONTROL] ; 00000000
        DebugReg a1
        LDR     a1, [v1, #DSI_PLL_STATUS] ; 00000041 - DSI_PLL_BYPASS, RESETDONE
        DebugReg a1
        LDR     a1, [v1, #DSI_PLL_GO] ; 00000000
        DebugReg a1
        LDR     a1, [v1, #DSI_PLL_CONFIGURATION1] ; 00387c0f - STOPMODE selected (bad?), REGN = 7, REGM = 7c, M3REG = 7, M4REG = 0
        DebugReg a1
        LDR     a1, [v1, #DSI_PLL_CONFIGURATION2] ; 0010200c - FREQSEL 1.5-1.75, DSI_PLL_REFEN, DSI_HSDIVBYPASS
        DebugReg a1
        LDR     a1, [v1, #DSI_CLK_CTRL] ; 50000001 - LP_CLK_DIVISOR 1, power=HSDIVIDER off, PLL on
        DebugReg a1
  ]
;6
;        B       %BT6
5
        ; Modify CONFIGURATION2 again to do stuff
        ; Some of this can be skipped, matches reset values?
        BIC     a2, a2, #1 ; DSI_PLL_IDLE = 0
        BIC     a2, a2, #&3F:SHL:5 ; DSI_PLL_PLLLPMODE, LOWCURRSTBY, TIGHTPHASELOCK, DRIFTGUARDEN, LOCKSEL = 0
        ORR     a2, a2, #1:SHL:16 ; DSS_CLOCK_EN
        BIC     a2, a2, #1:SHL:17 ; DSS_CLOCK_PWDN
        BIC     a2, a2, #1:SHL:20 ; HSDIVIDER in normal mode
        STR     a2, [v1, #DSI_PLL_CONFIGURATION2]
 ]
        ; Make sure DISPC uses DSI1_PLL_FCLK
        LDR     a1, [v1, #DSS_CONTROL]
        ORR     a1, a1, #1
        STR     a1, [v1, #DSS_CONTROL]

        ; Enable functional clock again
        LDR     a1, [v1, #DISPC_CONFIG]
        BIC     a1, a1, #1:SHL:9
        ; Also make sure colour phase rotation is enabled
        ORR     a1, a1, #1:SHL:15
        ; Also make sure transparency colour key is enabled for the mouse cursor
        ORR     a1, a1, #&F:SHL:10 ; Video source transparency colour key for both LCD & digital output
        STR     a1, [v1, #DISPC_CONFIG]

        Pull    "a1"
        ; Remember the new pixel rate!
        LDR     a3, [a1, #VIDCList3_PixelRate]
        STR     a3, pixel_rate
20
 [ VideoDebug
        DebugTX "Programming VIDC timings"
 ]
        ; Now we can program the rest of the VIDC list
        ; Program DISPC_TIMING_H
        LDR     a2, [a1, #VIDCList3_HorizSyncWidth]
        LDR     a4, video_maxsync
        SUBS    a2, a2, #1
        MOVLT   a2, #0
        CMP     a2, a4
        SUBGE   a2, a4, #1 ; a4 is actually max value+1
        LDR     a3, [a1, #VIDCList3_HorizFrontPorch]
        LDR     a4, video_maxporch
        SUBS    a3, a3, #1
        MOVLT   a3, #0
        CMP     a3, a4
        SUBGE   a3, a4, #1
        ORR     a2, a2, a3, LSL #8
        LDR     a3, [a1, #VIDCList3_HorizBackPorch]
        SUBS    a3, a3, #1
        MOVLT   a3, #0
        CMP     a3, a4
        SUBGE   a3, a4, #1
        ORR     a2, a2, a3, LSL #20
        STR     a2, [v1, #DISPC_TIMING_H]
        ; Program DISPC_TIMING_V
        LDR     a2, [a1, #VIDCList3_VertiSyncWidth]
        LDR     a4, video_maxsync
        SUBS    a2, a2, #1
        MOVLT   a2, #0
        CMP     a2, a4
        SUBGE   a2, a4, #1
        LDR     a3, [a1, #VIDCList3_VertiFrontPorch]
        LDR     a4, video_maxporch
        CMP     a3, #0
        MOVLT   a3, #0
        CMP     a3, a4
        SUBGE   a3, a4, #1
        ORR     a2, a2, a3, LSL #8
        LDR     a3, [a1, #VIDCList3_VertiBackPorch]
        CMP     a3, #0
        MOVLT   a3, #0
        CMP     a3, a4
        SUBGE   a3, a4, #1
        ORR     a2, a2, a3, LSL #20
        STR     a2, [v1, #DISPC_TIMING_V]
        ; Program DISPC_POL_FREQ
        MOV     a2, #&28 ; AC bias pin frequency - from u-boot
        LDR     a3, [a1, #VIDCList3_SyncPol]
        TST     a3, #SyncPol_InvertHSync
        ORRNE   a2, a2, #&2000
        TST     a3, #SyncPol_InvertVSync
        ORRNE   a2, a2, #&1000
        STR     a2, [v1, #DISPC_POL_FREQ]
        ; DISPC_SIZE_DIG, DISPC_SIZE_LCD, DISPC_GFX_SIZE, DISPC_PICTURE_SIZE
        LDR     a2, [a1, #VIDCList3_HorizDisplaySize]
        CMP     a2, #2048
        MOVGT   a2, #2048
        CMP     a2, #0
        MOVLE   a2, #1
        SUB     a2, a2, #1
        LDR     a3, [a1, #VIDCList3_VertiDisplaySize]
        CMP     a3, #2048
        MOVGT   a3, #2048
        CMP     a3, #0
        MOVLE   a3, #1
        SUB     a3, a3, #1
        ORR     a2, a2, a3, LSL #16
        STR     a2, [v1, #DISPC_SIZE_DIG]
        STR     a2, [v1, #DISPC_SIZE_LCD]
        STR     a2, [v1, #DISPC_GFX_SIZE]
        ; DISPC_GFX_ATTRIBUTES
        LDR     a2, [a1, #VIDCList3_PixelDepth]
        ADR     a3, gfxenable_table
        LDR     a2, [a3, a2, LSL #2]
        STR     a2, [v1, #DISPC_GFX_ATTRIBUTES]
        ; DISPC_GFX_TABLE_BA
        Push    "a1"
        LDR     a1, video_palette
        BL      vtophys
        STR     a1, [v1, #DISPC_GFX_TABLE_BA]
        Pull    "a1"
        ; DISPC_CPR_COEF_*
        ; For now, swap R&B channels regardless of whether we're in a palettised mode or not
        MOV     a2,#256
        STR     a2, [v1, #DISPC_CPR_COEF_R]
        MOV     a2, a2, LSL #11
        STR     a2, [v1, #DISPC_CPR_COEF_G]
        MOV     a2, a2, LSL #11
        STR     a2, [v1, #DISPC_CPR_COEF_B]
        ; DISPC_GLOBAL_ALPHA
        LDR     a2, =&FF00FF
        STR     a2, [v1, #DISPC_GLOBAL_ALPHA]
        ; DISPC_CONTROL
        LDR     a2, =&1836b ; Same value as u-boot: GPOUT0, GPOUT1, 24-bit TFT data lines, GODIGITAL, GOLCD, active matrix display, DIGITALENABLE, LCDENABLE
        STR     a2, [v1, #DISPC_CONTROL]
 [ VideoDebug
        DebugTX "Video mode programmed, waiting for GOLCD/GODIGITAL to clear"
 ]
10
        LDR     a1, [v1, #DISPC_CONTROL]
        TST     a1, #3:SHL:5
        BNE     %BT10
        ; Re-enable VSync IRQ
        LDR     a1, [v1, #DISPC_IRQENABLE]
        MOV     a1, #2
        STR     a1, [v1, #DISPC_IRQENABLE]
        ; Now enable the DVI framer
        LDR     v2, TFP410_Ptr
        MOV     a1, #TFP410_Pin
        STR     a1, [v2, #GPIO_SETDATAOUT]
        ; Done?
 [ VideoDebug
        DebugTX "HAL_Video_SetMode complete"
 ]
;        MOV     a1, #1 ; NTSC 601 s-video
;        BL venc_setmode
 [ ExtraVideoDebug
        DebugTX "Final regs"
        BL DumpDispcRegs
 ]
        Pull    "v1-v4,pc"

gfxenable_table ; GFXENABLE values for different BPP values
            ; FEDCBA9876543210
        DCD 2_0100100010000001 ; 1BPP
        DCD 2_0100100010000011 ; 2BPP
        DCD 2_0100100010000101 ; 4BPP
        DCD 2_0100100010000111 ; 8BPP
        DCD 2_0100100010001101 ; 16BPP
        DCD 2_0100100010010001 ; 32BPP

; -------------------------------------------------------------------------

;
; void HAL_Video_WritePaletteEntry(uint type, uint pcolour, uint index)
;
; write palette entry to video controller
;
;  type     = 0 for normal palette entry
;             1 for border colour
;             2 for pointer colour
;          >= 3 reserved
;  pcolour  = palette entry colour in BBGGRRSS format (Blue,Green,Red,Supremacy)
;  index    = index of entry  (0..255 for normal, 0 for border, 0..3 for pointer)
;             note that RISC OS only uses 1..3 for pointer (0 is assumed to be transparent)
;
HAL_Video_WritePaletteEntry ROUT
        CMP     a1, #3
        MOVHS   pc, lr
        CMP     a1, #1
        BEQ     %FT10 ; Border colour
        BGT     %FT20 ; Pointer colour
        CMP     a3, #255
        MOVHI   pc, lr ; Out of range
        LDR     a4, video_palette
        MOV     a2, a2, ROR #8
        STR     a2, [a4, a3, LSL #2]
        MOV     pc, lr
10      ; Update border colour
        ; We don't really support this properly. for now just set the default background colour.
        MOV     a2, a2, ROR #8
        LDR     a1, L4_Display_Log
        STR     a2, [a1, #DISPC_DEFAULT_COLOR0]
        STR     a2, [a1, #DISPC_DEFAULT_COLOR1]
        ; GODIGITAL+GOLCD to ensure it's updated
        SetGoBits a1,a2,a3,"HAL_Video_WritePaletteEntry GOLCD timeout"
        MOV     pc, lr
20      ; Update cursor colour
        CMP     a3, #4
        MOVHS   pc, lr ; Out of range
        MOV     a2, a2, LSR #8 ; 00bbggrr
        ADR     a1, cursor_palette
        STR     a2, [a1, a3, LSL #2]
        STR     a1, cursor_update ; Store nonzero value to update flag
        MOV     pc, lr

; -------------------------------------------------------------------------

;
; void HAL_Video_WritePaletteEntries(uint type, const uint *pcolours, uint index, uint Nentries)
;
; write block of palette entries to video controller
;
;  type     = 0 for normal palette entry
;             1 for border colour
;             2 for pointer colour
;          >= 3 reserved
;  pcolours = pointer to block of palette entry colours in BBGGRRSS format (Blue,Green,Red,Supremacy)
;  index    = start index in palette (for first entry in block)
;             note that RISC OS only uses 1..3 for pointer (0 is assumed to be transparent)
;  Nentries = number of entries in block (must be >= 1)
;
HAL_Video_WritePaletteEntries ROUT
        CMP     a4, #0
        MOVEQ   pc, lr ; Nothing to store
        CMP     a1, #3
        MOVHS   pc, lr
        CMP     a1, #1
        BEQ     %FT10 ; Border colour
        BGT     %FT20 ; Pointer colour
        CMP     a3, #255
        CMPLS   a4, #256
        MOVHI   pc, lr ; Out of range
        ADD     ip, a3, a4
        CMP     ip, #256
        MOVHI   pc, lr ; Out of range
        LDR     a1, video_palette
        ADD     a1, a1, a3, LSL #2
5
        LDR     a3, [a2], #4
        MOV     a3, a3, ROR #8
        STR     a3, [a1], #4
        SUBS    a4, a4, #1
        BGT     %BT5
        MOV     pc, lr
10      ; Border update
        ; We don't really support this properly. for now just set the default background colour.
        CMP     a4, #1
        MOVNE   pc, lr
        LDR     a2, [a2]
        MOV     a2, a2, ROR #8
        LDR     a1, L4_Display_Log
        STR     a2, [a1, #DISPC_DEFAULT_COLOR0]
        STR     a2, [a1, #DISPC_DEFAULT_COLOR1]
        ; GODIGITAL+GOLCD to ensure it's updated
        SetGoBits a1,a2,a3,"HAL_Video_WritePaletteEntries GOLCD timeout"
        MOV     pc, lr
20      ; Cursor update
        CMP     a3, #3
        CMPLS   a4, #4
        MOVHI   pc, lr ; Out of range
        ADD     ip, a3, a4
        CMP     ip, #4
        MOVHI   pc, lr ; Out of range
        ADR     a1, cursor_palette
        ADD     a1, a1, a3, LSL #2
5
        LDR     ip, [a2], #4
        MOV     ip, ip, LSR #8 ; 00bbggrr
        ADD     a3, a3, #1
        STR     ip, [a1], #4
        SUBS    a4, a4, #1
        BGT     %BT5
        STR     a1, cursor_update ; Store nonzero value to update flag
        MOV     pc, lr

; -------------------------------------------------------------------------

;
; uint HAL_Video_ReadPaletteEntry(uint type, uint pcolour, uint index)
;
; return the effective palette entry after taking into account any hardware
; restrictions in the video controller, assuming it was programmed with pcolour
;
;  type     = 0 for normal palette entry
;             1 for border colour
;             2 for pointer colour
;          >= 3 reserved
;  pcolour  = palette entry colour in BBGGRRSS format (Blue,Green,Red,Supremacy)
;  index    = index of entry  (0..255 for normal, 0 for border, 0..3 for pointer)
;             note that RISC OS only uses 1..3 for pointer (0 is assumed to be transparent)
;  returns  : effective BBGGRRSS
;
;  mjs: depending on h/w capabilities, specific HALs may have to
;       remember current settings (eg. bits per pixel), keep soft copy
;       of entries or whatever, in their workspace. Because the HAL API
;       supplies a pcolour, the need to keep a full palette soft copy
;       in the HAL is minimised

HAL_Video_ReadPaletteEntry ROUT
        MOV     a1, a2
        MOV     pc, lr

; -------------------------------------------------------------------------

; void HAL_Video_SetInterlace(uint interlace)
;
;  interlace = 0/1 for interlace off/on

HAL_Video_SetInterlace ROUT
        MOV     pc, lr

; -------------------------------------------------------------------------

; void HAL_Video_SetBlank(uint blank, uint DPMS)
;
; blank = 0/1 for unblank/blank
; DMPS  = 0..3 as specified by monitor DPMSState (from mode file)
;         0 for no DPMS power saving

; HAL is expected to attempt to turn syncs off according to DPMS, and
; to turn video DMA off for blank (and therefore on for unblank) if possible.
; HAL is not expected to do anything else, eg. blank all palette entries.
; Such things are the responsibility of the OS, and also this call is expected
; to be fast. May be called with interrupts off.

HAL_Video_SetBlank ROUT
        MOV     pc, lr

; -------------------------------------------------------------------------

; void HAL_Video_SetPowerSave(uint powersave)
;
; powersave = 0/1 for power save off/on

HAL_Video_SetPowerSave ROUT
        MOV     pc, lr

; -------------------------------------------------------------------------

; void HAL_Video_UpdatePointer(uint flags, int x, int y, const shape_t *shape)
;
; Update the displayed position of the current pointer shape (or turn
; shape off)
;
; HAL code may need to take note of shape updated flag, and make its
; own new copies if true. This is to handle cases like dual scan LCD
; pointer, which typically needs two or more shapes buffers for the
; hardware. This work should _only_ be done when the updated flag
; is true, or possibly because provoked by clipping requirements.
; A simple HAL, using the kernel shape buffer directly, may be able to
; ignore the updated flag.
;
; flags:
;   bit 0  = pointer display enable (0=off, 1=on)
;   bit 1  = pointer shape update (0=no change, 1=updated)
;   bits 2..31 reserved (0)
; xpos = x position of top left of pointer (xpos = 0 for left of display)
; ypos = y position of top left of pointer (ypos = 0 for top of display)
; shape points to shape_t descriptor block:
;   typedef struct shape_t
;   {
;     uint8   width;      /* unpadded width in bytes (see notes) */
;     uint8   height;     /* in pixels */
;     uint8   padding[2]; /* 2 bytes of padding for field alignment */
;     void   *buffLA;     /* logical address of buffer holding pixel data */
;     void   *buffPA;     /* corresponding physical address of buffer */
;   }
;
; Notes:
; 1) if flags bit 0 is 0 (pointer off), x, y, shape are undefined
; 2) the shape data from RISC OS is always padded with transparent pixels
;    on the rhs, to a width of 32 pixels (8 bytes)
; 3) pointer clipping is the responsibility of the HAL (eg. may be able to
;    allow display of pointer in border region on some h/w)
; 4) buffer for pixel data is aligned to a multiple of 256 bytes or better
;
; This call is made by the OS at a time to allow smoothly displayed changes
; (on a VSync)

HAL_Video_UpdatePointer ROUT
        Push     "v1-v5,lr"
        TST     a1, #1
        BEQ     %FT10 ; Cursor off
        ; Update image?
        TST      a1, #2
        LDREQ    a1, cursor_update ; Any palette changes that need implementing?
        CMPEQ    a1, #0
        BEQ      %FT20
        ; The current pointer implementation assumes that palette index 0 is
        ; the only transparent entry (which is currently true for RISC OS).
        ; And since we're using the DSS transparency colour key feature, we
        ; need to make sure the (24bit RGB) value for entry 0 is unique.
        ; So this piece of code, just before the image update, ensures that
        ; the palette entry is unique
        ADR      a1, cursor_palette
        LDMIA    a1, {v1-v4} ; Grab the 4 entries. We assume that the top bit is already clear (the palette set functions should have done that for us)
5
        CMP      v1, v2
        CMPNE    v1, v3
        CMPNE    v1, v4
        ADDEQ    v1, v1, #1
        BICEQ    v1, v1, #&FF000000 ; Avoid overflowing into top byte
        BEQ      %BT5 ; Loop until we find a uinque colour
        STR      v1, [a1] ; Store it back
        ; Here we also update the colour key registers with the right value
        LDR      v2, L4_Display_Log
        STR      v1, [v2, #DISPC_TRANS_COLOR0]
        STR      v1, [v2, #DISPC_TRANS_COLOR1]
        ; Now we can update the image
        LDRB     v1, [a4] ; width
        LDRB     v2, [a4,#1] ; height
        MOV      v1, v1, LSL #2 ; Byte width -> pixel width
        ; Clamp size to sensible values
        CMP      v1, #0
        CMPNE    v2, #0
        BEQ      %FT20 ; No width/height... ignore
        CMP      v2, #HW_CURSOR_HEIGHT
        MOVGT    v2, #HW_CURSOR_HEIGHT
        ; No upper bound on width; we just skip the extra pixels when copying to our buffer
        LDR      lr, cursor_image
        ; Good - now copy the data over
        ASSERT   HW_CURSOR_WIDTH = 32 ; RISC OS pads each row to be 32 pixels wide, make sure our buffer is the same width
        MOV      v4, #0 ; Current y
        LDR      a4, [a4,#4] ; Get source buffer
5
        MOV      v1, #16
        CMP      v4,v2 ; If the cursor is smaller than our buffer, don't load. No need to set v3 & v5 to 0 either; they will be 0 from the previous row
        LDMLTIA  a4!, {v3,v5} ; Get all 32 pixels for this row
6
        AND      ip, v3, #&3
        LDR      ip, [a1, ip, LSL #2]
        MOV      v3, v3, LSR #2
        SUBS     v1, v1, #1
        STR      ip, [lr], #4
        BGT      %BT6
        MOV      v1, #16
7
        AND      ip, v5, #&3
        LDR      ip, [a1, ip, LSL #2]
        MOV      v5, v5, LSR #2
        SUBS     v1, v1, #1
        STR      ip, [lr], #4
        BGT      %BT7
        ADD      v4,v4,#1
        CMP      v4,#HW_CURSOR_HEIGHT
        BLT      %BT5
        ; Image updated!
        STR      v3, cursor_update ; Clear update flag (v3 will be 0 at this point)
20
        ; Update position
        ; Even though the source image is likely to be smaller than our buffer, we always treat it as being the full buffer size
        LDR     a1, cursor_image
        MOV     v1, a2
        MOV     v2, a3
        BL      vtophys ; Get physical addr for programming VID2 base address
        MOV     v3, #HW_CURSOR_WIDTH
        MOV     v4, #HW_CURSOR_HEIGHT
        CMP     v1, #0
        SUBLT   a1, a1, v1, LSL #2 ; Skip pixels that are off the left of the screen
        ADDLT   v3, v3, v1
        MOVLT   v1, #0
        CMP     v2, #0
        SUBLT   a1, a1, v2, LSL #HW_CURSOR_WIDTH_POW2+2 ; Skip rows that are off the top of the screen
        ADDLT   v4, v4, v2
        MOVLT   v2, #0
        ; This may just be an issue with how the graphics windows are set up, but it looks like we need to crop the pixels that are off the right/bottom of the screen
        LDR     a3, L4_Display_Log
        LDR     a4, [a3, #DISPC_SIZE_LCD] ; Use SIZE_LCD, since we program VID2 for LCD output? (Not that it matters, since the sizes are identical ATM)
        MOV     v5, a4, LSR #16 ; Height-1
        BIC     a4, a4, v5, LSL #16 ; Width-1
        ADD     v5, v5, #1
        ADD     a4, a4, #1
        SUB     a4, a4, v1 ; a4 = max width
        CMP     a4, v3
        MOVLT   v3, a4
        SUB     v5, v5, v2 ; v5 = max height
        CMP     v5, v4
        MOVLT   v4, v5
        CMP     v1, #2048
        CMPLS   v2, #2048
        BHI     %FT10 ; Outside programmable video window position
        CMP     v3, #0
        CMPGT   v4, #0
        BLE     %FT10 ; Cropped image has no or negative size
        ; Else program attributes
        ADD     a4, a3, #DISPC_VID2:AND:&FF
        ADD     a4, a4, #DISPC_VID2:AND:&FF00
        LDR     a2, [a4, #DISPC_VIDn_BA0]
        CMP     a1, a2 ; Update required?
        STR     a1, [a4, #DISPC_VIDn_BA0]
        LDR     a2, [a4, #DISPC_VIDn_BA1]
        CMPEQ   a1, a2
        STR     a1, [a4, #DISPC_VIDn_BA1]
        ORR     a1, v1, v2, LSL #16
        LDR     a2, [a4, #DISPC_VIDn_POSITION]
        CMPEQ   a1, a2
        STR     a1, [a4, #DISPC_VIDn_POSITION]
        MOV     a1, v3, LSL #2 ; Width in bytes
        RSB     a1, a1, #HW_CURSOR_WIDTH*4 ; ROW_INC in bytes
        ADD     a1, a1, #1 ; Add 1 to get correct ROW_INC value
        LDR     a2, [a4, #DISPC_VIDn_ROW_INC]
        CMPEQ   a1, a2
        STR     a1, [a4, #DISPC_VIDn_ROW_INC]
        SUB     v3, v3, #1
        SUB     v4, v4, #1
        ORR     a1, v3, v4, LSL #16
        LDR     a2, [a4, #DISPC_VIDn_SIZE]
        CMPEQ   a1, a2
        STR     a1, [a4, #DISPC_VIDn_SIZE]
        LDR     a2, [a4, #DISPC_VIDn_PICTURE_SIZE]
        CMPEQ   a1, a2
        STR     a1, [a4, #DISPC_VIDn_PICTURE_SIZE] ; Required?
        LDR     a1, =2_0100010000000000000011001 ; high priority pipeline, use FIFO threshold, LCD output, 4x32bit DMA burst, ARGB 32, enabled
        LDR     a2, [a4, #DISPC_VIDn_ATTRIBUTES]
        CMPEQ   a1, a2
        STR     a1, [a4, #DISPC_VIDn_ATTRIBUTES]
        BEQ     %FT30 ; Will only be EQ if none of the registers changed
        B       %FT20 ; Else at least one reg changed and we need to set the go bits
10
        ; Cursor off
        LDR     a3, L4_Display_Log
        ADD     a1, a3, #DISPC_VID2:AND:&FF
        ADD     a1, a1, #DISPC_VID2:AND:&FF00
        MOV     a2, #0 ; Disabled
        LDR     a4, [a1, #DISPC_VIDn_ATTRIBUTES]
        CMP     a4, a2
        BEQ     %FT30 ; Skip update
        STR     a2, [a1, #DISPC_VIDn_ATTRIBUTES]
20
        ; Set GODIGITAL+GOLCD to ensure the register updates are applied
        SetGoBits a3,a1,a2,"HAL_Video_UpdatePointer GOLCD timeout"
30
        ; If pixel_rate is zero or DISPC_CONFIG bit 9 is set, we're in the middle of a mode change
        ; in which case setting the go bits is probably a bad idea
        LDR     a1, pixel_rate
        CMP     a1, #0
        Pull    "v1-v5,pc",EQ
        LDR     a1, [a3, #DISPC_CONFIG]
        TST     a1, #1:SHL:9
        Pull    "v1-v5,pc",NE
        ; Do we have a pending gobits from elsewhere?
        LDR     a1, video_gobits
        LDR     a2, [a3, #DISPC_CONTROL]
        BICS    a4, a1, a2 ; a4 = bits we can safely set
        ORRNE   a2, a2, a4
        STRNE   a2, [a3, #DISPC_CONTROL] ; Set the bits
        BICNE   a1, a1, a4 ; Clear them from our update mask
        STRNE   a1, video_gobits
        Pull     "v1-v5,pc"


; -------------------------------------------------------------------------

; void HAL_Video_SetDAG(uint DAG, uint paddr)
;
; set Video DMA address generator value to given physical address
;
; DAG   = 0 set start address of current video display
;         1 set start address of total video buffer
;         2 set end address (exclusive) of total video buffer
;         all other values reserved
; paddr = physical address for given DAG
;
; Notes:
; The OS has a video buffer which is >= total display size, and may be using
; bank switching (several display buffers) or hardware scroll within the
; total video buffer.
;
; DAG=1 will be start address of current total video buffer
; DAG=2 will be end address (exclusive) of current total video buffer
; DAG=0 will be start address in buffer for current display
;
; HALs should respond as follows:
; 1) If they have no hardware scroll support, only DAG=0 is significant,
;    and the end address of the current display is implied by the size
;    of the current mode. Calls with DAG=1,2 should be ignored.
; 2) If they support hardware scroll, DAG=0 again defines display start.
;    DAG=2 defines the last address (exclusive) that should be displayed
;    before wrapping back (if reached within display size), and DAG=1
;    defines the address to which accesses should wrap back.

HAL_Video_SetDAG ROUT
        CMP     a1, #0
        MOVNE   pc, lr
        Push    "lr"
        LDR     a3, L4_Display_Log
        STR     a2, [a3, #DISPC_GFX_BA0]
        STR     a2, [a3, #DISPC_GFX_BA1]
 [ VideoDebug
        DebugTX "SetDAG"
        DebugReg a2
 ]
        ; GODIGITAL+GOLCD to ensure it's updated
        SetGoBits a3,a1,a2,"HAL_Video_SetDAG GOLCD timeout"
        Pull    "pc"
        MOV     pc, lr


; -------------------------------------------------------------------------

;
; uint HAL_Video_Features(void)
;
; returns a flags word:
;    bit 0     h/w scroll is supported
;    bit 1     h/w pointer is supported
;    bit 2     interlace is supported with progressive framestore
;    other bits reserved (returned as 0)

HAL_Video_Features ROUT
        MOV     a1, #2_010   ; no hw scroll, h/w pointer, no interlace
        MOV     pc, lr

; -------------------------------------------------------------------------

;
; uint HAL_Video_PixelFormats(void)
;
;
; returns flags word:
;    bit 0     1 bpp is supported
;    bit 1     2 bpp is supported
;    bit 2     4 bpp is supported
;    bit 3     8 bpp is supported
;    bit 4    16 bpp is supported
;    bit 5    32 bpp is supported
;    other bits reserved (returned as 0)
;    bits 0-5 refer to support with standard RO pixel layout (little endian
;    packing for 1,2,4 bpp, 5-5-5 RGB for 16 bpp, etc.)
;    other formats may be introduced when/if RO supports them

HAL_Video_PixelFormats ROUT
        MOV     a1, #2_111111   ; 1,2,4,8,16,32 bpp
        MOV     pc, lr

; -------------------------------------------------------------------------

;
; uint HAL_Video_BufferAlignment(void)
;
; returns the required alignment for the framestore buffer, in bytes
; (expected to be a power of 2)

HAL_Video_BufferAlignment ROUT
        MOV     a1, #32          ; align to 32 bytes
        MOV     pc, lr

; -------------------------------------------------------------------------

;
; uint HAL_Video_OutputFormat(void)
;
; returns current video output format
;      0 = analogue (or 'normal', unspecified)
;      1 = 16-bit non-multiplexed RGB 5-6-5 (Chrontel 7003 format 0)
;     ...
;     10 = Chrontel 7003 format 9
;  >= 11 reserved
;

HAL_Video_OutputFormat
        MOV     a1, #0     ;normal/unspecified
        MOV     pc, lr

; -------------------------------------------------------------------------

;;;mjsHAL - is the mode workspace really generic enough to pass to HAL?
;;;

;
; int HAL_Video_VetMode(const void *VIDClist, const void *workspace)
;
; VIDClist  -> generic video controller list (VIDC list type 3)
; workspace -> mode workspace (if mode number), or 0
; returns 0 if OK (may be minor adjusts to VIDClist and/or workspace values)
;         non-zero if not OK
;
HAL_Video_VetMode ROUT
 [ VetModeDebug
        Push "lr"
        DebugTX "HAL_Video_VetMode"
        LDR     a3, [a1, #VIDCList3_PixelRate]
        DebugTX "Pixel rate"
        DebugReg a3
        LDR     a3, [a1, #VIDCList3_PixelRate]
        MOV     a2,#1000
        Push    "a1"
        MUL     a1,a2,a3
        BL      calculate_DSS_clock_divider
        DebugTX "DSI PLL divider"
        DebugReg a1
        DebugReg a2
        ADD     a1, a2, #1
        LDR     a2, sys_clk
        BL      |__rt_udiv|
        DebugTX "Fint"
        DebugReg a1
        BL      PRCM_GetFreqSel
        DebugTX "FreqSel"
        DebugReg a1
        Pull    "a1"
        LDR     a2, [a1, #VIDCList3_HorizSyncWidth]
        DebugTX "HSW"
        DebugReg a2
        LDR     a2, [a1, #VIDCList3_HorizFrontPorch]
        DebugTX "HFP"
        DebugReg a2
        LDR     a2, [a1, #VIDCList3_HorizBackPorch]
        DebugTX "HBP"
        DebugReg a2
        LDR     a2, [a1, #VIDCList3_VertiSyncWidth]
        DebugTX "VSW"
        DebugReg a2
        LDR     a2, [a1, #VIDCList3_VertiFrontPorch]
        DebugTX "VFP"
        DebugReg a2
        LDR     a2, [a1, #VIDCList3_VertiBackPorch]
        DebugTX "VBP"
        DebugReg a2
        LDR     a2, [a1, #VIDCList3_HorizDisplaySize]
        DebugTX "Width"
        DebugReg a2
        LDR     a2, [a1, #VIDCList3_VertiDisplaySize]
        DebugTX "Height"
        DebugReg a2
        LDR     a2, [a1, #VIDCList3_PixelDepth]
        DebugTX "Colours"
        DebugReg a2
        Pull "lr"
 ]
        ; Check pixel rate
        LDR     a3, [a1, #VIDCList3_PixelRate]
        LDR     a2, =MAX_PIXEL_RATE
        CMP     a3, a2
        MOVGT   pc,lr
        CMP     a3,#0
        MOVEQ   pc,lr
        MOV     a2,#1000
        Push    "a1,lr"
        MUL     a1,a2,a3
        BL      calculate_DSS_clock_divider
        CMP     a1, #2048
        CMPLO   a2, #128
        Pull    "a1,pc",HS
        ; Check Fint is between 0.75MHz and 2.1MHz
        ADD     a1, a2, #1
        LDR     a2, sys_clk
        BL      |__rt_udiv|
        BL      PRCM_GetFreqSel
        SUB     a1, a1, #3
        CMP     a1, #4
        Pull    "a1,lr"
        MOVHI   pc,lr ; Accept FreqSel values of 3-7 (0.75MHz->2.1MHz)
        ; Check H, V timing values
        ; Although SetMode does attempt to adjust these itself, for the time being let's try and avoid any modes with out of range values altogether.
        LDR     a2, [a1, #VIDCList3_HorizSyncWidth]
        LDR     a3, video_maxsync
        SUB     a2, a2, #1
        CMP     a2, a3
        MOVHS   pc,lr
        LDR     a2, [a1, #VIDCList3_VertiSyncWidth]
        SUB     a2, a2, #1
        CMP     a2, a3
        MOVHS   pc,lr
        LDR     a2, [a1, #VIDCList3_HorizFrontPorch]
        LDR     a3, video_maxporch
        SUB     a2, a2, #1
        CMP     a2, a3
        MOVHS   pc,lr
        LDR     a2, [a1, #VIDCList3_HorizBackPorch]
        SUB     a2, a2, #1
        CMP     a2, a3
        MOVHS   pc,lr
        LDR     a2, [a1, #VIDCList3_VertiFrontPorch]
        SUB     a2, a2, #1
        CMP     a2, a3
        MOVHS   pc,lr
        LDR     a2, [a1, #VIDCList3_VertiBackPorch]
        SUB     a2, a2, #1
        CMP     a2, a3
        MOVHS   pc,lr
        ; Check (HBP+HSW+HFP)*PCD > 8
        LDR     a2, [a1, #VIDCList3_HorizBackPorch]
        LDR     a3, [a1, #VIDCList3_HorizSyncWidth]
        LDR     a4, [a1, #VIDCList3_HorizFrontPorch]
        ADD     a2, a2, a3
        ADD     a2, a2, a4
        CMP     a2, #4 ; We always use PCD of 2
        MOVLE   pc,lr
        ; Check screen size
        LDR     a2, [a1, #VIDCList3_HorizDisplaySize]
        SUB     a2, a2, #1
        CMP     a2, #2048
        MOVHS   pc,lr
        LDR     a2, [a1, #VIDCList3_VertiDisplaySize]
        SUB     a2, a2, #1
        CMP     a2, #2048
        ; Check colour depth
        LDR     a2, [a1, #VIDCList3_PixelDepth]
        CMP     a2, #5
        MOVHI   pc,lr
        ; Else we're good
        MOV     a1,#0
        MOV     pc,lr

; -------------------------------------------------------------------------

; Function for calculating DSI PLL clock dividers
; in:  a1 = desired pixel rate (Hz)
; out: a1 = DSI_PLL_REGM (values not guaranteed to be in range)
;      a2 = DSI_PLL_REGN
; all other regs preserved
;
; The full clock flow is as follows:
;   DSI PLL input = DSI_PLL_REFCLK (= DSS2_ALWON_FCLK = sys_clk)
;   Data lane freq = DSIPHY = 2*pixel rate*DSI_BPP/DSI_LANES
;   DSIPHY = 2*DSI_PLL_REGM/(DSI_PLL_REGN+1)*DSI_PLL_REFCLK/(HIGHFREQ+1)
;   HIGHFREQ = (DSI_PLL_REFCLK>32MHz?1:0) (=0 for all current sys_clk values)
; Also, 0.75MHz <= Fint <= 2.1MHz where Fint = DSI_PLL_REFCLK/(DSI_PLL_REGN+1)
; DSIPHY is then used as the source for the HSDIVIDER, which does the following:
;   DSI1_PLL_FCLK = DSIPHY/REGM3
;   DSI2_PLL_FCLK = DSIPHY/REGM4
; Both must be <= 173MHz
; DSI2_PLL_FCLK can be used to drive the DSI module, except we don't care much for that
; DSI1_PLL_FCLK is what we use to drive the DISPC pixel clock:
;   pixel rate = (DSI1_PLL_FCLK/LCD)/PCD
;   1 <= LCD <= 255
;   2 <= PCD <= 255
; Even though we don't use the DSI output module, we still calculate DSIPHY as if we are using it (so that DSIPHY = data lane freq)
; This then simplifies REGM3, REGM4, LCD and PCD programming so that it can use fixed values independent of the pixel rate:
;   REGM3 = DSIPHY/(pixel rate*2) = DSI_LANES/DSI_BPP (so that DSI1_PLL_FCLK = 2*pixel rate)
;   LCD=1, PCD=2 (so that DSI1_PLL_FCLK is divided by two to get the target pixel rate)
;
; An algorithm that aims for 100% accurate clock rates was found to be incapable of calculating divider values for most RISC OS screen modes (in particular the 25.175MHz mode used at boot). Instead, an algorithm that aims to find the closest possible clock rate is used. Most clock rate errors appear to be tiny (within 1kHz of the desired data lane frequency), so this shouldn't pose any problems.
;
calculate_DSS_clock_divider
        CMP     a1, #0
        MVNEQ   a2, #0
        MOVEQ   pc, lr
        Push    "a3-v5,lr"
        ; data lane freq = 2*pixel rate*bpp/lanes
        ASSERT  ((2*DSI_BPP) :MOD: DSI_LANES)=0
        MOV     a3, #(2*DSI_BPP)/DSI_LANES
        MUL     v3, a3, a1 ; data lane freq
        MOV     v1, v3 ; Best error. This can easily be modified to limit the clock to a maximum error value, either fixed or to within a percentage of the pixel rate.
        MVN     v4, #0 ; Best REGM
        MVN     v5, #0 ; Best REGN
        ; Search for satisfactory REGN:
        ; - Provides Fint that's within range
        ; - sys_clk MOD REGN = 0
        MOV     v2, #1 ; Current N(+1)
;        DebugTX "calculate_DSS_clock_divider"
        LDR     a1, sys_clk
;        DebugReg a1
10
        MOV     a1, v2
        LDR     a2, sys_clk
        BL      |__rt_udiv| ; a1 = Fint, a2 = remainder
        CMP     a2, #0
        BNE     %FT20
        LDR     a3, =750000
        LDR     a4, =2100000
        CMP     a1, a3
        CMPHI   a4, a1
        BLO     %FT20
;        DebugTX "REGN -> Fint"
;        DebugReg v2
;        DebugReg a1
        ; REGN is good; use it to calculate nearest REGM
        MOV     a4, a1 ; Fint
        MOV     a1, a4, LSL #1 ; Fint*2
        MOV     a2, v3
;        DebugTX "REGM calc"
;        DebugReg a1
;        DebugReg a2
        BL      |__rt_udiv| ; a1 = REGM, a2 = error
;        DebugTX "REGM result"
;        DebugReg a1
;        DebugReg a2
        ; Round to nearest, maintaining a2 as the error
        CMP     a2, a4
        ADDGE   a1, a1, #1
        RSBGE   a2, a2, a4, LSL #1
        ; Is a2 better than our current error?
        CMP     a2, v1
        MOVLT   v1, a2
        MOVLT   v4, a1
        MOVLT   v5, v2
        CMP     a2, #0 ; Is it perfect?
        BEQ     %FT30
20
        ; Try another N
        ADD     v2, v2, #1
        CMP     v2, #129
        BLT     %BT10
30
        ; Use v4 & v5, whether they're accurate or not
        MOV     a1, v4 ; DSI_PLL_REGM
        SUB     a2, v5, #1 ; DSI_PLL_REGN
        Pull    "a3-v5,pc"

venc_setmode
       ; Change VENC mode
       ; a1 = mode:
       ;  -1 = off
       ;   0 = NTSC 601, composite
       ;   1 = NTSC 601, s-video
       ;   2 = PAL 601, composite
       ;   3 = PAL 601, s-video
       CMP      a1, #-1
       CMPNE    a1, #3
       MOVHI    pc, lr
       Push     "v1,lr"
       LDR      v1, L4_Display_Log
       ; todo - enable clocks & stuff.
       ; First we reset VENC
       LDR      a2, =VENC_F_CONTROL
       LDR      a3, [v1, a2]
       ORR      a3, a3, #1:SHL:8 ; SOFTRESET
       STR      a3, [v1, a2]
       ; Wait for reset
10
       LDR      a3, [v1, a2]
       TST      a3, #1:SHL:8
       BNE      %BT10
       ; If we were turning it off, we're done
       CMP      a1, #0
       MOVEQ    pc, lr
       ; Else we need to program the right registers
       TST      a1, #2
       MOVEQ    a2, #0 ; NTSC offset
       MOVNE    a2, #4 ; PAL offset
       ADR      a3, venc_settings
10
       LDR      a4, [a3], #4
       CMP      a4, #0
       BLT      %FT20
       LDR      ip, [a3, a2]
       STR      ip, [v1, a4]
       ADD      a3, a3, #8
       B        %BT10
20
       ; Composite/s-video
       TST      a1, #1
       MOVEQ    ip, #&A ; Composite
       MOVNE    ip, #&D ; S-video
       LDR      a4, =VENC_OUTPUT_CONTROL
       STR      ip, [v1, a4]
       ; And in DSS
       LDR      ip, [v1, #DSS_CONTROL]
       BICEQ    ip, ip, #1:SHL:6 ; Composite
       ORRNE    ip, ip, #1:SHL:6 ; S-video
       ; Other bits from u-boot: DAC_POWERDN_BGZ, DAC_DEMEN, VENC4x2
       ORR      ip, ip, #&38
       STR      ip, [v1, #DSS_CONTROL]
       ; Program remaining regs from the table
10
       LDR      a4, [a3], #4
       CMP      a4, #0
       BLT      %FT20
       LDR      ip, [a3, a2]
       STR      ip, [v1, a4]
       ADD      a3, a3, #8
       B        %BT10
20
       ; Set GODIGITAL, DIGITALENABLE
       LDR      a1, [v1, #DISPC_CONTROL]
       ORR      a1, a1, #&42
       STR      a1, [v1, #DISPC_CONTROL]
       ; Wait for vsync
       ; Except we're too lazy to do it properly
       MOV      a1, #50*1024 ; 50msec ish
       BL       HAL_CounterDelay
       ; Clear SYNCLOSTDIGITAL
       LDR      a1, [v1, #DISPC_IRQSTATUS]
       ORR      a1, a1, #1:SHL:15
       STR      a1, [v1, #DISPC_IRQSTATUS]
       ; Done!
       Pull     "v1,pc"


venc_settings
; VENC settings table
; Taken from OMAP3530 TRM (spruf98b, table 15-64)
; Format:
;           register                               NTSC 601    PAL 601
        DCD VENC_VIDOUT_CTRL,                      &00000001, &00000001
        DCD VENC_LLEN,                             &00000359, &0000035F
        DCD VENC_FLENS,                            &0000020C, &00000270
        DCD VENC_HFLTR_CTRL,                       &00000000, &00000000
        DCD VENC_CC_CARR_WSS_CARR,                 &043F2631, &2F7225ED
        DCD VENC_C_PHASE,                          &00000000, &00000000
        DCD VENC_GAIN_U,                           &00000102, &00000111
        DCD VENC_GAIN_V,                           &0000016C, &00000181
        DCD VENC_GAIN_Y,                           &0000012F, &00000140
        DCD VENC_BLACK_LEVEL,                      &00000043, &0000003B
        DCD VENC_BLANK_LEVEL,                      &00000043, &0000003B
        DCD VENC_X_COLOR,                          &00000007, &00000007
        DCD VENC_M_CONTROL,                        &00000001, &00000002
        DCD VENC_BSTAMP_WSS_DATA,                  &00000038, &0000003F
        DCD VENC_S_CARR,                           &21F07C1F, &2A098ACB
        DCD VENC_LINE21,                           &00000000, &00000000
        DCD VENC_LN_SEL,                           &01310011, &01290015
        DCD VENC_L21_WC_CTL,                       &0000F003, &0000F603
        DCD VENC_HTRIGGER_VTRIGGER,                &00000000, &00000000
        DCD VENC_SAVID_EAVID,                      &069300F4, &06A70108
        DCD VENC_FLEN_FAL,                         &0016020C, &00180270
        DCD VENC_LAL_PHASE_RESET,                  &00060107, &00040135
        DCD VENC_HS_INT_START_STOP_X,              &008E0350, &00880358
        DCD VENC_HS_EXT_START_STOP_X,              &000F0359, &000F035F
        DCD VENC_VS_INT_START_X,                   &01A00000, &01A70000
        DCD VENC_VS_INT_STOP_X_VS_INT_START_Y,     &020701A0, &000001A7
        DCD VENC_VS_INT_STOP_Y_VS_EXT_START_X,     &01AC0024, &01AF0000
        DCD VENC_VS_EXT_STOP_X_VS_EXT_START_Y,     &020D01AC, &000101AF
        DCD VENC_VS_EXT_STOP_Y,                    &00000006, &00000025
        DCD VENC_AVID_START_STOP_X,                &03480078, &03530083
        DCD VENC_AVID_START_STOP_Y,                &02060024, &026C002E
        DCD VENC_FID_INT_START_X_FID_INT_START_Y,  &0001008A, &0001008A
        DCD VENC_FID_INT_OFFSET_Y_FID_EXT_START_X, &01CA0106, &002E0138
        DCD VENC_FID_EXT_START_Y_FID_EXT_OFFSET_Y, &01060006, &01380001
        DCD VENC_TVDETGP_INT_START_STOP_X,         &00140001, &00140001
        DCD VENC_TVDETGP_INT_START_STOP_Y,         &00010001, &00010001
        DCD VENC_GEN_CTRL,                         &00F90000, &00FF0000
        DCD VENC_OUTPUT_TEST,                      &00000000, &00000000
        ; Fix display resolution - how does this affect the other regs? (SIZE_LCD, etc.) Do we need to set up scaling stuff?
        DCD DISPC_SIZE_DIG,                        &00F002CF, &00F002CF
        DCD -1 ; Break to program other registers
        ; These two must be last, apparently
        DCD VENC_F_CONTROL,                        &00000000, &00000000
        DCD VENC_SYNC_CTRL,                        &00008040, &00000040
        DCD -1

        END