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
*/