root/src/add-ons/media/media-add-ons/radeon/Capture.cpp
/******************************************************************************
/
/       File:                   Capture.cpp
/
/       Description:    ATI Radeon Capture Unit interface.
/
/       Copyright 2001, Carlos Hasan
/
/       TK:     something about synchronization: I removed all the FIFO wait 
/               functions as they aren't thread-safe and AFAIK not needed as
/               only 2D/3D register accesses are buffered
/
*******************************************************************************/

#include <Debug.h>
#include "Capture.h"

CCapture::CCapture(CRadeon & radeon)
        :       fRadeon(radeon),
                fMode(C_RADEON_CAPTURE_FIELD_SINGLE),
                fFormat(C_RADEON_CAPTURE_CCIR656),
                fOffset0(0),
                fOffset1(0),
                fVBIOffset0(0),
                fVBIOffset1(0),
                fSize(0),
                fVBISize(0),
                fPitch(0),
                fClip(),
                fVBIClip()
{
        PRINT(("CCapture::CCapture()\n"));
}

CCapture::~CCapture()
{
        PRINT(("CCapture::~CCapture()\n"));
}

status_t CCapture::InitCheck() const
{
        return fRadeon.InitCheck();
}

void CCapture::SetBuffer(capture_stream_format format, capture_buffer_mode mode,
                                                 int offset0, int offset1, int size, int pitch)
{
        PRINT(("CCapture::SetBuffer(%s, %s, 0x%08x, 0x%08x, 0x%08x, %d)\n",
                "BROOKTREE\0CCIR656\0\0\0ZVIDEO\0\0\0\0VIP"+10*format,
                "FIELD-SINGLE\0FIELD-DOUBLE\0BOB-SINGLE\0\0\0"
                "BOB-DOUBLE\0\0\0WEAVE-SINGLE\0WEAVE-DOUBLE"+13*mode,
                offset0, offset1, size, pitch));
        
        fMode = mode;
        fFormat = format;
        fOffset0 = offset0 + fRadeon.VirtualMemoryBase();
        fOffset1 = offset1 + fRadeon.VirtualMemoryBase();
        fSize = size;
        fPitch = pitch;
}

void CCapture::SetClip(int left, int top, int right, int bottom)
{
        PRINT(("CCapture::SetClip(%d, %d, %d, %d)\n",
                left, top, right, bottom));
        
        fClip.SetTo(left, top, right, bottom);
}

void CCapture::SetVBIBuffer(int offset0, int offset1, int size)
{
        PRINT(("CCapture::SetVBIBuffer(0x%08x, 0x%08x, %d)\n", offset0, offset1, size));

        fVBIOffset0 = offset0 + fRadeon.VirtualMemoryBase();
        fVBIOffset1 = offset1 + fRadeon.VirtualMemoryBase();
        fVBISize = size;
}
        
void CCapture::SetVBIClip(int left, int top, int right, int bottom)
{
        PRINT(("CCapture::SetVBIClip(%d, %d, %d, %d)\n",
                left, top, right, bottom));
        
        fVBIClip.SetTo(left, top, right, bottom);
}

void CCapture::Start(bool vbi)
{
        PRINT(("CCapture::Start(%d)\n", vbi));
        
        // initially the capture unit is disabled
        //fRadeon.WaitForFifo(2);
        SetRegister(C_RADEON_CAP0_TRIG_CNTL, C_RADEON_CAP0_TRIGGER_W_NO_ACTION);
        
        // select buffer offset and pitch
        //fRadeon.WaitForFifo(5);
        
        switch (fMode) {
        case C_RADEON_CAPTURE_FIELD_SINGLE:
                /* capture single field, single buffer */
                SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0);
                SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 1);
                break;

        case C_RADEON_CAPTURE_FIELD_DOUBLE:
                /* capture single field, double buffer */
                SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0);
                SetRegister(C_RADEON_CAP0_BUF1_OFFSET, fOffset1);
                SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 1);
                break;

        case C_RADEON_CAPTURE_BOB_SINGLE:
                /* capture interlaced frame, single buffer */
                SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0);
                SetRegister(C_RADEON_CAP0_BUF0_EVEN_OFFSET, fOffset0 + fSize);
                SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 1);
                break;

        case C_RADEON_CAPTURE_BOB_DOUBLE:
                /* capture interlaced frame, double buffer */
                SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0);
                SetRegister(C_RADEON_CAP0_BUF0_EVEN_OFFSET, fOffset0 + fSize);
                SetRegister(C_RADEON_CAP0_BUF1_OFFSET, fOffset1);
                SetRegister(C_RADEON_CAP0_BUF1_EVEN_OFFSET, fOffset1 + fSize);
                SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 1);
                break;

        case C_RADEON_CAPTURE_WEAVE_SINGLE:
                /* capture deinterlaced frame, single buffer */
                SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0);
                SetRegister(C_RADEON_CAP0_BUF0_EVEN_OFFSET, fOffset0 + (fPitch << 1));
                SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 2);
                break;

        case C_RADEON_CAPTURE_WEAVE_DOUBLE:
                /* capture deinterlaced frame, double buffer */
                SetRegister(C_RADEON_CAP0_BUF0_OFFSET, fOffset0);
                SetRegister(C_RADEON_CAP0_BUF0_EVEN_OFFSET, fOffset0 + (fPitch << 1));
                SetRegister(C_RADEON_CAP0_BUF1_OFFSET, fOffset1);
                SetRegister(C_RADEON_CAP0_BUF1_EVEN_OFFSET, fOffset1 + (fPitch << 1));
                SetRegister(C_RADEON_CAP0_BUF_PITCH, fPitch << 2);
                break;
        }

        // select VBI buffer offset
        //fRadeon.WaitForFifo(4);
        
        // FIXME: change according to the buffering mode?
        SetRegister(C_RADEON_CAP0_VBI0_OFFSET, fVBIOffset0);
        SetRegister(C_RADEON_CAP0_VBI1_OFFSET, fVBIOffset0 + fVBISize);

        SetRegister(C_RADEON_CAP0_VBI2_OFFSET, fVBIOffset1);
        SetRegister(C_RADEON_CAP0_VBI3_OFFSET, fVBIOffset1 + fVBISize);
                
        // select capture clipping window
        //fRadeon.WaitForFifo(2);

        SetRegister(C_RADEON_CAP0_H_WINDOW,
                ((fClip.Left()  <<  1) & C_RADEON_CAP0_H_START) |
                ((fClip.Width() << 17) & C_RADEON_CAP0_H_WIDTH));

        SetRegister(C_RADEON_CAP0_V_WINDOW,
                ((fClip.Top()    <<  0) & C_RADEON_CAP0_V_START) |
                ((fClip.Bottom() << 16) & C_RADEON_CAP0_V_END));

        // select VBI clipping window
        //fRadeon.WaitForFifo(2);
        
        SetRegister(C_RADEON_CAP0_VBI_H_WINDOW,
                ((fVBIClip.Left()  <<  0) & C_RADEON_CAP0_VBI_H_START) |
                ((fVBIClip.Width() << 16) & C_RADEON_CAP0_VBI_H_WIDTH));

        SetRegister(C_RADEON_CAP0_VBI_V_WINDOW,
                ((fVBIClip.Top()    <<  0) & C_RADEON_CAP0_VBI_V_START) |
                ((fVBIClip.Bottom() << 16) & C_RADEON_CAP0_VBI_V_END));
                
        // select buffer type, input mode, video format and buffering mode
        //fRadeon.WaitForFifo(10);

        switch (fMode) {
        case C_RADEON_CAPTURE_FIELD_SINGLE:
        case C_RADEON_CAPTURE_FIELD_DOUBLE:
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_BUF_TYPE, C_RADEON_CAP0_BUF_TYPE_FIELD);
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_ONESHOT_MODE, C_RADEON_CAP0_ONESHOT_MODE_FIELD);
                break;
                
        case C_RADEON_CAPTURE_BOB_SINGLE:
        case C_RADEON_CAPTURE_BOB_DOUBLE:
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_BUF_TYPE, C_RADEON_CAP0_BUF_TYPE_ALTERNATING);
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_ONESHOT_MODE, C_RADEON_CAP0_ONESHOT_MODE_FIELD);
                break;
                
        case C_RADEON_CAPTURE_WEAVE_SINGLE:
        case C_RADEON_CAPTURE_WEAVE_DOUBLE:
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_BUF_TYPE, C_RADEON_CAP0_BUF_TYPE_FRAME);
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_ONESHOT_MODE, C_RADEON_CAP0_ONESHOT_MODE_FRAME);
                break;
        }
        
        switch (fMode) {
        case C_RADEON_CAPTURE_FIELD_SINGLE:
        case C_RADEON_CAPTURE_BOB_SINGLE:
        case C_RADEON_CAPTURE_WEAVE_SINGLE:
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_BUF_MODE, C_RADEON_CAP0_BUF_MODE_SINGLE);
                break;
                
        case C_RADEON_CAPTURE_FIELD_DOUBLE:
        case C_RADEON_CAPTURE_BOB_DOUBLE:
        case C_RADEON_CAPTURE_WEAVE_DOUBLE:
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_BUF_MODE, C_RADEON_CAP0_BUF_MODE_DOUBLE);
                break;
        }

        SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_INPUT_MODE, C_RADEON_CAP0_INPUT_MODE_CONTINUOUS);
        SetRegister(C_RADEON_CAP0_CONFIG,
                C_RADEON_CAP0_VIDEO_IN_FORMAT | C_RADEON_CAP0_VIDEO_SIGNED_UV, C_RADEON_CAP0_VIDEO_IN_VYUY422);
        
        
        // select stream format and port mode
        //fRadeon.WaitForFifo(4);
        
        switch (fFormat) {
        case C_RADEON_CAPTURE_BROOKTREE:
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_STREAM_FORMAT, C_RADEON_CAP0_STREAM_BROOKTREE);
                SetRegister(C_RADEON_CAP0_PORT_MODE_CNTL, C_RADEON_CAP0_PORT_WIDTH_8_BITS | C_RADEON_CAP0_PORT_LOWER_BYTE_USED);
                break;
        case C_RADEON_CAPTURE_CCIR656:
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_STREAM_FORMAT, C_RADEON_CAP0_STREAM_CCIR656);
                SetRegister(C_RADEON_CAP0_PORT_MODE_CNTL, C_RADEON_CAP0_PORT_WIDTH_8_BITS | C_RADEON_CAP0_PORT_LOWER_BYTE_USED);
                break;
        case C_RADEON_CAPTURE_ZOOMVIDEO:
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_STREAM_FORMAT, C_RADEON_CAP0_STREAM_ZV);
                SetRegister(C_RADEON_CAP0_PORT_MODE_CNTL, C_RADEON_CAP0_PORT_WIDTH_16_BITS | C_RADEON_CAP0_PORT_LOWER_BYTE_USED);
                break;
        case C_RADEON_CAPTURE_VIP:
                SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_STREAM_FORMAT, C_RADEON_CAP0_STREAM_VIP);
                SetRegister(C_RADEON_CAP0_PORT_MODE_CNTL, C_RADEON_CAP0_PORT_WIDTH_16_BITS | C_RADEON_CAP0_PORT_LOWER_BYTE_USED);
                break;
        }

        // set capture mirror mode, field sense, downscaler/decimator, enable 3:4 pull down
        //fRadeon.WaitForFifo(16);
        SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_MIRROR_EN, 0);
        SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_ONESHOT_MIRROR_EN, 0);
        SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_START_FIELD, C_RADEON_CAP0_START_ODD_FIELD);
        SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_HORZ_DOWN, C_RADEON_CAP0_HORZ_DOWN_1X);
        SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_VERT_DOWN, C_RADEON_CAP0_VERT_DOWN_1X);
        SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_VBI_HORZ_DOWN, C_RADEON_CAP0_VBI_HORZ_DOWN_1X);
        SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_HDWNS_DEC, C_RADEON_CAP0_DECIMATOR);
        SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_SOFT_PULL_DOWN_EN, C_RADEON_CAP0_SOFT_PULL_DOWN_EN);

        // prepare to enable capture
        //fRadeon.WaitForFifo(14);
        
        // disable test and debug modes 
        SetRegister(C_RADEON_TEST_DEBUG_CNTL, 0);
        SetRegister(C_RADEON_CAP0_VIDEO_SYNC_TEST, 0);
        SetRegister(C_RADEON_CAP0_DEBUG, C_RADEON_CAP0_V_SYNC);
        
        // connect capture engine to AMC connector
        SetRegister(C_RADEON_VIDEOMUX_CNTL, 1, 1);

        // select capture engine clock source to PCLK
        SetRegister(C_RADEON_FCP_CNTL, C_RADEON_FCP0_SRC_PCLK);
        
        // enable capture unit
        SetRegister(C_RADEON_CAP0_TRIG_CNTL,
                C_RADEON_CAP0_TRIGGER_W_CAPTURE | C_RADEON_CAP0_EN);
        
        // enable VBI capture
        SetRegister(C_RADEON_CAP0_CONFIG, C_RADEON_CAP0_VBI_EN,
                (vbi ? C_RADEON_CAP0_VBI_EN : 0));
}

void CCapture::Stop()
{
        PRINT(("CCapture::Stop()\n"));

        // disable capture unit
        //fRadeon.WaitForFifo(4);

        // disable capture unit 
        SetRegister(C_RADEON_CAP0_TRIG_CNTL, C_RADEON_CAP0_TRIGGER_W_NO_ACTION);
        
        // disable the capture engine clock (set to ground)
        fRadeon.SetRegister(C_RADEON_FCP_CNTL, C_RADEON_FCP0_SRC_GND);
}

void CCapture::SetInterrupts(bool enable)
{
        PRINT(("CCapture::SetInterrupts(%d)\n", enable));

        fRadeon.SetRegister(C_RADEON_CAP_INT_CNTL,
                C_RADEON_CAP0_BUF0_INT_EN |     C_RADEON_CAP0_BUF0_EVEN_INT_EN |
                C_RADEON_CAP0_BUF1_INT_EN |     C_RADEON_CAP0_BUF1_EVEN_INT_EN |
                C_RADEON_CAP0_VBI0_INT_EN |     C_RADEON_CAP0_VBI1_INT_EN,
                (enable ?       C_RADEON_CAP0_BUF0_INT_EN | C_RADEON_CAP0_BUF0_EVEN_INT_EN |
                                        C_RADEON_CAP0_BUF1_INT_EN |     C_RADEON_CAP0_BUF1_EVEN_INT_EN |
                                        C_RADEON_CAP0_VBI0_INT_EN |     C_RADEON_CAP0_VBI1_INT_EN : 0));

        // clear any stick interrupt
        fRadeon.SetRegister(C_RADEON_CAP_INT_STATUS, 
                C_RADEON_CAP0_BUF0_INT_AK |     C_RADEON_CAP0_BUF0_EVEN_INT_AK |
                C_RADEON_CAP0_BUF1_INT_AK |     C_RADEON_CAP0_BUF1_EVEN_INT_AK |
                C_RADEON_CAP0_VBI0_INT_AK |     C_RADEON_CAP0_VBI1_INT_AK);
}

int CCapture::WaitInterrupts(int * sequence, bigtime_t * when, bigtime_t timeout)
{
        int mask;

        if (fRadeon.WaitInterrupt(&mask, sequence, when, timeout) == B_OK) {
                /*
                int mask = fRadeon.Register(C_RADEON_CAP_INT_STATUS);
                
                fRadeon.SetRegister(C_RADEON_CAP_INT_STATUS, 
                        C_RADEON_CAP0_BUF0_INT_AK |     C_RADEON_CAP0_BUF0_EVEN_INT_AK |
                        C_RADEON_CAP0_BUF1_INT_AK |     C_RADEON_CAP0_BUF1_EVEN_INT_AK |
                        C_RADEON_CAP0_VBI0_INT_AK |     C_RADEON_CAP0_VBI1_INT_AK);
                */
                
                return 
                        ((mask & C_RADEON_CAP0_BUF0_INT) != 0 ? C_RADEON_CAPTURE_BUF0_INT : 0) |
                        ((mask & C_RADEON_CAP0_BUF1_INT) != 0 ? C_RADEON_CAPTURE_BUF1_INT : 0) |
                        ((mask & C_RADEON_CAP0_BUF0_EVEN_INT) != 0 ? C_RADEON_CAPTURE_BUF0_EVEN_INT : 0) |
                        ((mask & C_RADEON_CAP0_BUF1_EVEN_INT) != 0 ? C_RADEON_CAPTURE_BUF1_EVEN_INT : 0) |
                        ((mask & C_RADEON_CAP0_VBI0_INT) != 0 ? C_RADEON_CAPTURE_VBI0_INT : 0) |
                        ((mask & C_RADEON_CAP0_VBI1_INT) != 0 ? C_RADEON_CAPTURE_VBI1_INT : 0);
        }
        return 0;
}

int CCapture::Register(radeon_register index, int mask)
{
        return fRadeon.Register(index, mask);
}

void CCapture::SetRegister(radeon_register index, int value)
{
        fRadeon.SetRegister(index, value);
}

void CCapture::SetRegister(radeon_register index, int mask, int value)
{
        fRadeon.SetRegister(index, mask, value);
}