; 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.PRCM GET hdr.GPIO GET hdr.CoPro15ops GET hdr.Timers 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 [ :LNOT: HALDoesVideo EXPORT VideoDevice_Init EXPORT Video_Power_VBC_DVI EXPORT Video_Power_VBC_Pandora EXPORT Video_Power_VBC_TouchBook ] IMPORT vtophys IMPORT |__rt_udiv| IMPORT DebugHALPrint IMPORT DebugHALPrintReg IMPORT PRCM_GetFreqSel IMPORT HAL_CounterDelay IMPORT DebugMemDump IMPORT memcpy IMPORT GPIOx_SetAsOutput IMPORT GPIOx_SetOutput MACRO CallOS $entry ASSERT $entry <= HighestOSEntry MOV lr, pc LDR pc, OSentries + 4*$entry MEND ; 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) [ HALDoesVideo 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 -> gone to board config ; 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 (but TFP410 datasheet says 165MHz?) ; For the moment, limit the pixel rate to 75MHz. This could probably be increased to 86.5 if some of the FIFO fixes were brought over from the OMAPVideo module. [ HALDoesVideo 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. ; ----------------------------------------------------------------------------------- [ HALDoesVideo 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} ; Set to TRUE to disable all changes to video output settings GBLL PassiveVideo PassiveVideo 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" [ :LNOT: PassiveVideo ; 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? BNE %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 LDRB a1, [sb, #BoardConfig_VideoGPIO] CMP a1, #255 MOV a2, #0 BLNE GPIOx_SetAsOutput ; Turn DVI framer off | [ ExtraVideoDebug DebugTX "Original regs" BL DumpDispcRegs ] LDR v1, L4_Display_Log ] ; PassiveVideo ; 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] ; 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 BL Determine_PorchSync_Limits STR a1, video_maxporch STR a2, 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 [ :LNOT: PassiveVideo ; 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" 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 MOV a1, #0 STR a1, [v1, #DISPC_IRQENABLE] ; Data synchronisation barrier myDSB ; 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)+(1:SHL:8) ; Enable reference clock, drift guard 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, #&6E0 ; DSI_PLL_PLLLPMODE, LOWCURRSTBY, TIGHTPHASELOCK, 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 BIC a1, a1, #3:SHL:1 ; load palette+frame data! 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] | ; Disable Vsync IRQ to avoid pointer corruption issues MOV a2, #0 STR a2, [v1, #DISPC_IRQENABLE] ; Data synchronisation barrier myDSB ; Remember the new pixel rate! LDR a3, [a1, #VIDCList3_PixelRate] STR a3, pixel_rate ; Update DISPC_CONFIG to make sure colour phase rotation & transparency colour key is in use LDR a2, [v1, #DISPC_CONFIG] BIC a2, a2, #1:SHL:3 ; LUT used as palette BIC a2, a2, #3:SHL:18 ; alpha blending disabled ORR a2, a2, #1:SHL:15 ; colour phase rotation enabled ORR a2, a2, #&F:SHL:10 ; Video source transparency colour key for both LCD & digital output BIC a2, a2, #3:SHL:1 ; Load palette+frame data! STR a2, [v1, #DISPC_CONFIG] ; Crop graphics overlay to fit LCD size LDR a2, [v1, #DISPC_SIZE_LCD] MOV a3, a2, LSR #16 ; height-1 BIC a2, a2, a3, LSL #16 ADD a2, a2, #1 ; width LDR a4, [a1, #VIDCList3_HorizDisplaySize] CMP a4, a2 SUBGT v2, a4, a2 MOVGT a4, a2 MOVLE v2, #0 SUB a4, a4, #1 LDR a2, [a1, #VIDCList3_VertiDisplaySize] SUB a2, a2, #1 CMP a2, a3 ORRGT a4, a4, a3, LSL #16 ORRLE a4, a4, a2, LSL #16 STR a4, [v1, #DISPC_GFX_SIZE] LDR a2, [a1, #VIDCList3_PixelDepth] SUBS a2, a2, #3 MOVGT v2, v2, LSL a2 ; Convert pixel overlap to bytes RSBLT a2, a2, #0 MOVLT v2, v2, LSR a2 ADD v2, v2, #1 STR v2, [v1, #DISPC_GFX_ROW_INC] MOV v2, #1 STR v2, [v1, #DISPC_GFX_PIXEL_INC] ; Just in case ] ; PassiveVideo ; 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 [ :LNOT: PassiveVideo LDR a2, =&1836b ; Same value as u-boot: GPOUT0, GPOUT1, 24-bit TFT data lines, GODIGITAL, GOLCD, active matrix display, DIGITALENABLE, LCDENABLE | LDR a2, [v1, #DISPC_CONTROL] ORR a2, a2, #3:SHL:5 ] 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 MOV a1, #2 STR a1, [v1, #DISPC_IRQENABLE] [ :LNOT: PassiveVideo ; Now enable the DVI framer LDRB a1, [sb, #BoardConfig_VideoGPIO] CMP a1, #255 MOV a2, #1 BLNE GPIOx_SetOutput ] ; 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 LTORG ; ------------------------------------------------------------------------- ; ; 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 [ :LNOT: PassiveVideo [ 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 ] ; PassiveVideo ; 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" | ; HALDoesVideo Video_init ; Configure GPIO pins so we can turn the DVI framer on/off LDRB a1, [sb, #BoardConfig_VideoGPIO] CMP a1, #255 MOV a2, #0 BNE GPIOx_SetAsOutput ; Turn DVI framer off MOV pc, lr ; Stubs for unused HAL functions HAL_Video_OutputFormat HAL_Video_VetMode MOV r0, #0 HAL_Video_SetMode HAL_Video_WritePaletteEntry HAL_Video_WritePaletteEntries HAL_Video_SetInterlace HAL_Video_SetBlank HAL_Video_SetPowerSave HAL_Video_UpdatePointer HAL_Video_SetDAG HAL_Video_ReadPaletteEntry MOV pc, lr HAL_VideoFlybackDevice MOV a1, #-1 MOV pc, lr HAL_Video_PixelFormats MOV a1, #2_111111 MOV pc, lr HAL_Video_Features MOV a1, #2_10010 ; hw pointer, no VSyncs MOV pc, lr HAL_Video_BufferAlignment MOV a1, #4 MOV pc, lr VideoDevice_Init ; Not much to do here - just register our HAL device Push "v1,lr" ADRL v1, VideoDevice MOV a1, v1 ADR a2, VideoDeviceTemplate MOV a3, #Video_DeviceSize BL memcpy LDR a1, L4_Display_Log STR a1, [v1, #HALDevice_Address] ADRL a3, VideoBoardConfig STR a3, [v1, #HALDevice_VDUDeviceSpecificField] STR sb, [v1, #:INDEX:VideoWorkspace] ; Fill in the board config LDR a2, sys_clk STR a2, [a3, #VideoBoardConfig_sys_clk] BL Determine_PorchSync_Limits STRH a1, [a3, #VideoBoardConfig_Max_Porch] STRH a2, [a3, #VideoBoardConfig_Max_Sync] LDR a2, L4_sDMA_Log STR a2, [a3, #VideoBoardConfig_DMA_Ptr] MOV a2, #SDMA_IRQ_1 STR a2, [a3, #VideoBoardConfig_DMA_Device] ASSERT SDMA_NumDevices = 31 MOV a2, #&80000000 STR a2, [a3, #VideoBoardConfig_DMA_Chans] LDRB a2, [sb, #BoardConfig_VBC_Flags] STRB a2, [a3, #VideoBoardConfig_Flags] LDRB a2, [sb, #BoardConfig_VBC_LCDNum] STRB a2, [a3, #VideoBoardConfig_Num_LCDs] LDR a2, [sb, #BoardConfig_VBC_LCDPtr] STR a2, [a3, #VideoBoardConfig_LCD_Configs] MOV a1, #0 MOV a2, v1 CallOS OS_AddDevice Pull "v1,pc" VideoDeviceTemplate DCW HALDeviceType_Video + HALDeviceVideo_VDU DCW HALDeviceID_VDU_OMAP3 DCD HALDeviceBus_Interconnect + HALDeviceInterconnectBus_L3 DCD 0 ; API version 0 DCD VideoDevice_Desc DCD 0 ; Address - filled in later % 12 ; Reserved DCD VideoDevice_Activate DCD VideoDevice_Deactivate DCD VideoDevice_Reset DCD VideoDevice_Sleep DCD VIDEO_IRQ ; Device. DCD 0 ; TestIRQ cannot be called % 8 DCD 0 ; Pointer to board config stuff - filled in later ASSERT (.-VideoDeviceTemplate) = HALDevice_VDU_Size DCD 0 ; HAL workspace pointer - filled in later ASSERT (.-VideoDeviceTemplate) = Video_DeviceSize VideoDevice_Desc = "OMAP3 video controller", 0 ALIGN VideoDevice_Activate Entry "sb" LDR sb, VideoWorkspace ; Enable DSS power ; TODO - should disable interrupts for this bit! 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] MOV a1, #1 EXIT VideoDevice_Deactivate ; TODO! VideoDevice_Reset MOV pc, lr VideoDevice_Sleep MOV a1, #0 MOV pc, lr Video_Power_VBC_Pandora Video_Power_VBC_DVI ; a1 = HAL device ; a2 = brightness 0-65536, which we treat as a simple on/off flag Entry "sb" LDR sb, VideoWorkspace ; Just set the GPIO to the right value LDRB a1, [sb, #BoardConfig_VideoGPIO] CMP a1, #255 BLNE GPIOx_SetOutput EXIT Video_Power_VBC_TouchBook ; a1 = HAL device ; a2 = brightness 0-65536 Entry "sb" LDR sb, VideoWorkspace ; TODO - Proper brightness controls ; For now, just toggle on/off, via the SCPWM bit of GPTIMER9.TCLR LDR a3, Timers_Log ADD a3, a3, #L4_GPTIMER9-TIMER_BASE CMP a2, #0 LDR a4, [a3, #TCLR] BICEQ a4, a4, #&80 ORRNE a4, a4, #&80 STR a4, [a3, #TCLR] ; Also toggle the GPIO that controls the DVI framer LDRB a1, [sb, #BoardConfig_VideoGPIO] CMP a1, #255 BLNE GPIOx_SetOutput EXIT ] Determine_PorchSync_Limits ; Returns: ; a1 = max porch value ; a2 = max sync value ; Get the OMAP revision so we can find out how large the timing registers are. OMAP35x errata shows that in ES 3.1+ the registers were increased from 8/6 bits to 12/8 bits. LDR a2, L4_Wakeup_Log ADD a2, a2, #(L4_CONTROL_IDCODE-L4_Wakeup) :AND: &FF00 LDR a2, [a2, #(L4_CONTROL_IDCODE-L4_Wakeup) :AND: &00FF] ; Check revision first CMP a2, #REVISION_ES31 :SHL: 28 MOVHS a1, #1:SHL:12 MOVHS a2, #1:SHL:8 MOVHS pc, lr ; Revision check failed - check if this is OMAP35x ; If not, assume it's something newer which supports the larger timings LDR a1, =HAWKEYE_OMAP35x_ES10 MOV a2, a2, LSL #4 CMP a1, a2, LSR #16 LDRNE a1, =HAWKEYE_OMAP35x CMPNE a1, a2, LSR #16 MOVNE a1, #1:SHL:12 MOVNE a2, #1:SHL:8 MOVEQ a1, #1:SHL:8 MOVEQ a2, #1:SHL:6 MOV pc, lr END