Trying to get the DSi cameras working

Discussion of development of software for any "obsolete" computer or video game system.
Post Reply
Arisotura
Posts: 16
Joined: Sun May 19, 2019 7:01 am

Trying to get the DSi cameras working

Post by Arisotura » Sat Oct 24, 2020 5:42 pm

I want to hardware-test the camera functionality before implementing it in melonDS (well, I did, but so far my implementation is a trainwreck).

but, that's the thing, I can't get cameras to work on hardware, no matter what I do. I tried following the init procedure on GBAtek, I tried copying that found in some open-source MT9V113 driver, I even tried going from a disassembly of the DSi camera app code and some logs from melonDS, but none of it did the trick.

basically, the camera seems to initialize itself in some fucked-up status where it will never send any data, and if I put it in standby mode, I can never turn it back on again -- 'standard' camera wakeup code just hangs forever.

furthermore, some registers seem to entirely ignore writes at that point. setting bit9 in 001A (to enable data transfers) does not work, and I can't seem to change that register at all. also, MIPI registers are in some weird contradictory state (3402 reports MIPI is in standby, even though 3400 doesn't enable standby).

I even went and looked for code using the DSi cameras. found this, but they seem to have run into the same issue as me.

at this point, I threw everything I could at this wall, with zero effect whatsoever. so, has anybody ever managed to get the DSi cameras working? GBAtek kinda implies nocash did get them working, so I'd be curious.

nocash
Posts: 1259
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: Trying to get the DSi cameras working

Post by nocash » Sat Oct 24, 2020 9:35 pm

Below is messy, but tested and working on hardware...

Code: Select all

;------------------
dsi_cam_init:         ;init camera (and display the camera bitmap)...
 push lr
 bl   cls
 ;- - -
 REGBASE_CAM               equ 4004200h
 REG_CAM_MCNT                equ 000h
 REG_CAM_CNT                 equ 002h
 REG_CAM_DAT                 equ 004h
 REG_CAM_SOFS                equ 010h
 REG_CAM_EOFS                equ 014h
 ;- - -

@@yuv_mode equ 0;1   ;YUV mode (with SLOW manual YUV-to-RGB conversion)

 bl   ini_engine_b_32k_bitmap

 mov  r4,4000000h                       ;\
 mov  r0,84h                            ; map VramC to 6200000h
 strb r0,[r4,242h]                      ;/
 mov  r0,0                              ;\
 mov  r1,0100h     ;rot/scal            ;
 ldr  r4,=4001000h                      ;
 strh r1,[r4,20h]                       ;
 strh r0,[r4,22h]                       ; rot/scale
 strh r0,[r4,24h]                       ;
 strh r1,[r4,26h]                       ;
 strh r0,[r4,28h]                       ;
 strh r0,[r4,2ah]                       ;
 strh r0,[r4,2ch]                       ;
 strh r0,[r4,2eh]                       ;/

ldr  r1,=06200000h
mov  r0,0
mov  r2,256*256 ;/2
@@lll:
orr  r0,8000h
strh r0,[r1],2
add  r0,1
subs r2,1
bne  @@lll

        bl arm9_show_cam_irq

 bl   arm9_cam_int_clk_on               ;-cam interface clk on (blah/anyways)
 bl   arm9_cam_mcnt_zero                ;-cam mcnt
 bl   arm9_cam_ext_clk_on               ;-cam clk on  (required for I2C here)
 bl   arm9_cam_mcnt_22h                 ;-cam mcnt
;bl   arm9_cam_ext_clk_off              ;-cam clk off (blah)

 ldr  r1,=REGBASE_CAM
 ldrh r0,[r1,REG_CAM_CNT]
 bic  r0,8000h // strh r0,[r1,REG_CAM_CNT]  ;AND NOT 8000h ;CAM_CNT, disable (allow changing params)
 orr  r0,0020h // strh r0,[r1,REG_CAM_CNT]  ;OR 0020h
  bic r0,20h ;?
 bic  r0,0300h   ;PICMODE?
 orr  r0,0200h // strh r0,[r1,REG_CAM_CNT]  ;AND NOT 0300h, OR 0200h         (0..3 no effect)
 orr  r0,0400h // strh r0,[r1,REG_CAM_CNT]  ;OR 0400h   ;whatever (works without) (no effect)
 orr  r0,0800h // strh r0,[r1,REG_CAM_CNT]  ;OR 0800h   ;IRQ enable

;bl   arm9_cam_ext_clk_on               ;-cam clk on (blah)
 ldr  r10,=aptina_code_list_init        ;\
 mov  r11,7ah  ;device1                 ; init BOTH cameras
 mov  r12,78h  ;device2                 ;
 bl   send_aptina_code_list             ;/
;bl   arm9_cam_ext_clk_off              ;\cam clk off-then-on (blah)
;bl   arm9_cam_ext_clk_on               ;/

        bl arm9_show_cam_irq


;Now do more camera I2C stuff (LED on, and init ONE of the cameras, till E0 04 98)
 ldr  r10,=aptina_code_list_activate    ;\
 mov  r11,7ah  ;device1                 ; activate ONE camera
 mov  r12,r11  ;same                    ; (7Ah=SelfPortrait, 78h=Landscape)
 bl   send_aptina_code_list             ;/

        bl arm9_show_cam_irq

 ldr  r1,=REGBASE_CAM
 ldr  r0,=01ff0080h
 ldr  r0,=008001ffh  ;<-- full scanlines (rapid vertical roll)
 ldr  r0,=006001ffh  ;<-- full scanlines (slow vertical roll)
 ldr  r0,=00c00080h  ;<-- half scanlines (left half repeated 2x, plus black lines)
 ldr  r0,=00c00100h  ;<-- full scanlines (normal picture)
 ldr  r0,=00c001ffh  ;<-- full scanlines (normal picture)
 str  r0,[r1,REG_CAM_EOFS]

 ldr  r1,=REGBASE_CAM
 ldrh r0,[r1,REG_CAM_CNT]
 orr  r0,2000h // strh r0,[r1,REG_CAM_CNT]  ;OR 2000h  ;whatever (works without)
.if @@yuv_mode
   bic r0,2000h   ;<-- cause valid picture (but with white-green-snow instead of correct RGB colors)
.endif
        orr r0,4000h  ;trimming enable
        bic r0,4000h
 bic  r0,000fh
 orr  r0,0003h // strh r0,[r1,REG_CAM_CNT]  ;AND NOT 000Fh, OR 0003h        ;DMA.BLK
             ;3=normal
             ;1=draw2lines, insert2blacklines?
             ;7=skip each 2nd line (or draw4lines, skip4lines?)
 orr  r0,0020h // strh r0,[r1,REG_CAM_CNT]  ;OR 0020h  ;was already so though (or is it W?)
  bic r0,20h ;?
 orr  r0,8000h // strh r0,[r1,REG_CAM_CNT]  ;OR 8000h  ;<--- kick (now AE23h)
 ;Read r0 = ([REGBASE_CAM+REG_CAM_CNT] AND 000Fh)+0001h


        bl arm9_show_cam_irq

                 ldr  r1,=REGBASE_CAM
                 ldrh r0,[r1,REG_CAM_CNT]
                 bl   wrhex16bit
                 bl   wrspc
                 ldrh r0,[r1,REG_CAM_CNT]
                 bl   wrhex16bit
                .if 0
                 bl   wrspc
                 ldr  r0,[r1,REG_CAM_DAT]
                 bl   wrhex32bit
                 bl   wrspc
                 ldr  r0,[r1,REG_CAM_DAT]
                 bl   wrhex32bit
                .endif
                 bl   wrcrlf
                 bl   wrspc
                 ldr  r0,[r1,REG_CAM_SOFS] ;trim start
                 bl   wrhex32bit
                 bl   wrspc
                 ldr  r0,[r1,REG_CAM_EOFS] ;trim end
                 bl   wrhex32bit
                 bl   wrcrlf
         ldr  r1,=4004100h
         ldr  r0,[r1,38h]  ;NDMA1CNT
         bl   wrhex32bit
         bl   wrcrlf


;XXX ---> Now do NDMA1
bl flushkey
@@keylop:

.if 0;1  ;try manual read...
         ldr  r4,=REGBASE_CAM

        .if 01
             ldrh r0,[r4,REG_CAM_CNT]
              orr r0,20h
             strh r0,[r4,REG_CAM_CNT]

           ldr  r2,[r4,REG_CAM_MCNT]
           mov  r0,r2
           bl   wrhex32bit
           bl   wrspc
          @@wx:
           ldr  r0,[r4,REG_CAM_MCNT]
           cmp  r0,r2
          beq  @@wx
           bl   wrhex32bit
           bl   wrspc
           bl   wrhex32bit
           bl   wrcrlf
        .endif

         mov  r1,6200000h  ;dst
         mov  r3,192/4
        @@xfer_ylop:
        @@wait_blk_lop:                 ;\
         ldrh r0,[r4,REG_CAM_CNT]       ; wait
         tst  r0,10h                    ;
         beq  @@wait_blk_lop            ;/
         mov  r2,200h                   ;\
        @@xfer_blk_lop:                 ;
         ldr  r0,[r4,REG_CAM_DAT]       ; read
         str  r0,[r1],4                 ;
         subs r2,1                      ;
         bne  @@xfer_blk_lop            ;/

        .if 0
           ldrh r0,[r4,REG_CAM_CNT]          ;PICMODE?
           bic  r0,300h
           ;orr  r0,100h+200h
           ;bic  r0,400h   ;?
           strh r0,[r4,REG_CAM_CNT]

           ldrh r0,[r4,REG_CAM_CNT]
           ; orr r0,20h       ;<-- forces delay, plus UPPER block again
           ;bic r0,8000h
           ;strh r0,[r4,REG_CAM_CNT]
              ;mov r2,10000h   ;<-- gets one 2nd block after this HUGE delay!
              ;@@wwyy:
              ;subs r2,1
              ;bne @@wwyy
        .endif

         subs r3,1
         bne  @@xfer_ylop

         ldr  r2,[r4,REG_CAM_MCNT]
         mov  r0,r2
         bl   wrhex32bit
         bl   wrspc
         bl   wrhex32bit
         bl   wrcrlf
        @@w:
         ldr  r0,[r4,REG_CAM_MCNT]
         cmp  r0,r2
        ;beq  @@w
         bl   wrhex32bit
         bl   wrspc
         bl   wrhex32bit
         bl   wrcrlf


.else
         ldr  r1,=4004100h
        ;ldr  r0,=80050000h // str  r0,[r1,0h]   ;NDMACNT  ;DSi7
         ldr  r0,=80060000h // str  r0,[r1,0h]   ;NDMACNT  ;DSi9
         mov  r0, 0         // str  r0,[r1,38h]  ;NDMA1CNT
         ldr  r0,=REGBASE_CAM+REG_CAM_DAT // str  r0,[r1,20h]  ;NDMA1SAD <-- CAMERA
         ldr  r0,=06200000h // str  r0,[r1,24h]  ;NDMA1DAD <-- VRAM
        .if @@yuv_mode                                        ;or
         ldr  r0,=02800000h // str  r0,[r1,24h]  ;NDMA1DAD <-- RAM
        .endif
         mov  r0, 00006000h // str  r0,[r1,28h]  ;NDMA1TCNT
         mov  r0, 00000200h // str  r0,[r1,2Ch]  ;NDMA1WCNT   ;DMA.BLK
         mov  r0, 00000002h // str  r0,[r1,30h]  ;NDMA1BCNT
         mov  r0, 00000000h // str  r0,[r1,34h]  ;NDMA1FDATA
         ldr  r0,=8B044000h // str  r0,[r1,38h]  ;NDMA1CNT    ;bit16-19:DMA.BLK
        @@wait_dma:
         ldr  r1,=4004100h
         ldr  r0,[r1,38h]  ;NDMA1CNT
         tst  r0,80000000h
         bne  @@wait_dma

.if @@yuv_mode
  push r0-r12
  mov  r4,2800000h
  mov  r5,6200000h
  mov  r6,256*192
 @@copy_to_vram_lop:
  ldr  r0,[r4],4
  mov  r0,r0,lsr 0   ;Y1
  mov  r1,r0,lsr 16  ;Y2
  mov  r2,r0,lsr 8   ;Cb
  mov  r3,r0,lsr 24  ;Cr
  and  r0,0ffh       ;Y1
  and  r1,0ffh       ;Y2
  and  r2,0ffh       ;Cb
  and  r3,0ffh       ;Cr
  sub  r2,80h        ;Cb
  sub  r3,80h        ;Cr
  ldr  r8,=1772*100h/1000  ;(for B)
  ldr  r9,=1402*100h/1000  ;(for R)
  mov  r10,0344*100h/1000  ;(for G-B)
  mov  r11,0714*100h/1000  ;(for G-R)
  mul  r8,r8,r3            ;(for B)
  mul  r9,r9,r2            ;(for R)
  mul  r10,r10,r3          ;(for G-B)
  mul  r11,r11,r2          ;(for G-R)
  bl   @@emit
  mov  r0,r1  ;Y2
  bl   @@emit
; R = 1.000 x Y + 1.402 x (V - 128)
; G = 1.000 x Y - 0.344 x (U - 128) - 0.714 x (V - 128)
; B = 1.000 x Y + 1.772 x (U - 128)
  mov  r0,r0,lsr 3
  subs r6,1+1
  bne  @@copy_to_vram_lop
  b    @@skip
          @@emit:
           add  r2,r0,r9,asr 8   ;B
           add  r3,r0,r8,asr 8   ;R
           sub  r7,r0,r10,asr 8  ;G-B
           sub  r7,r7,r11,asr 8  ;G-R
           cmp  r2,0 // movlt r2,0
           cmp  r3,0 // movlt r3,0
           cmp  r7,0 // movlt r7,0
           cmp  r2,100h // movge r2,0ffh
           cmp  r3,100h // movge r3,0ffh
           cmp  r7,100h // movge r7,0ffh
           mov  r2,r2,lsr 3
           mov  r3,r3,lsr 3
           mov  r7,r7,lsr 3
           mov  r0,8000h           ;Alpha
           orr  r0,r0,r2,lsl 10    ;B
           orr  r0,r0,r7,lsl 5     ;G
           orr  r0,r0,r3,lsl 0     ;R
           strh r0,[r5],2
           bx  lr
  @@skip:
   pop r0-r12
.endif ;(@@yuv_mode)

.if 1
    mov  r0,10
    ldr  r4,=scryloc
    strb r0,[r4]
   ldr  r1,=02800000h
   ldr  r0,[r1]
   bl wrhex32bit
   bl wrcrlf
   ldr  r1,=02800200h
   ldr  r0,[r1]
   bl wrhex32bit
   bl wrcrlf
   ldr  r1,=02800400h
   ldr  r0,[r1]
   bl wrhex32bit
   bl wrcrlf
   ldr  r1,=02800600h
   ldr  r0,[r1]
   bl wrhex32bit
   bl wrcrlf
   bl wrcrlf
.endif

.endif


bl   getkey
beq @@keylop


                 ldr  r1,=REGBASE_CAM
                 ldrh r0,[r1,REG_CAM_CNT]
                 bl   wrhex16bit
                 bl   wrspc
                 ldrh r0,[r1,REG_CAM_CNT]
                 bl   wrhex16bit
                .if 0
                 bl   wrspc
                 ldr  r0,[r1,REG_CAM_DAT]
                 bl   wrhex32bit
                 bl   wrspc
                 ldr  r0,[r1,REG_CAM_DAT]
                 bl   wrhex32bit
                .endif
                 bl   wrcrlf
         ldr  r1,=4004100h
         ldr  r0,[r1,38h]  ;NDMA1CNT
         bl   wrhex32bit
         bl   wrcrlf
        bl arm9_show_cam_irq


 bl   waitkey
 pop  pc
;---
arm9_cam_mcnt_zero:
 ldr  r1,=REGBASE_CAM                   ;CAM_MCNT, Camera Module Control
 mov  r0,0000h          ;CLR all
 strh r0,[r1,REG_CAM_MCNT]
 mov  r0,1eh
 b    arm9_camera_delay
;---
arm9_cam_mcnt_22h:
 ldr  r1,=REGBASE_CAM                   ;CAM_MCNT, Camera Module Control
 mov  r0,0022h          ;SET bit1,bit5
 strh r0,[r1,REG_CAM_MCNT]
 mov  r0,2000h          ;HUGE DELAY here
 b    arm9_camera_delay
;---
arm9_cam_int_clk_on:
 ldr  r1,=4004004h
 ldrh r0,[r1]
 orr  r0,0004h          ;SET bit2 (usually enabled anyways)
 strh r0,[r1]
 mov  r0,1eh
 b    arm9_camera_delay
;---
arm9_cam_ext_clk_on:
 ldr  r1,=4004004h
 ldrh r0,[r1]
 orr  r0,0100h          ;SET bit8 (needed for camera access)
 strh r0,[r1]
 mov  r0,1eh
 b    arm9_camera_delay
;---
arm9_cam_ext_clk_off:
 ldr  r1,=4004004h
 ldrh r0,[r1]
 bic  r0,0100h          ;CLR bit8
 strh r0,[r1]
 mov  r0,1eh
 b    arm9_camera_delay
;---
arm9_camera_delay:
    ;XXX should adjust to ARM9-CPU-SPEED
    ;mov r0,r0,lsl 1  ;???
    ;mov r0,r0,lsl 1  ;???
@@lop:
 subs r0,1
 bne  @@lop
 bx   lr
;---
arm9_show_cam_irq:
 push lr
 mov  r1,4000000h
 ldr  r0,[r1,214h] ;IF
 and  r0,1 shl 25  ;CAM
 bl   wrhex32bit
 bl   wrcrlf
 pop  pc
;------------------
send_aptina_code_list:   ;in: r10=src, r11=device1, r12=device2
 push   r0-r12,lr
 bl     switch_to_other_cpu             ;-be ARM7
@@list_lop:                             ;-list lop...
 push   r11
 ldrh   r4,[r10],2 ;CMD (token)         ;\
 ldrh   r5,[r10],2 ;IDX (index)         ;
 ldrh   r6,[r10],2 ;DTA (data)          ; obtain list entry
 tst    r4,AptDiff                      ;
 moveq  r7,r6      ;DTA2=DTA            ;
 ldrneh r7,[r10],2 ;DTA2 (data2)        ;/
 mov    r11,r11 ;device1                ;\device lop...
@@device_lop:                           ;/
@@wait_lop:                             ;\
 bl     @@read_old_data                 ;
 bl     @@check_wait_condition          ; data core
 bne    @@wait_lop                      ;
 bl     @@manipulate_data               ;
 bl     @@write_new_data                ;/

  .if 0
   cmp   r11,7ah
   bne   @@no_read
   and   r0,r4,0ffh ;CMD.lsbs
   cmp   r0,AptRd
   bne   @@no_read
   mov   r0,r8
   bl    wrhex16bit
   bl    wrcrlf
   bl    waitkey
  @@no_read:
  .endif


 cmp    r11,r12 ;device2                ;\
 movne  r11,r12 ;device2                ; lop next device
 movne  r6,r7      ;DTA=DTA2            ; (unless already device2)
 bne    @@device_lop                    ;/
 pop    r11
 ldrsh  r0,[r10]   ;CMD (token)         ;\
 cmp    r0,-1                           ; lop next list entry
 bne    @@list_lop                      ;/
 bl     switch_to_other_cpu             ;-back to ARM9 side
 pop    r0-r12,pc
;--- --- ---
@@manipulate_data:
 and   r0,r4,0ffh ;CMD.lsbs
 cmp   r0,AptSet  ;\SET (or)
 orreq r8,r6      ;/
 cmp   r0,AptClr  ;\CLR (and not)
 biceq r8,r6      ;/
 bx    lr
;---
@@check_wait_condition:
 and   r0,r4,0ffh ;CMD.lsbs
 cmp   r0,AptWaitSet   ;\
 beq   @@wait_set      ;/
 cmp   r0,AptWaitClr   ;\
 beq   @@wait_clr      ;/
 cmp   r0,AptWaitOther ;\
 beq   @@wait_other    ;/
@@do_not_wait:
 cmp   r0,r0  ;EQ=no wait
 bx    lr
@@do_wait:
 cmp   sp,0   ;NE=wait
 bx    lr
@@wait_set:
 tst   r8,r6
 bne   @@do_not_wait  ;ready (set)
 b     @@do_wait
@@wait_clr:
 tst   r8,r6
 beq   @@do_not_wait  ;ready (cleared)
 b     @@do_wait
@@wait_other:
 cmp   r8,r6
 bne   @@do_not_wait  ;ready (other)
 b     @@do_wait
;---
@@read_old_data:
 mov   r8,r6      ;DTA           ;\
 and   r0,r4,0ffh ;CMD.lsbs      ; exit if raw-write (and return DTA from list)
 cmp   r0,AptWr                  ;
 bxeq  lr                        ;/
 push  lr
 bl    @@send_mcu_idx    ;-send MCU index (if any)
 mov   r1,082h // orr r0,r11,0    // bl i2c_send_data ;device    + START     ;\
 mov   r1,080h // mov r0,r9,lsr 8 // bl i2c_send_data ;index.msb (IDX/0990h) ;
 mov   r1,081h // mov r0,r9       // bl i2c_send_data ;index.lsb + STOP      ;/
 mov   r1,082h // orr r0,r11,1    // bl i2c_send_data ;device+rd + START     ;\
 mov   r1,0b0h // bl i2c_get_data // mov r8,r0,lsl 8  ;index.msb (DTA)       ;
 mov   r1,0a1h // bl i2c_get_data // orr r8,r0        ;index.lsb + STOP      ;/
 pop   pc
;---
@@write_new_data:
 and   r0,r4,0ffh ;CMD.lsbs     ;\
 cmp   r0,AptWr                 ;
 cmpne r0,AptSet                ; exit if not write (or set/clr)
 cmpne r0,AptClr                ;
 bxne  lr                       ;/
 push  lr
 bl    @@send_mcu_idx    ;-send MCU index (if any)
 mov   r1,082h // orr r0,r11,0    // bl i2c_send_data ;device    + START     ;\
 mov   r1,080h // mov r0,r9,lsr 8 // bl i2c_send_data ;index.msb (IDX/0990h) ;
 mov   r1,080h // mov r0,r9       // bl i2c_send_data ;index.lsb             ;
 mov   r1,080h // mov r0,r8,lsr 8 // bl i2c_send_data ;data.msb  (DTA)       ;
 mov   r1,081h // mov r0,r8       // bl i2c_send_data ;data.lsb  + STOP      ;/
 pop   pc
;---
@@send_mcu_idx:
 mov   r9,r5     ;IDX    ;\
 tst   r4,AptMcu ;CMD    ; exit if non-mcu (and return r9=IDX)
 bxeq  lr                ;/
 push  lr
 mov   r1,082h // orr r0,r11,0    // bl i2c_send_data ;device    + START     ;\
 mov   r1,080h // mov r0,09h      // bl i2c_send_data ;index.msb (098Ch)     ;
 mov   r1,080h // mov r0,8ch      // bl i2c_send_data ;index.lsb             ;
 mov   r1,080h // mov r0,r5,lsr 8 // bl i2c_send_data ;data.msb  (IDX)       ;
 mov   r1,081h // mov r0,r5       // bl i2c_send_data ;data.lsb  + STOP      ;/
 mov   r9,0990h          ;-return r9=0990h (instead of IDX)
 pop   pc
;------------------
.pool
;------------------
AptWr        equ 00h  ;MOV
AptSet       equ 01h  ;OR
AptClr       equ 02h  ;AND NOT
AptWaitSet   equ 03h  ;test, wait until NZ
AptWaitClr   equ 04h  ;test, wait until Z
AptWaitOther equ 05h  ;compare, wait until NE
AptRd        equ 06h  ;read...
;---
AptMcu       equ 1000h
AptDiff      equ 2000h
AptOnly7Ah   equ 4000h
AptOnly78h   equ 8000h
;---
AptWrDiff       equ AptWr+AptDiff
AptRdMcu        equ AptMcu+AptRd
AptWrMcu        equ AptMcu+AptWr
AptSetMcu       equ AptMcu+AptSet
AptClrMcu       equ AptMcu+AptClr
AptWrMcuDiff    equ AptMcu+AptWr+AptDiff
AptWrMcuOnly7Ah equ AptMcu+AptWr+AptOnly7Ah
AptWaitMcuOther equ AptMcu+AptWaitOther
;------------------

 InitAptDetails  equ 0

;------------------
 ;SysFlaw init from 200D144h:
 ;  [4004004h]=[4004004h] OR 0004h      ;SCFG_CLK, Camera Interface Clock = ON
 ;  [4004200h]=0000h                    ;CAM_MCNT, Camera Module Control
 ;  test [4004004h].bit0 and delay(1Eh, step4) ;SCFG_CLK, ARM9 CPU Clock
 ;  [4004004h]=[4004004h] OR 0100h      ;SCFG_CLK, Camera External Clock = ON
 ;  test [4004004h].bit0 and delay(1Eh, step4) ;SCFG_CLK, ARM9 CPU Clock
 ;  [4004200h]=0022h                    ;CAM_MCNT, Camera Module Control
 ;  test [4004004h].bit0 and delay(2008h, step4) ;SCFG_CLK, ARM9 CPU Clock
 ;  [4004004h]=[4004004h] AND NOT 0100h ;SCFG_CLK, Camera External Clock = OFF
 ;Further code from 200D1F4h:
 ;  [4004202h]=[4004202h] AND NOT 8000h ;CAM_CNT, disable (allow changing params)
 ;Further code from 200D270h:
 ;  [4004202h]=[4004202h] OR 0020h
 ;Further code from 200D2C8h:
 ;  [4004202h]=([4004202h] AND NOT 0300h) OR 0200h
 ;Further code from 200D2F4h:
 ;  [4004202h]=[4004202h] OR 0400h
 ;Further code from 200D294h:
 ;  [4004202h]=[4004202h] OR 0800h
 ;Further code from 200C9BCh:
 ;  [4004004h]=[4004004h] OR 0100h      ;SCFG_CLK, Camera External Clock = ON
 ;  test [4004004h].bit0 and delay(14h, step4) ;SCFG_CLK, ARM9 CPU Clock
 ;---> Now do camera I2C init (up to setting BPTWL camera LED=off) <---
 ;     aptina_code_list_init
 ;Further code from 200CBxxh:
 ;  [4004004h]=[4004004h] AND NOT 0100h ;SCFG_CLK, Camera External Clock = OFF
 ;Further code from 200CBxxh:
 ;  [4004004h]=[4004004h] OR 0100h      ;SCFG_CLK, Camera External Clock = ON
 ;  test [4004004h].bit0 and delay(14h, step4) ;SCFG_CLK, ARM9 CPU Clock
 ;---> Now do more camera I2C stuff (LED on, and init ONE of the cameras, till E0 04 98)
 ;     aptina_code_list_activate
 ;Further code from 200D218h:
 ;  [4004202h]=[4004202h] OR 2000h
 ;Further code from 200D3xxh:
 ;  [4004202h]=([4004202h] AND NOT 000Fh) OR 0003h
 ;Further code from 200D270h:
 ;  [4004202h]=[4004202h] OR 0020h   ;was already so though
 ;Further code from 200D1D0h:
 ;  [4004202h]=[4004202h] OR 8000h   ;<--- kick (now AE23h)
 ;Further code from 200D36Ch:
 ;  Read r0 = ([4004202h] AND 000Fh)+0001h
 ;  Mul 200h
 ;---> Now do NDMA1:
 ;  [4004120h]=04004204h    ;NDMA1SAD <-- CAMERA
 ;  [4004124h]=020D67E0h    ;NDMA1DAD <-- RAM
 ;  [4004128h]=00006000h    ;NDMA1TCNT
 ;  [400412Ch]=00000200h    ;NDMA1WCNT
 ;  [4004130h]=00000002h    ;NDMA1BCNT
 ;  [4004134h]=00000000h    ;NDMA1FDATA
 ;  [4004138h]=8B044000h    ;NDMA1CNT
 ;Then,
 ;  Read r0 = ([4004202h] AND 000Fh)+0001h
 ;  and then hang... until NDMA finishes or so
;------------------
aptina_code_list_init:   ;(as from system flaw)
 dw AptRd     ,00000h,0               ;CHIP_VERSION_REG
 dw AptWr     ,0001Ah,00003h          ;RESET_AND_MISC_CONTROL (issue reset)
 dw AptWr     ,0001Ah,00000h          ;RESET_AND_MISC_CONTROL (release reset)
 dw AptWr     ,00018h,04028h          ;STANDBY_CONTROL (wakeup)
 dw AptWr     ,0001Eh,00201h          ;PAD_SLEW
 dw AptWr     ,00016h,042DFh          ;CLOCKS_CONTROL
 dw AptWaitClr,00018h,04000h          ;STANDBY_CONTROL (wait for bit14=0=WakeupDone)
 dw AptWaitSet,0301Ah,00004h          ;UNDOC! RESERVED? ALIAS of 3022h? (bit2=1=WakeupDone)
 dw AptWrMcu  ,002F0h,00000h          ;UNDOC! RAM?
 dw AptWrMcu  ,002F2h,00210h          ;UNDOC! RAM?
 dw AptWrMcu  ,002F4h,0001Ah          ;UNDOC! RAM?
 dw AptWrMcu  ,02145h,002F4h          ;UNDOC! SEQ?
 dw AptWrMcu  ,0A134h,  001h          ;UNDOC! SEQ?
 dw AptSetMcu ,0A115h,  002h          ;SEQ_CAP_MODE (set bit1=video)
 dw AptWrMcu  ,02755h,00002h          ;MODE_OUTPUT_FORMAT_A (bit5=0=YUV)
 dw AptWrMcu  ,02757h,00002h          ;MODE_OUTPUT_FORMAT_B
 dw AptWr     ,00014h,02145h          ;PLL_CONTROL
 dw AptWr     ,00010h,00111h          ;PLL_DIVIDERS
 dw AptWr     ,00012h,00000h          ;PLL_P_DIVIDERS
 dw AptWr     ,00014h,0244Bh          ;PLL_CONTROL
 dw AptWr     ,00014h,0304Bh          ;PLL_CONTROL
 dw AptWaitSet,00014h,08000h          ;PLL_CONTROL (wait for bit15=1=PllLockOkay)
 dw AptClr    ,00014h,00001h          ;PLL_CONTROL (change bit0=0=PllBypassOff)
 ;---
.if InitAptDetails   ;WORKs if skipped
 dw AptWrMcu  ,0222Dh,00058h          ;AE_R9_STEP                       ;-AE R9 step
 dw AptWrMcu  ,0A408h,  015h          ;FD_SEARCH_F1_50
 dw AptWrMcu  ,0A409h,  018h          ;FD_SEARCH_F2_50
 dw AptWrMcu  ,0A40Ah,  019h          ;FD_SEARCH_F1_60
 dw AptWrMcu  ,0A40Bh,  01Ch          ;FD_SEARCH_F2_60
 dw AptWrMcu  ,02411h,00058h          ;FD_R9_STEP_F60_A
 dw AptWrMcu  ,02413h,0006Ah          ;FD_R9_STEP_F50_A
 dw AptWrMcu  ,02415h,00058h          ;FD_R9_STEP_F60_B
 dw AptWrMcu  ,02417h,0006Ah          ;FD_R9_STEP_F50_B
 dw AptWrMcu  ,0A402h,  01Dh          ;FD_WINDOW_POSH (Horizontal Pos)
 dw AptWrMcu  ,0A403h,  004h          ;FD_WINDOW_HEIGHT
 dw AptWrMcu  ,0A404h,  010h          ;FD_MODE
 dw AptWrMcu  ,0A40Dh,  002h          ;FD_STAT_MIN
 dw AptWrMcu  ,0A40Eh,  003h          ;FD_STAT_MAX
 dw AptWrMcu  ,0A410h,  00Ah          ;FD_MIN_AMPLITUDE
 dw AptWrMcu  ,0A40Ch,  000h          ;UNDOC! RESERVED_FD_0C
.endif
 dw AptWrMcu  ,02703h,00100h  ;256    ;MODE_OUTPUT_WIDTH_A              ;\Size A
 dw AptWrMcu  ,02705h,000C0h  ;192    ;MODE_OUTPUT_HEIGHT_A             ;/ 256x192
 dw AptWrMcu  ,02707h,00280h  ;640    ;MODE_OUTPUT_WIDTH_B              ;\Size B
 dw AptWrMcu  ,02709h,001E0h  ;480    ;MODE_OUTPUT_HEIGHT_B             ;/ 640x480
 ;---
 dw AptWrMcu  ,02715h,00001h          ;MODE_SENSOR_ROW_SPEED_A          ;\
 dw AptWrMcu  ,02719h,0001Ah          ;MODE_SENSOR_FINE_CORRECTION_A    ;
 dw AptWrMcu  ,0271Bh,0006Bh          ;MODE_SENSOR_FINE_IT_MIN_A        ; Sensor A
 dw AptWrMcu  ,0271Dh,0006Bh          ;MODE_SENSOR_FINE_IT_MAX_MARGIN_A ;
 dw AptWrMcu  ,0271Fh,002C0h          ;MODE_SENSOR_FRAME_LENGTH_A       ;
 dw AptWrMcu  ,02721h,0034Bh          ;MODE_SENSOR_LINE_LENGTH_PCK_A    ;/
 ;---
 dw AptWrMcu  ,0A20Bh,  000h          ;AE_MIN_INDEX                     ;\AE min/max
 dw AptWrMcu  ,0A20Ch,  006h          ;AE_MAX_INDEX                     ;/
 ;skipping above two AE_MIN/MAX_INDEX makes conversion MUCH slower
 ;(and picture becomes MUCH brighter, with BETTER low-light quality)
 ;---
 dw AptWrMcu  ,0272Bh,00001h          ;MODE_SENSOR_ROW_SPEED_B          ;\
 dw AptWrMcu  ,0272Fh,0001Ah          ;MODE_SENSOR_FINE_CORRECTION_B    ;
 dw AptWrMcu  ,02731h,0006Bh          ;MODE_SENSOR_FINE_IT_MIN_B        ; Sensor B
 dw AptWrMcu  ,02733h,0006Bh          ;MODE_SENSOR_FINE_IT_MAX_MARGIN_B ;
 dw AptWrMcu  ,02735h,002C0h          ;MODE_SENSOR_FRAME_LENGTH_B       ;
 dw AptWrMcu  ,02737h,0034Bh          ;MODE_SENSOR_LINE_LENGTH_PCK_B    ;/
 ;---
.if InitAptDetails   ;WORKs if skipped
 dw AptWrMcu  ,02739h,00000h          ;MODE_CROP_X0_A                   ;\
 dw AptWrMcu  ,0273Bh,0027Fh          ;MODE_CROP_X1_A                   ; Crop A
 dw AptWrMcu  ,0273Dh,00000h          ;MODE_CROP_Y0_A                   ;
 dw AptWrMcu  ,0273Fh,001DFh          ;MODE_CROP_Y1_A                   ;/
 dw AptWrMcu  ,02747h,00000h          ;MODE_CROP_X0_B                   ;\
 dw AptWrMcu  ,02749h,0027Fh          ;MODE_CROP_X1_B                   ; Crop B
 dw AptWrMcu  ,0274Bh,00000h          ;MODE_CROP_Y0_B                   ;
 dw AptWrMcu  ,0274Dh,001DFh          ;MODE_CROP_Y1_B                   ;/
 dw AptWrMcu  ,0A40Dh,  002h          ;FD_STAT_MIN           (blah, already done above)
 dw AptWrMcu  ,0A410h,  00Ah          ;FD_MIN_AMPLITUDE      (blah, already done above)
 dw AptWrMcu  ,0A40Ch,  000h          ;UNDOC! RESERVED_FD_0C (blah, already done above)
 ;---
 dw AptWr     ,03658h,001F0h          ;P_RD_P0Q0  ;\                    ;\
 dw AptWr     ,0365Ah,059CCh          ;P_RD_P0Q1  ;                     ;
 dw AptWr     ,0365Ch,05232h          ;P_RD_P0Q2  ; P0                  ;
 dw AptWr     ,0365Eh,0006Dh          ;P_RD_P0Q3  ;                     ; P0..P4
 dw AptWr     ,03660h,08952h          ;P_RD_P0Q4  ;/                    ; Coefficients
 dw AptWr     ,03680h,03F8Ah          ;P_RD_P1Q0  ;\                    ; for Red
 dw AptWr     ,03682h,0D10Ch          ;P_RD_P1Q1  ;                     ;
 dw AptWr     ,03684h,05C90h          ;P_RD_P1Q2  ; P1                  ;
 dw AptWr     ,03686h,0D0B0h          ;P_RD_P1Q3  ;                     ;
 dw AptWr     ,03688h,0A614h          ;P_RD_P1Q4  ;/                    ;
 dw AptWr     ,036A8h,043B2h          ;P_RD_P2Q0  ;\                    ;
 dw AptWr     ,036AAh,04291h          ;P_RD_P2Q1  ;                     ;
 dw AptWr     ,036ACh,034B6h          ;P_RD_P2Q2  ; P2                  ;
 dw AptWr     ,036AEh,0F955h          ;P_RD_P2Q3  ;                     ;
 dw AptWr     ,036B0h,0F379h          ;P_RD_P2Q4  ;/                    ;
 dw AptWr     ,036D0h,023B2h          ;P_RD_P3Q0  ;\                    ;
 dw AptWr     ,036D2h,07630h          ;P_RD_P3Q1  ;                     ;
 dw AptWr     ,036D4h,0BA14h          ;P_RD_P3Q2  ; P3                  ;
 dw AptWr     ,036D6h,0C814h          ;P_RD_P3Q3  ;                     ;
 dw AptWr     ,036D8h,00FB9h          ;P_RD_P3Q4  ;/                    ;
 dw AptWr     ,036F8h,00E15h          ;P_RD_P4Q0  ;\                    ;
 dw AptWr     ,036FAh,0D055h          ;P_RD_P4Q1  ;                     ;
 dw AptWr     ,036FCh,0CFBAh          ;P_RD_P4Q2  ; P4                  ;
 dw AptWr     ,036FEh,0109Ah          ;P_RD_P4Q3  ;                     ;
 dw AptWr     ,03700h,07FBDh          ;P_RD_P4Q4  ;/                    ;/
 dw AptWr     ,0364Eh,002F0h          ;P_GR_P0Q0  ;\                    ;\
 dw AptWr     ,03650h,03D8Ah          ;P_GR_P0Q1  ;                     ;
 dw AptWr     ,03652h,02512h          ;P_GR_P0Q2  ; P0                  ;
 dw AptWr     ,03654h,06049h          ;P_GR_P0Q3  ;                     ; P0..P4
 dw AptWr     ,03656h,03B2Fh          ;P_GR_P0Q4  ;/                    ; Coefficients
 dw AptWr     ,03676h,08F06h          ;P_GR_P1Q0  ;\                    ; for Green1
 dw AptWr     ,03678h,0FCECh          ;P_GR_P1Q1  ;                     ;
 dw AptWr     ,0367Ah,03710h          ;P_GR_P1Q2  ; P1                  ;
 dw AptWr     ,0367Ch,0B62Dh          ;P_GR_P1Q3  ;                     ;
 dw AptWr     ,0367Eh,0C834h          ;P_GR_P1Q4  ;/                    ;
 dw AptWr     ,0369Eh,01072h          ;P_GR_P2Q0  ;\                    ;
 dw AptWr     ,036A0h,018F0h          ;P_GR_P2Q1  ;                     ;
 dw AptWr     ,036A2h,03156h          ;P_GR_P2Q2  ; P2                  ;
 dw AptWr     ,036A4h,0CCF4h          ;P_GR_P2Q3  ;                     ;
 dw AptWr     ,036A6h,0E8F9h          ;P_GR_P2Q4  ;/                    ;
 dw AptWr     ,036C6h,023F2h          ;P_GR_P3Q0  ;\                    ;
 dw AptWr     ,036C8h,0E40Fh          ;P_GR_P3Q1  ;                     ;
 dw AptWr     ,036CAh,0A814h          ;P_GR_P3Q2  ; P3                  ;
 dw AptWr     ,036CCh,00E52h          ;P_GR_P3Q3  ;                     ;
 dw AptWr     ,036CEh,05078h          ;P_GR_P3Q4  ;/                    ;
 dw AptWr     ,036EEh,01A75h          ;P_GR_P4Q0  ;\                    ;
 dw AptWr     ,036F0h,0B254h          ;P_GR_P4Q1  ;                     ;
 dw AptWr     ,036F2h,0C23Ah          ;P_GR_P4Q2  ; P4                  ;
 dw AptWr     ,036F4h,07CD8h          ;P_GR_P4Q3  ;                     ;
 dw AptWr     ,036F6h,05C1Dh          ;P_GR_P4Q4  ;/                    ;/
 dw AptWr     ,03662h,00230h          ;P_BL_P0Q0  ;\                    ;\
 dw AptWr     ,03664h,08D67h          ;P_BL_P0Q1  ;                     ;
 dw AptWr     ,03666h,01F32h          ;P_BL_P0Q2  ; P0                  ;
 dw AptWr     ,03668h,0ACECh          ;P_BL_P0Q3  ;                     ; P0..P4
 dw AptWr     ,0366Ah,003AEh          ;P_BL_P0Q4  ;/                    ; Coefficients
 dw AptWr     ,0368Ah,09ECCh          ;P_BL_P1Q0  ;\                    ; for Blue
 dw AptWr     ,0368Ch,09B2Dh          ;P_BL_P1Q1  ;                     ;
 dw AptWr     ,0368Eh,00631h          ;P_BL_P1Q2  ; P1                  ;
 dw AptWr     ,03690h,0224Fh          ;P_BL_P1Q3  ;                     ;
 dw AptWr     ,03692h,0BA54h          ;P_BL_P1Q4  ;/                    ;
 dw AptWr     ,036B2h,00392h          ;P_BL_P2Q0  ;\                    ;
 dw AptWr     ,036B4h,03E50h          ;P_BL_P2Q1  ;                     ;
 dw AptWr     ,036B6h,027B6h          ;P_BL_P2Q2  ; P2                  ;
 dw AptWr     ,036B8h,0FC54h          ;P_BL_P2Q3  ;                     ;
 dw AptWr     ,036BAh,0D9F9h          ;P_BL_P2Q4  ;/                    ;
 dw AptWr     ,036DAh,04112h          ;P_BL_P3Q0  ;\                    ;
 dw AptWr     ,036DCh,029F1h          ;P_BL_P3Q1  ;                     ;
 dw AptWr     ,036DEh,096D4h          ;P_BL_P3Q2  ; P3                  ;
 dw AptWr     ,036E0h,0F934h          ;P_BL_P3Q3  ;                     ;
 dw AptWr     ,036E2h,02B18h          ;P_BL_P3Q4  ;/                    ;
 dw AptWr     ,03702h,02615h          ;P_BL_P4Q0  ;\                    ;
 dw AptWr     ,03704h,0E9D4h          ;P_BL_P4Q1  ;                     ;
 dw AptWr     ,03706h,0B4DAh          ;P_BL_P4Q2  ; P4                  ;
 dw AptWr     ,03708h,03899h          ;P_BL_P4Q3  ;                     ;
 dw AptWr     ,0370Ah,052BDh          ;P_BL_P4Q4  ;/                    ;/
 dw AptWr     ,0366Ch,001B0h          ;P_GB_P0Q0  ;\                    ;\
 dw AptWr     ,0366Eh,069EAh          ;P_GB_P0Q1  ;                     ;
 dw AptWr     ,03670h,02432h          ;P_GB_P0Q2  ; P0                  ;
 dw AptWr     ,03672h,030EEh          ;P_GB_P0Q3  ;                     ; P0..P4
 dw AptWr     ,03674h,0056Fh          ;P_GB_P0Q4  ;/                    ; Coefficients
 dw AptWr     ,03694h,0B52Ah          ;P_GB_P1Q0  ;\                    ; for Green2
 dw AptWr     ,03696h,0C12Dh          ;P_GB_P1Q1  ;                     ;
 dw AptWr     ,03698h,04650h          ;P_GB_P1Q2  ; P1                  ;
 dw AptWr     ,0369Ah,0640Fh          ;P_GB_P1Q3  ;                     ;
 dw AptWr     ,0369Ch,0BAB4h          ;P_GB_P1Q4  ;/                    ;
 dw AptWr     ,036BCh,00CF2h          ;P_GB_P2Q0  ;\                    ;
 dw AptWr     ,036BEh,00351h          ;P_GB_P2Q1  ;                     ;
 dw AptWr     ,036C0h,036F6h          ;P_GB_P2Q2  ; P2                  ;
 dw AptWr     ,036C2h,0A3F5h          ;P_GB_P2Q3  ;                     ;
 dw AptWr     ,036C4h,0F2B9h          ;P_GB_P2Q4  ;/                    ;
 dw AptWr     ,036E4h,02B12h          ;P_GB_P3Q0  ;\                    ;
 dw AptWr     ,036E6h,0102Fh          ;P_GB_P3Q1  ;                     ;
 dw AptWr     ,036E8h,0E0F4h          ;P_GB_P3Q2  ; P3                  ;
 dw AptWr     ,036EAh,088F5h          ;P_GB_P3Q3  ;                     ;
 dw AptWr     ,036ECh,056B8h          ;P_GB_P3Q4  ;/                    ;
 dw AptWr     ,0370Ch,01EF5h          ;P_GB_P4Q0  ;\                    ;
 dw AptWr     ,0370Eh,09075h          ;P_GB_P4Q1  ;                     ;
 dw AptWr     ,03710h,0CC9Ah          ;P_GB_P4Q2  ; P4                  ;
 dw AptWr     ,03712h,04D59h          ;P_GB_P4Q3  ;                     ;
 dw AptWr     ,03714h,06BBDh          ;P_GB_P4Q4  ;/                    ;/
 dw AptWr     ,03644h,00144h          ;POLY_ORIGIN_C (Center Column)
 dw AptWr     ,03642h,000F4h          ;POLY_ORIGIN_R (Center Row)
.endif
 ;---
 dw AptSet    ,03210h,00008h          ;COLOR_PIPELINE_CONTROL (set bit3=1, Enable PGA pixel shading correction)
 ;---
.if 01                      ;<-- hangs if skipped?  !!
 dw AptWrMcu  ,0A208h,  000h          ;UNDOC! RESERVED_AE_08
 dw AptWrMcu  ,0A24Ch,  020h          ;AE_TARGETBUFFERSPEED
 dw AptWrMcu  ,0A24Fh,  070h          ;AE_BASETARGET
.endif
.if InitAptDetails   ;WORKs if skipped
 dw AptWrMcu  ,0A109h,  020h          ;SEQ_AE_FASTBUFF
 dw AptWrMcu  ,0A10Ah,  001h          ;SEQ_AE_FASTSTEP
 dw AptWrMcu  ,0A20Bh,  000h          ;AE_MIN_INDEX     (blah, already set above)
 dw AptWrMcu  ,0A20Dh,  030h          ;AE_MIN_VIRTGAIN
 dw AptWrMcu  ,0A20Eh,  080h          ;AE_MAX_VIRTGAIN
 dw AptWrMcu  ,0A24Ah,  028h          ;AE_TARGETMIN
 dw AptWrMcu  ,0A24Bh,  0C0h          ;AE_TARGETMAX
 dw AptWrMcu  ,0A75Dh,  000h          ;MODE_Y_RGB_OFFSET_A            ;\Offset A/B
 dw AptWrMcu  ,0A75Eh,  000h          ;MODE_Y_RGB_OFFSET_B            ;/
 dw AptWrMcu  ,0A11Dh,  001h          ;SEQ_PREVIEW_1_AE
 dw AptWrMcu  ,0A129h,  001h          ;SEQ_PREVIEW_3_AE
 dw AptWrMcu  ,0A207h,  00Ch          ;AE_GATE
 dw AptWrMcu  ,0A35Dh,  07Bh          ;AWB_STEADY_BGAIN_OUT_MIN
 dw AptWrMcu  ,0A35Eh,  085h          ;AWB_STEADY_BGAIN_OUT_MAX
 dw AptWrMcu  ,0A35Fh,  07Eh          ;AWB_STEADY_BGAIN_IN_MIN
 dw AptWrMcu  ,0A360h,  082h          ;AWB_STEADY_BGAIN_IN_MAX
 dw AptWrMcu  ,02361h,00040h          ;UNDOC! RESERVED_AWB_61
 dw AptWrMcu  ,0A10Bh,  008h          ;SEQ_AWB_CONTBUFF
 dw AptWrMcu  ,0A10Ch,  002h          ;SEQ_AWB_CONTSTEP
 dw AptWrMcu  ,0A302h,  000h          ;AWB_WINDOW_POS
 dw AptWrMcu  ,0A303h,  0FFh          ;AWB_WINDOW_SIZE
 dw AptWr     ,0323Eh,0C22Ch          ;UNDOC! RESERVED_SOC1_323E
 dw AptWr     ,03244h,00335h          ;UNDOC! RESERVED_SOC1_3244
 dw AptWr     ,03240h,06214h          ;UNDOC! RESERVED_SOC1_3240
 dw AptWr     ,031E0h,00001h          ;UNDOC! RESERVED_CORE_31E0 .. PIX_DEF_ID ?
 dw AptWr     ,0322Ah,00004h          ;UNDOC! RESERVED_SOC1_322A
 dw AptWr     ,035B0h,0049Dh          ;UNDOC! RESERVED_SOC2_35B0
 dw AptWrMcu  ,0AB04h,  020h          ;HG_MAX_DLEVEL
 dw AptWrMcu  ,0AB06h,  003h          ;HG_PERCENT
 dw AptWrMcu  ,0AB37h,  003h          ;HG_GAMMA_MORPH_CTRL
 dw AptWrMcu  ,02B28h,01388h          ;HG_LL_BRIGHTNESSSTART
 dw AptWrMcu  ,02B2Ah,04E20h          ;HG_LL_BRIGHTNESSSTOP
 dw AptWrMcu  ,02B38h,00100h          ;HG_GAMMASTARTMORPH
 dw AptWrMcu  ,02B3Ah,020CCh          ;HG_GAMMASTOPMORPH
 dw AptWrMcu  ,0AB21h,  008h          ;UNDOC! RESERVED_HG_21
 dw AptWrMcu  ,0AB22h,  002h          ;HG_LL_APCORR1
 dw AptWrMcu  ,0AB23h,  000h          ;UNDOC! RESERVED_HG_23
 dw AptWrMcu  ,0AB25h,  014h          ;HG_LL_INTERPTHRESH2
 dw AptWrMcu  ,0AB26h,  000h          ;HG_LL_APCORR2
 dw AptWrMcu  ,0AB27h,  004h          ;HG_LL_APTHRESH2
 dw AptWrMcu  ,0AB1Fh,  0C7h          ;HG_LLMODE
 dw AptWrMcu  ,0AB31h,  01Eh          ;HG_NR_STOP_G (huh, only "G", not "R+G+B"?)
.endif
 ;--- below is done separately for each camera (not done together at once)
 dw AptWrMcuDiff   ,02717h,00024h,00025h  ;MODE_SENSOR_READ_MODE_A    ;diff 7Ah/78h (diff=hflip)
 dw AptWrMcuDiff   ,0272Dh,00024h,00025h  ;MODE_SENSOR_READ_MODE_B    ;             (diff=hflip)
 dw AptWrMcuDiff   ,0A202h,  022h,  000h  ;AE_WINDOW_POS
 dw AptWrMcuDiff   ,0A203h,  0BBh,  0FFh  ;AE_WINDOW_SIZE
.if InitAptDetails   ;WORKs if skipped
 dw AptWr          ,03032h,00100h         ;DIGITAL_GAIN_GREENR
 dw AptWr          ,03034h,00100h         ;DIGITAL_GAIN_RED
 dw AptWr          ,03036h,00100h         ;DIGITAL_GAIN_BLUE
 dw AptWr          ,03038h,00100h         ;DIGITAL_GAIN_GREENB
.endif
 ;---
 dw AptSet         ,00016h,00020h         ;CLOCKS_CONTROL (set bit5=1, reserved, UNDOC!)
 ;---
.if InitAptDetails   ;WORKs if skipped
 dw AptWrMcu       ,0A24Fh,  070h         ;AE_BASETARGET    (blah, already set above)
 dw AptWrMcu       ,0A11Dh,  001h         ;SEQ_PREVIEW_1_AE (blah, already set above)
 dw AptWrMcu       ,0A129h,  001h         ;SEQ_PREVIEW_3_AE (blah, already set above)
 dw AptWrMcu       ,0AB3Ch,  000h         ;HG_GAMMA_TABLE_A_0  ;\
 dw AptWrMcuDiff   ,0AB3Dh,  00Ch,  00Bh  ;HG_GAMMA_TABLE_A_1  ;
 dw AptWrMcuDiff   ,0AB3Eh,  022h,  020h  ;HG_GAMMA_TABLE_A_2  ;
 dw AptWrMcuDiff   ,0AB3Fh,  03Eh,  03Bh  ;HG_GAMMA_TABLE_A_3  ;
 dw AptWrMcuDiff   ,0AB40h,  05Dh,  05Bh  ;HG_GAMMA_TABLE_A_4  ;
 dw AptWrMcuDiff   ,0AB41h,  073h,  072h  ;HG_GAMMA_TABLE_A_5  ;
 dw AptWrMcuDiff   ,0AB42h,  085h,  085h  ;HG_GAMMA_TABLE_A_6  ;
 dw AptWrMcuDiff   ,0AB43h,  094h,  096h  ;HG_GAMMA_TABLE_A_7  ;
 dw AptWrMcuDiff   ,0AB44h,  0A2h,  0A4h  ;HG_GAMMA_TABLE_A_8  ;
 dw AptWrMcuDiff   ,0AB45h,  0AEh,  0B1h  ;HG_GAMMA_TABLE_A_9  ;
 dw AptWrMcuDiff   ,0AB46h,  0B9h,  0BCh  ;HG_GAMMA_TABLE_A_10 ;
 dw AptWrMcuDiff   ,0AB47h,  0C3h,  0C6h  ;HG_GAMMA_TABLE_A_11 ;
 dw AptWrMcuDiff   ,0AB48h,  0CDh,  0D0h  ;HG_GAMMA_TABLE_A_12 ;
 dw AptWrMcuDiff   ,0AB49h,  0D7h,  0D9h  ;HG_GAMMA_TABLE_A_13 ;
 dw AptWrMcuDiff   ,0AB4Ah,  0DFh,  0E2h  ;HG_GAMMA_TABLE_A_14 ;
 dw AptWrMcuDiff   ,0AB4Bh,  0E8h,  0EAh  ;HG_GAMMA_TABLE_A_15 ;
 dw AptWrMcuDiff   ,0AB4Ch,  0F0h,  0F1h  ;HG_GAMMA_TABLE_A_16 ;
 dw AptWrMcu       ,0AB4Dh,  0F8h         ;HG_GAMMA_TABLE_A_17 ;
 dw AptWrMcu       ,0AB4Eh,  0FFh         ;HG_GAMMA_TABLE_A_18 ;/
 dw AptWrMcu       ,0AB4Fh,  000h         ;HG_GAMMA_TABLE_B_0  ;\
 dw AptWrMcuDiff   ,0AB50h,  00Ch,  00Bh  ;HG_GAMMA_TABLE_B_1  ;
 dw AptWrMcuDiff   ,0AB51h,  026h,  023h  ;HG_GAMMA_TABLE_B_2  ;
 dw AptWrMcuDiff   ,0AB52h,  050h,  04Dh  ;HG_GAMMA_TABLE_B_3  ;
 dw AptWrMcuDiff   ,0AB53h,  072h,  071h  ;HG_GAMMA_TABLE_B_4  ;
 dw AptWrMcuDiff   ,0AB54h,  088h,  088h  ;HG_GAMMA_TABLE_B_5  ;
 dw AptWrMcuDiff   ,0AB55h,  098h,  09Ah  ;HG_GAMMA_TABLE_B_6  ;
 dw AptWrMcuDiff   ,0AB56h,  0A6h,  0A9h  ;HG_GAMMA_TABLE_B_7  ;
 dw AptWrMcuDiff   ,0AB57h,  0B2h,  0B5h  ;HG_GAMMA_TABLE_B_8  ;
 dw AptWrMcuDiff   ,0AB58h,  0BCh,  0C0h  ;HG_GAMMA_TABLE_B_9  ;
 dw AptWrMcuDiff   ,0AB59h,  0C6h,  0C9h  ;HG_GAMMA_TABLE_B_10 ;
 dw AptWrMcuDiff   ,0AB5Ah,  0CFh,  0D2h  ;HG_GAMMA_TABLE_B_11 ;
 dw AptWrMcuDiff   ,0AB5Bh,  0D7h,  0DAh  ;HG_GAMMA_TABLE_B_12 ;
 dw AptWrMcuDiff   ,0AB5Ch,  0DFh,  0E1h  ;HG_GAMMA_TABLE_B_13 ;
 dw AptWrMcuDiff   ,0AB5Dh,  0E6h,  0E8h  ;HG_GAMMA_TABLE_B_14 ;
 dw AptWrMcuDiff   ,0AB5Eh,  0EDh,  0EEh  ;HG_GAMMA_TABLE_B_15 ;
 dw AptWrMcuDiff   ,0AB5Fh,  0F3h,  0F4h  ;HG_GAMMA_TABLE_B_16 ;
 dw AptWrMcuDiff   ,0AB60h,  0F9h,  0FAh  ;HG_GAMMA_TABLE_B_17 ;
 dw AptWrMcu       ,0AB61h,  0FFh         ;HG_GAMMA_TABLE_B_18 ;/
 dw AptWrMcu       ,02306h,0038Ah         ;AWB_CCM_L_0   ;\                ;\
 dw AptWrMcu       ,02308h,0FDDDh         ;AWB_CCM_L_1   ; K11,K12,K13     ;
 dw AptWrMcu       ,0230Ah,0003Ah         ;AWB_CCM_L_2   ;/                ;
 dw AptWrMcu       ,0230Ch,0FF42h         ;AWB_CCM_L_3   ;\                ; Left
 dw AptWrMcu       ,0230Eh,002D7h         ;AWB_CCM_L_4   ; K21,K22,K23     ; CCM
 dw AptWrMcu       ,02310h,0FF67h         ;AWB_CCM_L_5   ;/                ; Matrix
 dw AptWrMcu       ,02312h,0FF5Dh         ;AWB_CCM_L_6   ;\                ;
 dw AptWrMcu       ,02314h,0FD98h         ;AWB_CCM_L_7   ; K31,K32,K33     ;
 dw AptWrMcu       ,02316h,00437h         ;AWB_CCM_L_8   ;/                ;
 dw AptWrMcu       ,02318h,00015h         ;AWB_CCM_L_9   ;-Red/Green Gain  ;
 dw AptWrMcu       ,0231Ah,0003Ch         ;AWB_CCM_L_10  ;-Blue/Green Gain ;/
 dw AptWrMcu       ,0231Ch,0FF30h         ;AWB_CCM_RL_0  ;\                ;\
 dw AptWrMcu       ,0231Eh,00092h         ;AWB_CCM_RL_1  ; D11,D12,D13     ;
 dw AptWrMcu       ,02320h,0FFD1h         ;AWB_CCM_RL_2  ;/                ;
 dw AptWrMcu       ,02322h,00041h         ;AWB_CCM_RL_3  ;\                ; Delta
 dw AptWrMcu       ,02324h,0FFD4h         ;AWB_CCM_RL_4  ; D21,D22,D23     ; CCM
 dw AptWrMcu       ,02326h,0FFB8h         ;AWB_CCM_RL_5  ;/                ; Matrix
 dw AptWrMcu       ,02328h,00085h         ;AWB_CCM_RL_6  ;\                ;
 dw AptWrMcu       ,0232Ah,00199h         ;AWB_CCM_RL_7  ; D31,D32,D33     ;
 dw AptWrMcu       ,0232Ch,0FDE2h         ;AWB_CCM_RL_8  ;/                ;
 dw AptWrMcu       ,0232Eh,00010h         ;AWB_CCM_RL_9  ;-Red/Green Gain  ;
 dw AptWrMcu       ,02330h,0FFE9h         ;AWB_CCM_RL_10 ;-Blue/Green Gain ;/
 dw AptWrMcu       ,0AB20h,  068h         ;HG_LL_SAT1
 dw AptWrMcu       ,0AB24h,  028h         ;HG_LL_SAT2
 dw AptWrMcu       ,0A365h,  020h         ;AWB_X0
 dw AptWrMcu       ,0A366h,  0F0h         ;AWB_KR_L
 dw AptWrMcu       ,0A367h,  0A0h         ;AWB_KG_L
 dw AptWrMcu       ,0A368h,  060h         ;AWB_KB_L
 dw AptWrMcu       ,0A369h,  082h         ;AWB_KR_R
 dw AptWrMcu       ,0A36Ah,  082h         ;AWB_KG_R
 dw AptWrMcu       ,0A36Bh,  082h         ;AWB_KB_R
 dw AptWrMcu       ,0A363h,  0D5h         ;AWB_TG_MIN0
 dw AptWrMcu       ,0A364h,  0EDh         ;AWB_TG_MAX0
 dw AptWrMcu       ,0A34Ah,  076h         ;AWB_GAIN_MIN
 dw AptWrMcu       ,0A34Bh,  0D9h         ;AWB_GAIN_MAX
 dw AptWrMcu       ,0A34Ch,  060h         ;AWB_GAINMIN_B
 dw AptWrMcu       ,0A34Dh,  0C7h         ;AWB_GAINMAX_B
.endif
 dw AptWrMcu       ,0A115h,  072h         ;SEQ_CAP_MODE  (was already somehow manipulated)
 dw AptWrMcu       ,0A11Fh,  001h         ;SEQ_PREVIEW_1_AWB
 ;---
.if InitAptDetails   ;WORKs if skipped
 dw AptSet         ,00016h,00000h         ;CLOCKS_CONTROL (blah, nothing changed here)
.endif
 ;---
 dw AptWrDiff      ,0326Ch,00900h,01000h  ;APERTURE_PARAMETERS (diff... XXX)
 dw AptWrMcuDiff   ,0AB22h,  001h,  002h  ;HG_LL_APCORR1       (diff... XXX)
.if InitAptDetails   ;WORKs if skipped
 dw AptWrMcuOnly7Ah,0A202h,  022h         ;AE_WINDOW_POS       (device 7Ah only?) (and blah, already set so above!)
 dw AptWrMcuOnly7Ah,0A203h,  0BBh         ;AE_WINDOW_SIZE      (device 7Ah only?) (and blah, already set so above!)
.endif
 ;---
 dw AptWrMcu       ,0A103h,  006h         ;SEQ_CMD (06h=RefreshMode)
 dw AptWaitMcuOther,0A103h,  006h         ;SEQ_CMD (wait to become ZERO instead of 06h)
 ;---
 dw AptWrMcu       ,0A103h,  005h         ;SEQ_CMD (05h=Refresh)
 dw AptWaitMcuOther,0A103h,  005h         ;SEQ_CMD (wait to become ZERO instead of 05h)
 ;---
.if InitAptDetails   ;WORKs if skipped
 dw AptSet         ,00018h,00001h         ;STANDBY_CONTROL (set bit0=1=Standby)
 dw AptWaitSet     ,00018h,04000h         ;STANDBY_CONTROL (wait for bit14=1=StandbyDone)
 dw AptWaitClr     ,0301Ah,00004h         ;UNDOC! RESERVED? ALIAS of 3022h? (bit2=0=StandbyDone)
.endif
 ;---
 dw -1
 .align 4
;------------------
aptina_code_list_activate:   ;(as from system flaw)
;--- below ONLY for device 78h (=active camera?)
.if InitAptDetails   ;WORKs if skipped
 dw AptSet         ,00016h,00000h         ;CLOCKS_CONTROL (blah, nothing changed here)
 dw AptRdMcu       ,0A117h,0              ;SEQ_PREVIEW_0_AE   ;\
 dw AptRdMcu       ,0A11Dh,0              ;SEQ_PREVIEW_1_AE   ; backup old? uh, is that reading possible during standby?
 dw AptRdMcu       ,0A11Fh,0              ;SEQ_PREVIEW_1_AWB  ;/
 dw AptRd          ,03012h,0              ;COARSE_INTEGRATION_TIME (Y Time)
 dw AptRd          ,03028h,0              ;ANALOGUE_GAIN_CODE_GLOBAL  ;\
 dw AptRd          ,0302Ah,0              ;ANALOGUE_GAIN_CODE_GREENR  ; Analogue Gain Codes
 dw AptRd          ,0302Ch,0              ;ANALOGUE_GAIN_CODE_RED     ; with 3bit fraction
 dw AptRd          ,0302Eh,0              ;ANALOGUE_GAIN_CODE_BLUE    ; (0..007Fh, def=000Bh)
 dw AptRd          ,03030h,0              ;ANALOGUE_GAIN_CODE_GREENB  ;/
 dw AptWrMcu       ,0A117h,  000h         ;SEQ_PREVIEW_0_AE   ;\
 dw AptWrMcu       ,0A11Dh,  000h         ;SEQ_PREVIEW_1_AE   ; temporarily off?
 dw AptWrMcu       ,0A11Fh,  000h         ;SEQ_PREVIEW_1_AWB  ;/
.endif
 ;---
 dw AptClr         ,00018h,00001h         ;STANDBY_CONTROL (bit0=0=wakeup)
 dw AptWaitClr     ,00018h,04000h         ;STANDBY_CONTROL (wait for bit14=0=WakeupDone)
 dw AptWaitSet     ,0301Ah,00004h         ;UNDOC! RESERVED? ALIAS of 3022h? (bit2=1=WakeupDone)
 ;---
.if InitAptDetails   ;WORKs if skipped
 dw AptWr          ,031E0h,00001h         ;UNDOC! RESERVED_CORE_31E0 .. PIX_DEF_ID ? (blah, already set so in init)
.endif
 ;---
 dw AptWr          ,03012h,00000h +10h  +800h    ;COARSE_INTEGRATION_TIME (Y Time) (raises brightness !!!)
   ;above COARSE=800h is required at night for good brightness when sitting in a room with a small bulb ?
   ;but at normal daylight (without much sunshine), COARSE 0,10h,800h, doesn't make much of a difference?
   ;see also AE_MIN/MAX_INDEX
 ;---
.if InitAptDetails   ;WORKs if skipped
 dw AptWr          ,03028h,00000h         ;ANALOGUE_GAIN_CODE_GLOBAL  ;\
 dw AptWr          ,0302Ah,00000h         ;ANALOGUE_GAIN_CODE_GREENR  ; Analogue Gain Codes
 dw AptWr          ,0302Ch,00000h         ;ANALOGUE_GAIN_CODE_RED     ; with 3bit fraction
 dw AptWr          ,0302Eh,00000h         ;ANALOGUE_GAIN_CODE_BLUE    ; (0..007Fh, def=000Bh)
 dw AptWr          ,03030h,00000h         ;ANALOGUE_GAIN_CODE_GREENB  ;/
 dw AptWrMcu       ,0A117h,  000h         ;SEQ_PREVIEW_0_AE   ;\
 dw AptWrMcu       ,0A11Dh,  001h         ;SEQ_PREVIEW_1_AE   ; restore old?
 dw AptWrMcu       ,0A11Fh,  001h         ;SEQ_PREVIEW_1_AWB  ;/
 dw AptWrMcu       ,0A103h,  006h         ;SEQ_CMD (06h=RefreshMode)
 dw AptWaitMcuOther,0A103h,  006h         ;SEQ_CMD (wait to become ZERO instead of 06h)
 dw AptWrMcu       ,0A103h,  005h         ;SEQ_CMD (05h=Refresh)
 dw AptWaitMcuOther,0A103h,  005h         ;SEQ_CMD (wait to become ZERO instead of 05h)
.endif
 dw AptSet         ,0001Ah,00200h         ;RESET_AND_MISC_CONTROL (bit9=1=ParallelEnable)
 ;---
 dw -1
 .align 4
;------------------
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty

Arisotura
Posts: 16
Joined: Sun May 19, 2019 7:01 am

Re: Trying to get the DSi cameras working

Post by Arisotura » Sun Oct 25, 2020 5:16 am

thanks! I'll give it a try and let you know.

Arisotura
Posts: 16
Joined: Sun May 19, 2019 7:01 am

Re: Trying to get the DSi cameras working

Post by Arisotura » Sun Oct 25, 2020 7:25 pm

well, my init procedure does include all the 'essential' operations your code is doing, and yet, it's still freezing.

I figure that might be because I'm only initializing one camera, somehow; maybe I'm supposed to init both simultaneously, like official apps do? that's all I can see, I'm entirely out of ideas there. lest of trying a straight copypaste of your init list...

EDIT- initializing both cameras simultaneously didn't help, either.

but there must be something screwy with I2C comm. I'm trying to read some registers from the cameras after init, and the results for some registers differ depending on previous reads, and registers for camera 0x78 tend to be ORed with 0x03FF, which is weird.

EDIT- okay, seems I fixed the I2C fuckiness (turns out you don't set the ACK bit when receiving the last byte during a read). that seems to have fixed the hang, let's see if I can actually get something done now.

EDIT- well, I managed to have one camera frame displayed onscreen. thank you for your code, that helped me figure out what the issue was (after much fumbling, heh). now I can focus on what I originally wanted to do: reversing the specifics of the camera transfer hardware.

basically, setting bit15 in CAM_CNT and setting up the NDMA transfer will allow one frame to be transferred, but that's about it. I noticed some more oddities, for example, the DSi camera app won't set CAM_CNT.bit15 until it has received some IRQ. I want to see what the conditions are, because obviously, the frame transfer is working well on its own, but the DRQ bit in CAM_CNT isn't getting set on its own...

EDIT- some quick precisions on this before I go to bed

* camera IRQ is thrown every time the camera has a new frame it can transfer ('camera vblank' I guess)
* DRQ bit gets set when the internal buffer is full, ie. when enabling the transfer in CAM_CNT but never actually transferring shit
* have yet to figure out how big the buffer is
* DMA is triggered when CAM_CNT.bit15 is set and when the buffer is full enough (sure enough, the 'DMA scanlines' setting in CAM_CNT)

wasn't sure how all of these interacted, so maybe now I can make an implementation that won't make the camera app shit itself... we'll see tomorrow.

nocash
Posts: 1259
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: Trying to get the DSi cameras working

Post by nocash » Mon Oct 26, 2020 12:16 am

Yeah, some I2C device seem to be using ACK to indicate whether to "request to receive more data". Not sure if that is standarized, or if it depends on the device/manufacturer. And not sure what it is good for (slightly earlier transfer-stop notification than stop condition)? Anyways, glad that you got it working!

Are you planning to support USB cameras in the emulator? And if yes, for which OS, with which software interface? I had tried it in no$gba some years ago using DirectShow (which seemed to be the most standard backwards compatible way). The default behaviour of DirectShow is:

Code: Select all

Camera Source ---> optional filter(s) ---> ActiveMovie Window
To process the camera image in software, one is supposed to add two filters (Sample Grabber to receive the bitmap, and Null Filter to prevent the ugly ActiveMovie Window being displayed):

Code: Select all

Camera Source ---> Sample Grabber Filter ---> Null Filter
The problem is that those two filter don't seem to be part of the window OS, and instead microsoft seems to want people to write those filters themselves using some kind of microsoft SDK (without having that SDK, it appears to impossible to use DirectShow, especially when coding in raw ASM, so I finally gave up on that part).

I guess anything newer than DirectShow is coming up with yet more nasty issues, and/or require to include 500MByte of driver binaries ; )
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty

Arisotura
Posts: 16
Joined: Sun May 19, 2019 7:01 am

Re: Trying to get the DSi cameras working

Post by Arisotura » Mon Oct 26, 2020 6:26 am

heh.

well, regarding melonDS, we use the Qt framework. it's not what I like the most, but it lets us port melonDS to other platforms without too much trouble (well, less trouble than the existing alternatives, and I've studied them long and hard).

from what I could see, Qt does support camera input, via the QCamera class. I'm not sure how good that is, though. we use SDL2 as a sorta-complement library for things like audio support and joystick input, because Qt isn't very good at these things. (that's the thing about Qt, it kinda tries to cover everything, but without being particularly good at it. also it's huge)

SDL2 itself doesn't seem to have any camera support however. I found a sample that uses v4l2 but that seems to be specific to Linux.

shrug.

we might also want alternate camera input modes, akin to what we have for microphone input. like feeding a still picture or a video file, but that's another can of worms.

Arisotura
Posts: 16
Joined: Sun May 19, 2019 7:01 am

Re: Trying to get the DSi cameras working

Post by Arisotura » Mon Oct 26, 2020 8:28 am

so, my observations so far on the whole 'camera buffer' thing:

* when doing manual reading of the camera picture: setting the CAM_CNT DMA threshold to 0 lets me read two blocks instead of one
* this, even if I use manual DMA transfers instead of reading it word per word, which strikes me as weird, manual DMA would hardly be slower than the automated NDMA transfer, unless NDMA is able to use some magical faster bus?
* nope, not even NDMA is fast enough
* from what I gather, the DRQ bit is set when the amount of data in the buffer reaches the DMA threshold, and that is what triggers a DMA transfer
* nope. DRQ bit is set on overflow as well as underflow (if DMA threshold is lower than DMA block size), it's an error bit
* transfer stops presumably when the buffer overflows, atleast until you set bit5 in CAM_CNT
* no idea yet how big the buffer is
* haven't found a way to read the current buffer level, either

it's weird because if I measure the time between setting CAM_CNT.bit15 and the DRQ bit getting set, it's always some ugly number (like 2214203 cycles), and has no relation to the DMA threshold. so really, what's the DRQ bit about, is it more like some overflow bit?

measurements made by making the camera DMA read from timer registers instead of 0x04004204, show the following:
* when DMA threshold is 0: transfer starts 708572 cycles after it's enabled
* when DMA threshold is 3: 730944 cycles.
* it takes 2 cycles per word.

beh, the timing are just weird.

I also couldn't get DMA thresholds above 3 to work. probably has to do with the PAD_SLEW register on the camera side.

lidnariq
Posts: 9850
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Trying to get the DSi cameras working

Post by lidnariq » Mon Oct 26, 2020 10:55 am

Arisotura wrote:
Sun Oct 25, 2020 7:25 pm
EDIT- okay, seems I fixed the I2C fuckiness (turns out you don't set the ACK bit when receiving the last byte during a read).
This stung me the first time I wrote an I²C host. The endpoint wouldn't listen to a STOP if I'd acknowledged a byte, and would talk all over subsequent communication.

This is part of the standard, but kind of hidden:
NXP UM10204 §3.1.6 wrote:There are five conditions that lead to the generation of a NACK: [...] 5. A master-receiver must signal the end of the transfer to the slave transmitter.

nocash
Posts: 1259
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: Trying to get the DSi cameras working

Post by nocash » Mon Oct 26, 2020 1:41 pm

CAM_CNT.bit4 in gbatek is currently described as "Data request? or Data overrun? or so?".
If you are also thinking that it is just an error flag... okay, I'll change it to "Data overrun/underrun error".

I think there might be no buffer at all at camera side, ie. it does probably just output pixels on the fly.
If that's right, the PAD_SLEW register (in the camera) can hardly affect the receive buffer size (in the console).

The receive buffer size... well it's apparently selected via CAM_CNT.bit0-3. Setting that to 3 (default) will receive 3+1 scanlines, usually with 256 pixels. So the receive buffer would be 1024 pixels, or probably twice as much for receiving another block while DMAing the previous block.

The question would be what happens when using horizontal resolutions other than 256 pixels:
For 128-pixel scanlines... maybe you can receive up to 8 scanlines per block?
For 640-pixel scanlines... maybe you can receive only 1 or 2 scanlines per block?

If you want to do very uncommon things: The camera's MODE_OUTPUT_FORMAT could be theoretically programmed to Mono or RGB instead of YUV, that might also affect the number of pixels vs buffer size.

The conversion time does very much vary depending on brightness/exposure settings (and there's even an auto-exposure mode). I don't think that you could convert that into a meaningful formula for calculating timings.

Setting CAM_CNT.bit15 does probably just enable receive (without notifying the camera about it, so the timing till first data block would depend on when the camera is sending the next VSYNC signal).
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty

nocash
Posts: 1259
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: Trying to get the DSi cameras working

Post by nocash » Mon Oct 26, 2020 1:59 pm

The 3DS is having very similar camera hardware.
But, looking at the 3DS CAM_CNT register, there is no "number scanlines" setting.
Instead, there's a CAM_STAT register indicating the FIFO size in range 0..500h (mul 8 bytes?)

So, the buffer size is apparently matched to a multiple of 320 pixels, not 256 pixels (on 3DS at least).
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty

nocash
Posts: 1259
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: Trying to get the DSi cameras working

Post by nocash » Mon Oct 26, 2020 3:34 pm

lidnariq wrote:
Mon Oct 26, 2020 10:55 am
This is part of the standard, but kind of hidden... There are five conditions that lead to the generation of a NACK: [...] 5. A master-receiver must signal the end of the transfer to the slave transmitter.
Good to know, thanks! The full text is...

There are five conditions that lead to the generation of a NACK:
1. No receiver is present on the bus with the transmitted address so there is no device to respond with an acknowledge.
2. The receiver is unable to receive or transmit because it is performing some real-time function and is not ready to start communication with the master.
3. During the transfer, the receiver gets data or commands that it does not understand.
4. During the transfer, the receiver cannot receive any more data bytes.
5. A master-receiver must signal the end of the transfer to the slave transmitter.
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty

nocash
Posts: 1259
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: Trying to get the DSi cameras working

Post by nocash » Thu Oct 29, 2020 3:52 pm

I've been gazing at the I2C low level transfer diagrams...

Sending the I2C Stop condition is done by sending a "corrupted" databit (instead of sending the first databit of the next byte) on the SDA output pin.

However, in receive mode, SDA would be in input direction (for receiving the first databit of next byte). So the master simply cannot output the Stop condition in that state (unless when using the ACK bit to notify the slave that the transfer will be stopped now).

---

Apart from Start and Stop conditions, Nintendo's I2C_CNT register can also generate some sort of "Error/Flush/Pause/whatever" condition:

Code: Select all

  bit0 Stop     (0=No, 1=Stop/last byte)
  bit1 Start    (0=No, 1=Start/first byte)
  bit2 Error    (0=No, 1=Pause/Flush? after Error, used with/after Stop)
I don't really know what bit2 is doing... Everything seems to work without using that bit... Although Nintendo's code does set that bit in some cases (no idea when/why they are doing that exactly).
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty

Arisotura
Posts: 16
Joined: Sun May 19, 2019 7:01 am

Re: Trying to get the DSi cameras working

Post by Arisotura » Sat Oct 31, 2020 8:07 am

well, good to know. reminds me there are still flaws in melonDS's I2C code...

by the way, if you're interested in updating GBAtek's DS side of the documentation, just let me know. (alternately you can look around on the melonDS blog or board)

namely, I have some notes on the 3D engine (details on GX timings and fun low-level details on the rasterizer), some details on wifi operation (namely about the multiplayer features), and some other minor things (mostly things like register bits/values documented as 'reserved/prohibited')...

nocash
Posts: 1259
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: Trying to get the DSi cameras working

Post by nocash » Sun Nov 01, 2020 9:42 pm

Arisotura wrote:
Sat Oct 31, 2020 8:07 am
by the way, if you're interested in updating GBAtek's DS side of the documentation, just let me know.
Yes, let's start with...
http://forums.nesdev.com/viewtopic.php?f=23&t=21140 - NDS Cartridge ROM specs
http://forums.nesdev.com/viewtopic.php?f=23&t=21141 - NDS Wifi specs
I am a bit out of touch with 2D/3D video hardware, but I'll still figure out how to update, too (preferably with understanding the updated stuff myself).
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty

profi200
Posts: 49
Joined: Fri May 10, 2019 4:48 am

Re: Trying to get the DSi cameras working

Post by profi200 » Tue Nov 03, 2020 1:44 pm

Regarding I²C:
Maybe my driver helps. This is for 3DS but the I²C regs are basically identically with few exceptions.

https://github.com/profi200/open_agb_fi ... ware/i2c.c
https://github.com/profi200/open_agb_fi ... ware/i2c.h

On the "kernel_experiments" branch you can also find a version with proper locks and waiting for events instead of blocking on the IRQ.

Post Reply