WM8731 Initialize

DE1-SoCにはAudio CodecのWM8731が搭載されていますが、これを初期化しなければ使用できません。

WolfsonのWM8731のページ.

http://www.wolfsonmicro.com/products/audio_hubs/WM8731/

そこで、これをDE1-SoCのARMサイドから初期化することを考えてみます。

プログラムを作成します。

まず最初にFPGA側になっているGPIOをHPS側で使用できるようにします。

int fd;

void *base;

fd = open ("/dev/mem", O_RDWR | O_SYNC);

base = mmap( NULL, HW_REGS_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED, fd, HW_REGS_BASE );

SOCFPGAのBSPでI2Cデバイスはドライバとして登録されているので、

これをオープンします。

int fd_i2c;

fd_i2c = open("/dev/i2c-0", O_RDWR);

コードはこちら。

WM8731 Driver

#include <errno.h>

#include <string.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/mman.h>

#include <linux/i2c.h>

#include <linux/i2c-dev.h>

#include <sys/ioctl.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include "hwlib.h"

#include "socal/socal.h"

#include "socal/hps.h"

#include "socal/alt_gpio.h"

#define HW_REGS_BASE ( ALT_STM_OFST )

#define HW_REGS_SPAN ( 0x04000000 )

#define HW_REGS_MASK ( HW_REGS_SPAN - 1 )

#define HPS_I2C_CONTROL ( 0x00080000 ) // GPIO48, CONTROL1

static int set_i2c_register(int file, unsigned char addr, unsigned char reg, unsigned char value) {

unsigned char outbuf[2];

struct i2c_rdwr_ioctl_data packets;

struct i2c_msg messages[1];

messages[0].addr = addr;

messages[0].flags = 0;

messages[0].len = sizeof(outbuf);

messages[0].buf = outbuf;

/* The first byte indicates which register we'll write */

outbuf[0] = reg;

/*

* The second byte indicates the value to write. Note that for many

* devices, we can write multiple, sequential registers at once by

* simply making outbuf bigger.

*/

outbuf[1] = value;

/* Transfer the i2c packets to the kernel and verify it worked */

packets.msgs = messages;

packets.nmsgs = 1;

if(ioctl(file, I2C_RDWR, &packets) < 0) {

return false;

}

return true;

}

static int get_i2c_register(int file, unsigned char addr, unsigned char reg, unsigned char *val) {

unsigned char inbuf, outbuf;

struct i2c_rdwr_ioctl_data packets;

struct i2c_msg messages[2];

/*

* In order to read a register, we first do a "dummy write" by writing

* 0 bytes to the register we want to read from. This is similar to

* the packet in set_i2c_register, except it's 1 byte rather than 2.

*/

outbuf = reg;

messages[0].addr = addr;

messages[0].flags = 0;

messages[0].len = sizeof(outbuf);

messages[0].buf = &outbuf;

/* The data will get returned in this structure */

messages[1].addr = addr;

messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;

messages[1].len = sizeof(inbuf);

messages[1].buf = &inbuf;

/* Send the request to the kernel and get the result back */

packets.msgs = messages;

packets.nmsgs = 2;

if(ioctl(file, I2C_RDWR, &packets) < 0) {

return false;

}

*val = inbuf;

return true;

}

bool I2C_REG_WRITE(int file, uint8_t address, uint8_t value){

bool bSuccess = false;

uint8_t szValue[2];

// write to define register

szValue[0] = address;

szValue[1] = value;

if (write(file, &szValue, sizeof(szValue)) == sizeof(szValue)){

bSuccess = true;

}

return bSuccess;

}

bool I2C_REG_READ(int file, uint8_t address,uint8_t *value){

bool bSuccess = false;

uint8_t Value;

int read_len;

// write to define register

if (write(file, &address, sizeof(address)) == sizeof(address)){

// read back value

read_len = read(file, &Value, sizeof(Value));

if (read_len == sizeof(Value)){

*value = Value;

bSuccess = true;

}

}

}

return bSuccess;

}

int main(int argc, char *argv[]){

void *virtual_base;

int fd;

int file;

const char *filename = "/dev/i2c-0";

uint8_t id;

bool bSuccess;

int i;

int i2c_rw_mode; // 0=write, 1=read

unsigned char i2c_addr;

unsigned char i2c_reg;

unsigned char i2c_data;

i2c_rw_mode = 1;

i2c_addr = 0;

i2c_reg = 0;

i2c_data = 0;

if (argc >= 2){

i2c_rw_mode = atoi(argv[1]);

}

if (argc >= 3){

sscanf(argv[2], "%02X", &i2c_addr);

}

if (argc >= 4){

sscanf(argv[3], "%02X", &i2c_reg);

}

if (argc >= 5){

sscanf(argv[4], "%02X", &i2c_data);

}

if( ( fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) {

return( 1 );

}

virtual_base = mmap( NULL, HW_REGS_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED, fd, HW_REGS_BASE );

if( virtual_base == MAP_FAILED ) {

close( fd );

return( 1 );

}

//I2C multplex control

// set the direction of the I2C direction bits attached to U10 to output

alt_setbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DDR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), HPS_I2C_CONTROL );

// open the i2c interface with HPS

alt_setbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), HPS_I2C_CONTROL );

// open bus

if ((file = open(filename, O_RDWR)) < 0) {

/* ERROR HANDLING: you can check errno to see what went wrong */

exit(1);

}

// init set device address

if (i2c_rw_mode) {

// read mode

bSuccess = get_i2c_register(file, i2c_addr, i2c_reg, &i2c_data);

//bSuccess = I2C_REG_READ(file, i2c_reg, &i2c_data);

if (bSuccess==true) {

printf("%02X\n", i2c_data);

} else {

printf("FALSE\n");

}

} else {

// write mode

bSuccess = set_i2c_register(file, i2c_addr, i2c_reg, i2c_data);

//bSuccess = I2C_REG_WRITE(file, i2c_reg, i2c_data);

if (bSuccess==true) {

printf("OK\n");

} else {

printf("FALSE\n");

}

}

if (file) {

close(file);

}

// i2c bus restore

alt_clrbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), HPS_I2C_CONTROL );

if( munmap( virtual_base, HW_REGS_SPAN ) != 0 ) {

printf( "ERROR: munmap() failed...\n" );

close( fd );

return( 1 );

}

close( fd );

return 0;

}

/*

int addr = 0b0010 0000; // video-in chip

// gsensor i2c address: 101_0011

int addr = 0b01010011;

audio codec 0x1A (write only device)

read:

i2c_rw 1 20 11 00

write:

i2c_rw 0 1A 05 00

WM8731 Initialize

./i2c_rw 0 1A 00 17

./i2c_rw 0 1A 02 17

./i2c_rw 0 1A 04 E5

./i2c_rw 0 1A 06 E5

./i2c_rw 0 1A 08 12

./i2c_rw 0 1A 0A 00

./i2c_rw 0 1A 0C 62

./i2c_rw 0 1A 0E 0A

./i2c_rw 0 1A 10 28

./i2c_rw 0 1A 12 01

*/