root/src/add-ons/kernel/drivers/audio/ice1712/io.cpp
/*
 * Copyright 2004-2015 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Jérôme Duval, jerome.duval@free.fr
 *              Marcus Overhagen, marcus@overhagen.de
 *              Jérôme Lévêque, leveque.jerome@gmail.com
 */


#include "io.h"
#include "ice1712_reg.h"
#include "debug.h"

extern pci_module_info *pci;

static void ak45xx_write_gpio(ice1712 *ice, uint8 reg_addr,
        uint8 data, uint8 chip_select, uint8 invert_cs);

static void cs84xx_write_gpio(ice1712 *ice, uint8 reg_addr,
        uint8 data, uint8 chip_select, uint8 invert_cs);

static uint8 ak45xx_read_gpio(ice1712 *ice, uint8 reg_addr,
        uint8 chip_select, uint8 invert_cs)
                {return 0;} //Unimplemented

static uint8 cs84xx_read_gpio(ice1712 *ice, uint8 reg_addr,
        uint8 chip_select, uint8 invert_cs);

static void write_gpio_byte(ice1712 *ice, uint8 data, uint8 gpio_data);
static uint8 read_gpio_byte(ice1712 *ice, uint8 gpio_data);


//Address are [PCI_10] + xx
uint8
read_ccs_uint8(ice1712 *ice, int8 regno)
{
        return pci->read_io_8(ice->Controller + regno);
};


uint16
read_ccs_uint16(ice1712 *ice, int8 regno)
{
        return pci->read_io_16(ice->Controller + regno);
};


uint32
read_ccs_uint32(ice1712 *ice, int8 regno)
{
        return pci->read_io_32(ice->Controller + regno);
};


void
write_ccs_uint8(ice1712 *ice, int8 regno, uint8 value)
{
        pci->write_io_8(ice->Controller + regno, value);
};


void
write_ccs_uint16(ice1712 *ice, int8 regno, uint16 value)
{
        pci->write_io_16(ice->Controller + regno, value);
};


void
write_ccs_uint32(ice1712 *ice, int8 regno, uint32 value)
{
        pci->write_io_32(ice->Controller + regno, value);
};


uint8
read_cci_uint8(ice1712 *ice, int8 index)
{
        write_ccs_uint8(ice, CCS_CCI_INDEX, index);
        return read_ccs_uint8(ice, CCS_CCI_DATA);
};


void
write_cci_uint8(ice1712 *ice, int8 index, uint8 value)
{
        write_ccs_uint8(ice, CCS_CCI_INDEX, index);
        write_ccs_uint8(ice, CCS_CCI_DATA, value);
};


//Address are [PCI_14] + xx
uint8
read_ddma_uint8(ice1712 *ice, int8 regno)
{
        return pci->read_io_8(ice->DDMA + regno);
};


uint16
read_ddma_uint16(ice1712 *ice, int8 regno)
{
        return pci->read_io_16(ice->DDMA + regno);
};


uint32
read_ddma_uint32(ice1712 *ice, int8 regno)
{
        return pci->read_io_32(ice->DDMA + regno);
};


void
write_ddma_uint8(ice1712 *ice, int8 regno, uint8 value)
{
        pci->write_io_8(ice->DDMA + regno, value);
};


void
write_ddma_uint16(ice1712 *ice, int8 regno, uint16 value)
{
        pci->write_io_16(ice->DDMA + regno, value);
};


void
write_ddma_uint32(ice1712 *ice, int8 regno, uint32 value)
{
        pci->write_io_32(ice->DDMA + regno, value);
};


//Address are [PCI_18] + x
uint8
read_ds_uint8(ice1712 *ice, int8 regno)
{
        return pci->read_io_8(ice->DMA_Path + regno);
};


uint16
read_ds_uint16(ice1712 *ice, int8 regno)
{
        return pci->read_io_16(ice->DMA_Path + regno);
};


uint32
read_ds_uint32(ice1712 *ice, int8 regno)
{
        return pci->read_io_32(ice->DMA_Path + regno);
};


void
write_ds_uint8(ice1712 *ice, int8 regno, uint8 value)
{
        pci->write_io_8(ice->DMA_Path + regno, value);
};


void
write_ds_uint16(ice1712 *ice, int8 regno, uint16 value)
{
        pci->write_io_16(ice->DMA_Path + regno, value);
};


void
write_ds_uint32(ice1712 *ice, int8 regno, uint32 value)
{
        pci->write_io_32(ice->DMA_Path + regno, value);
};


uint32
read_ds_channel_data(ice1712 *ice, uint8 channel, ds8_register index)
{
        uint8 ds8_channel_index = channel << 4 | index;

        write_ds_uint8(ice, DS_CHANNEL_INDEX, ds8_channel_index);
        return read_ds_uint32(ice, DS_CHANNEL_DATA);
}


void
write_ds_channel_data(ice1712 *ice, uint8 channel, ds8_register index,
        uint32 data)
{
        uint8 ds8_channel_index = channel << 4 | index;

        write_ds_uint8(ice, DS_CHANNEL_INDEX, ds8_channel_index);
        write_ds_uint32(ice, DS_CHANNEL_DATA, data);
}


//Address are [PCI_1C] + xx
uint8
read_mt_uint8(ice1712 *ice, int8 regno)
{
        return pci->read_io_8(ice->Multi_Track + regno);
};


uint16
read_mt_uint16(ice1712 *ice, int8 regno)
{
        return pci->read_io_16(ice->Multi_Track + regno);
};


uint32
read_mt_uint32(ice1712 *ice, int8 regno)
{
        return pci->read_io_32(ice->Multi_Track + regno);
};


void
write_mt_uint8(ice1712 *ice, int8 regno, uint8 value)
{
        pci->write_io_8(ice->Multi_Track + regno, value);
};


void
write_mt_uint16(ice1712 *ice, int8 regno, uint16 value)
{
        pci->write_io_16(ice->Multi_Track + regno, value);
};


void
write_mt_uint32(ice1712 *ice, int8 regno, uint32 value)
{
        pci->write_io_32(ice->Multi_Track + regno, value);
};


/*
 * return -1 if error else return an uint8
 */
int16
read_i2c(ice1712 *ice, uint8 dev_addr, uint8 byte_addr)
{
        if (read_ccs_uint8(ice, CCS_I2C_CONTROL_STATUS) != 0x80)
                return -1;
        write_ccs_uint8(ice, CCS_I2C_BYTE_ADDRESS, byte_addr);
        write_ccs_uint8(ice, CCS_I2C_DEV_ADDRESS, dev_addr);
        snooze(1000);
        return read_ccs_uint8(ice, CCS_I2C_DATA);
}


/*
 * return -1 if error else return 0
 */
int16
write_i2c(ice1712 *ice, uint8 dev_addr, uint8 byte_addr, uint8 value)
{
        if (read_ccs_uint8(ice, CCS_I2C_CONTROL_STATUS) != 0x80)
                return -1;

        write_ccs_uint8(ice, CCS_I2C_BYTE_ADDRESS, byte_addr);
        write_ccs_uint8(ice, CCS_I2C_DEV_ADDRESS, dev_addr);
        write_ccs_uint8(ice, CCS_I2C_DATA, value);
        return 0;
}


int16 read_eeprom(ice1712 *ice, uint8 eeprom[32])
{
        int i;
        int16 tmp;

        for (i = 0; i < 6; i++) {
                tmp = read_i2c(ice, I2C_EEPROM_ADDRESS_READ, i);
                if (tmp >= 0)
                        eeprom[i] = (uint8)tmp;
                else
                        return -1;
        }
        if (eeprom[4] > 32)
                return -1;
        for (i = 6; i < eeprom[4]; i++) {
                tmp = read_i2c(ice, I2C_EEPROM_ADDRESS_READ, i);
                if (tmp >= 0)
                        eeprom[i] = (uint8)tmp;
                else
                        return -1;
        }
        return eeprom[4];
}


void
codec_write(ice1712 *ice, uint8 reg_addr, uint8 data)
{
        switch (ice->config.product) {
                case ICE1712_SUBDEVICE_DELTA66:
                case ICE1712_SUBDEVICE_DELTA44:
                        ak45xx_write_gpio(ice, reg_addr, data,
                                DELTA66_CODEC_CS_0, 0);
                        ak45xx_write_gpio(ice, reg_addr, data,
                                DELTA66_CODEC_CS_1, 0);
                        break;
                case ICE1712_SUBDEVICE_DELTA410:
                case ICE1712_SUBDEVICE_AUDIOPHILE_2496:
                case ICE1712_SUBDEVICE_DELTADIO2496:
                        ak45xx_write_gpio(ice, reg_addr, data, AP2496_CODEC_CS, 0);
                        break;
                case ICE1712_SUBDEVICE_DELTA1010:
                case ICE1712_SUBDEVICE_DELTA1010LT:
                        ak45xx_write_gpio(ice, reg_addr, data,
                                DELTA1010LT_CODEC_CS_0, DELTA1010LT_CS_NONE);
                        ak45xx_write_gpio(ice, reg_addr, data,
                                DELTA1010LT_CODEC_CS_1, DELTA1010LT_CS_NONE);
                        ak45xx_write_gpio(ice, reg_addr, data,
                                DELTA1010LT_CODEC_CS_2, DELTA1010LT_CS_NONE);
                        ak45xx_write_gpio(ice, reg_addr, data,
                                DELTA1010LT_CODEC_CS_3, DELTA1010LT_CS_NONE);
                        break;
                case ICE1712_SUBDEVICE_VX442:
                        ak45xx_write_gpio(ice, reg_addr, data, VX442_CODEC_CS_0, 0);
                        ak45xx_write_gpio(ice, reg_addr, data, VX442_CODEC_CS_1, 0);
                        break;
        }
}


void
spdif_write(ice1712 *ice, uint8 reg_addr, uint8 data)
{
        switch (ice->config.product) {
                case ICE1712_SUBDEVICE_DELTA1010:
                        break;
                case ICE1712_SUBDEVICE_DELTADIO2496:
                        break;
                case ICE1712_SUBDEVICE_DELTA66:
                        break;
                case ICE1712_SUBDEVICE_DELTA44:
                        break;
                case ICE1712_SUBDEVICE_AUDIOPHILE_2496:
                        cs84xx_write_gpio(ice, reg_addr, data, AP2496_SPDIF_CS, 0);
                        break;
                case ICE1712_SUBDEVICE_DELTA410:
                        break;
                case ICE1712_SUBDEVICE_DELTA1010LT:
                        cs84xx_write_gpio(ice, reg_addr, data, DELTA1010LT_SPDIF_CS,
                                DELTA1010LT_CS_NONE);
                        break;
                case ICE1712_SUBDEVICE_VX442:
                        cs84xx_write_gpio(ice, reg_addr, data, VX442_SPDIF_CS, 0);
                        break;
        }
}


uint8
codec_read(ice1712 *ice, uint8 reg_addr)
{
        uint8 val = 0xFF;
        switch (ice->config.product) {
                case ICE1712_SUBDEVICE_DELTA66:
                case ICE1712_SUBDEVICE_DELTA44:
                        val = ak45xx_read_gpio(ice, reg_addr, DELTA66_CODEC_CS_0, 0);
                        break;
                case ICE1712_SUBDEVICE_DELTA410:
                case ICE1712_SUBDEVICE_AUDIOPHILE_2496:
                case ICE1712_SUBDEVICE_DELTADIO2496:
                        val = ak45xx_read_gpio(ice, reg_addr, AP2496_CODEC_CS, 0);
                        break;
                case ICE1712_SUBDEVICE_DELTA1010:
                case ICE1712_SUBDEVICE_DELTA1010LT:
                        val = ak45xx_read_gpio(ice, reg_addr, DELTA1010LT_CODEC_CS_0,
                                DELTA1010LT_CS_NONE);
                        break;
                case ICE1712_SUBDEVICE_VX442:
                        val = ak45xx_read_gpio(ice, reg_addr, VX442_CODEC_CS_0, 0);
                        break;
        }

        return val;
}


uint8
spdif_read(ice1712 *ice, uint8 reg_addr)
{
        uint8 val = 0xFF;
        switch (ice->config.product) {
                case ICE1712_SUBDEVICE_DELTA1010:
                        break;
                case ICE1712_SUBDEVICE_DELTADIO2496:
                        break;
                case ICE1712_SUBDEVICE_DELTA66:
                        break;
                case ICE1712_SUBDEVICE_DELTA44:
                        break;
                case ICE1712_SUBDEVICE_AUDIOPHILE_2496:
                        val = cs84xx_read_gpio(ice, reg_addr, AP2496_SPDIF_CS, 0);
                        break;
                case ICE1712_SUBDEVICE_DELTA410:
                        break;
                case ICE1712_SUBDEVICE_DELTA1010LT:
                        val = cs84xx_read_gpio(ice, reg_addr, DELTA1010LT_SPDIF_CS,
                                DELTA1010LT_CS_NONE);
                        break;
                case ICE1712_SUBDEVICE_VX442:
                        val = cs84xx_read_gpio(ice, reg_addr, VX442_SPDIF_CS, 0);
                        break;
        }

        return val;
}


void
write_gpio_byte(ice1712 *ice, uint8 data, uint8 gpio_data)
{
        int i;

        for (i = 7; i >= 0; i--) {
                // drop clock and data bits
                gpio_data &= ~(ice->CommLines.clock | ice->CommLines.data_out);

                // set data bit if needed
                if (data & (1 << i))
                        gpio_data |= ice->CommLines.data_out;

                write_gpio(ice, gpio_data);
                snooze(GPIO_I2C_DELAY);

                // raise clock
                gpio_data |= ice->CommLines.clock;
                write_gpio(ice, gpio_data);
                snooze(GPIO_I2C_DELAY);
        }
}


uint8
read_gpio_byte(ice1712 *ice, uint8 gpio_data)
{
        int i;
        uint8 data = 0;

        for (i = 7; i >= 0; i--) {
                // drop clock
                gpio_data &= ~(ice->CommLines.clock);
                write_gpio(ice, gpio_data);
                snooze(GPIO_I2C_DELAY);

                if (read_gpio(ice) &ice->CommLines.data_in)
                        data |= 1 << i;

                gpio_data |= ice->CommLines.clock;

                write_gpio(ice, gpio_data);
                snooze(GPIO_I2C_DELAY);
        }

        return data;
}


void
ak45xx_write_gpio(ice1712 *ice, uint8 reg_addr, uint8 data,
        uint8 chip_select, uint8 invert_cs)
{
        uint8 tmp;

        tmp = read_gpio(ice);
        tmp |= ice->CommLines.cs_mask;

        if (invert_cs != 0) {
                tmp &= ~invert_cs;
                tmp |= chip_select;
        } else {
                tmp &= ~chip_select;
        }

        write_gpio(ice, tmp);
        snooze(GPIO_I2C_DELAY);

        write_gpio_byte(ice, ((AK45xx_CHIP_ADDRESS & 0x03) << 6) | 0x20
                | (reg_addr & 0x1F), tmp);
        write_gpio_byte(ice, data, tmp);

        if (invert_cs != 0) {
                tmp |= invert_cs;
        } else {
                tmp |= chip_select;
        }
        write_gpio(ice, tmp);
        snooze(GPIO_I2C_DELAY);
}


void
cs84xx_write_gpio(ice1712 *ice, uint8 reg_addr, uint8 data,
        uint8 chip_select, uint8 invert_cs)
{
        uint8 tmp;

        tmp = read_gpio(ice);
        tmp |= ice->CommLines.cs_mask;

        if (invert_cs != 0) {
                tmp &= ~invert_cs;
                tmp |= chip_select;
        } else {
                tmp &= ~chip_select;
        }

        write_gpio(ice, tmp);
        snooze(GPIO_I2C_DELAY);

        write_gpio_byte(ice, (CS84xx_CHIP_ADDRESS & 0x7F) << 1, tmp);
        write_gpio_byte(ice, reg_addr & 0x7F, tmp); //Do not Increment
        write_gpio_byte(ice, data, tmp);

        if (invert_cs != 0) {
                tmp |= invert_cs;
        } else {
                tmp |= chip_select;
        }
        write_gpio(ice, tmp);
        snooze(GPIO_I2C_DELAY);
}


uint8
cs84xx_read_gpio(ice1712 *ice, uint8 reg_addr, uint8 chip_select,
        uint8 invert_cs)
{
        uint8 tmp, data;

        tmp = read_gpio(ice);
        tmp |= ice->CommLines.cs_mask;

        if (invert_cs != 0) {
                tmp &= ~invert_cs;
                tmp |= chip_select;
        } else {
                tmp &= ~chip_select;
        }

        write_gpio(ice, tmp);
        snooze(GPIO_I2C_DELAY);

        write_gpio_byte(ice, (CS84xx_CHIP_ADDRESS & 0x7F) << 1,
                tmp); //For writing the MAP
        write_gpio_byte(ice, reg_addr & 0x7F, tmp); //Do not Increment

        //Deselect the chip
        if (invert_cs != 0) {
                tmp |= invert_cs;
        } else {
                tmp |= chip_select;
        }
        write_gpio(ice, tmp);
        snooze(GPIO_I2C_DELAY);

        if (invert_cs != 0) {
                tmp &= ~invert_cs;
                tmp |= chip_select;
        } else {
                tmp &= ~chip_select;
        }
        write_gpio(ice, tmp);
        snooze(GPIO_I2C_DELAY);

        write_gpio_byte(ice, (CS84xx_CHIP_ADDRESS & 0x7F) << 1 | 1,
                tmp); //For writing the MAP
        data = read_gpio_byte(ice, tmp); //For reading

        //Deselect the chip
        if (invert_cs != 0) {
                tmp |= invert_cs;
        } else {
                tmp |= chip_select;
        }
        write_gpio(ice, tmp);

        return data;
}


/*
 * return -1 if error else return an uint8
 */
uint8
read_gpio(ice1712 *ice)
{
        return read_cci_uint8(ice, CCI_GPIO_DATA);
}


/*
 * return -1 if error else return 0
 */
void
write_gpio(ice1712 *ice, uint8 value)
{
        write_cci_uint8(ice, CCI_GPIO_DATA, value);
}