2010년 6월 7일 월요일

하드웨어 의존적인 OV3640 코드 0xdroid의 커널의 board-devkit8000에 추가하기

Board-devkit8k.c
먼저 앞서 ov3640, Kconfig, 그리고 Makefile을 수정하여 Oxlab에서 배포하는 커널에 ov3640을 컴파일 할 수 있는 환경을 만들어 두었다. 나머지 부분은 실제 보드와 연동하여 동작 할 수 있는 환경을 만들어 주어야한다. 아래의 것들이 그에 해당하는 것인데, 이는 모두 ov3640을 지원하는 rowboat 안드로이드 배포판의 리눅스 커널에서 따왔다. Grep -r ov3640 * 으로 검색하여 보면 ov3640을 지원하는 보드는 딱 두가지 인데 그중 하나인 board-3430sdp의 코드를 보고 해당사항들을 board-devkit8000.c에 추가하고 여기에 기록으로 남긴다.

//#include
#ifdef CONFIG_VIDEO_OMAP3
#include
#include <../drivers/media/video/omap34xxcam.h>
#include <../drivers/media/video/isp/ispreg.h>
//#define REG_SDP3430_FPGA_GPIO_2 (0x50)
#define REG_DEVKIT8000_FPGA_GPIO_2 (0x50)
#define FPGA_SPR_GPIO1_3v3 (0x1 << 14)
#define FPGA_GPIO6_DIR_CTRL (0x1 << 6)
static void __iomem *fpga_map_addr;

// TODO:
#if 0
#if defined(CONFIG_VIDEO_MT9P012) || defined(CONFIG_VIDEO_MT9P012_MODULE)
/* Sensor specific GPIO signals */
#define MT9P012_RESET_GPIO 98
#define MT9P012_STANDBY_GPIO 58

#define MT9P012_USE_XCLKA 0
#define MT9P012_USE_XCLKB 1

#define VAUX_2_8_V 0x09
#define VAUX_DEV_GRP_P1 0x20
#define VAUX_DEV_GRP_NONE 0x00

#include
static enum v4l2_power mt9p012_previous_power = V4L2_POWER_OFF;
#endif
#endif

#if defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE)
#include <../drivers/media/video/ov3640.h>
#include <../drivers/media/video/isp/ispcsi2.h>
static struct omap34xxcam_hw_config *hwc;
#define OV3640_CSI2_CLOCK_POLARITY 0 /* +/- pin order */
#define OV3640_CSI2_DATA0_POLARITY 0 /* +/- pin order */
#define OV3640_CSI2_DATA1_POLARITY 0 /* +/- pin order */
#define OV3640_CSI2_CLOCK_LANE 1 /* Clock lane position: 1 */
#define OV3640_CSI2_DATA0_LANE 2 /* Data0 lane position: 2 */
#define OV3640_CSI2_DATA1_LANE 3 /* Data1 lane position: 3 */
#define OV3640_CSI2_PHY_THS_TERM 4
#define OV3640_CSI2_PHY_THS_SETTLE 14
#define OV3640_CSI2_PHY_TCLK_TERM 0
#define OV3640_CSI2_PHY_TCLK_MISS 1
#define OV3640_CSI2_PHY_TCLK_SETTLE 14
#endif
#endif

위의 코드는 v4l2(video4linux 2), Omap ISP코어와 연동하기위한 해더파일과 ov3640을 구동하기위해서 필요한 하드웨어 의존적이지 않은 코드의 해더파일을 추가하고 덧붙여 필요한 사전 정의 문구들이다. 한편 원본을 훼손하지 않기 위해 CONFIG_VIDEO_MT9P012에 관한 부분이 불가피하게 추가 되었지만, 시간 관계상 CONFIG_VIDEO_MT9P012부분은 막아 놓았다.

#ifdef CONFIG_VIDEO_OMAP3
//#define DEBUG_BASE 0x08000000
//#define REG_SDP3430_FPGA_GPIO_2 (0x50)
//#define FPGA_SPR_GPIO1_3v3 (0x1 << 14)
//#define FPGA_GPIO6_DIR_CTRL (0x1 << 6)

static void __iomem *fpga_map_addr;

static void enable_fpga_vio_1v8(u8 enable)
{
u16 reg_val;

fpga_map_addr = ioremap(DEBUG_BASE, 4096);
//reg_val = readw(fpga_map_addr + REG_SDP3430_FPGA_GPIO_2);
reg_val = readw(fpga_map_addr + REG_DEVKIT8000_FPGA_GPIO_2);

/* Ensure that the SPR_GPIO1_3v3 is 0 - powered off.. 1 is on */
if (reg_val & FPGA_SPR_GPIO1_3v3) {
reg_val |= FPGA_SPR_GPIO1_3v3;
reg_val |= FPGA_GPIO6_DIR_CTRL; /* output mode */
//writew(reg_val, fpga_map_addr + REG_SDP3430_FPGA_GPIO_2);
writew(reg_val, fpga_map_addr + REG_DEVKIT8000_FPGA_GPIO_2);
/* give a few milli sec to settle down
* Let the sensor also settle down.. if required..
*/
if (enable)
mdelay(10);
}

if (enable) {
reg_val |= FPGA_SPR_GPIO1_3v3 | FPGA_GPIO6_DIR_CTRL;
//writew(reg_val, fpga_map_addr + REG_SDP3430_FPGA_GPIO_2);
writew(reg_val, fpga_map_addr + REG_DEVKIT8000_FPGA_GPIO_2);
}
/* Vrise time for the voltage - should be less than 1 ms */
mdelay(1);
}
#endif

이 부분은 아마도 카메라와 주고 받는 I/O의 전원을 1.8V로 설정하는 부분인것 같다. 자세한 ioremap의 기능은 모르겠으나 REG_DEVKIT8000_FPGA_GPIO_2의 값을 읽어 와서 3.3V로 되어 있다면 설정을 지우는 작업과 약간의 딜레이를 주는 코드로 이해하도록 하겠다. 자세히 알고 싶다면 ioremap의 기능과 REG_DEVKIT8000_FPGA_GPIO_2의 레지스터를 확인해 보도록 하자.


#if defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE)

static struct omap34xxcam_sensor_config ov3640_hwc = {
.sensor_isp = 0,
#if defined(CONFIG_VIDEO_OV3640_CSI2)
.xclk = OMAP34XXCAM_XCLK_B,
#else
.xclk = OMAP34XXCAM_XCLK_A,
#endif
.capture_mem = PAGE_ALIGN(2048 * 1536 * 2) * 2,
.ival_default = { .numerator = 1, .demoninator = 30,},
};

static struct isp_interface_config ov3640_if_config = {
.ccdc_par_ser = ISP_CSIA,
.dataline_shift = 0x0,
.hsvs_syncdetect = ISPCTRL_SYNC_DETECT_VSRISE,
.strobe = 0x0,
.prestrobe = 0x0,
.shutter = 0x0,
// .prev_sph = 2,
// .prev_slv = 1,
.wenlog = ISPCCDC_CFG_WENLOG_AND,
.wait_hs_vs = 2,
.u.csi.crc = 0x0,
.u.csi.mode = 0x0,
.u.csi.edge = 0x0,
.u.csi.signalling = 0x0,
.u.csi.strobe_clock_inv = 0x0,
.u.csi.vs_edge = 0x0,
.u.csi.channel = 0x1,
.u.csi.vpclk = 0x1,
.u.csi.data_start = 0x0,
.u.csi.data_size = 0x0,
.u.csi.format = V4L2_PIX_FMT_SGRBG10,
};

static int ov3640_sensor_set_prv_data(void *priv)
{
hwc = priv;
hwc->u.sensor.xclk = ov3640_hwc.xclk;
hwc->u.sensor.sensor_isp = ov3640_hwc.sensor_isp;
hwc->u.sensor.capture_mem = ov3640_hwc.capture_mem;
hwc->dev_index = 1;
hwc->dev_minor = 4;
hwc->dev_type = OMAP34XXCAM_SLAVE_SENSOR;
return 0;
}

static int ov3640_sensor_power_set(enum v4l2_power power)
{
struct isp_csi2_lanes_cfg lanecfg;
struct isp_csi2_phy_cfg phyconfig;
static enum v4l2_power previous_power = V4L2_POWER_OFF;
switch (power) {
case V4L2_POWER_ON:
if (previous_power == V4L2_POWER_OFF)
isp_csi2_reset();

lanecfg.clk.pol = OV3640_CSI2_CLOCK_POLARITY;
lanecfg.clk.pos = OV3640_CSI2_CLOCK_LANE;
lanecfg.data[0].pol = OV3640_CSI2_DATA0_POLARITY;
lanecfg.data[0].pos = OV3640_CSI2_DATA0_LANE;
lanecfg.data[1].pol = OV3640_CSI2_DATA1_POLARITY;
lanecfg.data[1].pos = OV3640_CSI2_DATA1_LANE;
lanecfg.data[2].pol = 0;
lanecfg.data[2].pos = 0;
lanecfg.data[3].pol = 0;
lanecfg.data[3].pos = 0;
isp_csi2_complexio_lanes_config(&lanecfg);
isp_csi2_complexio_lanes_update(true);

phyconfig.ths_term = OV3640_CSI2_PHY_THS_TERM;
phyconfig.ths_settle = OV3640_CSI2_PHY_THS_SETTLE;
phyconfig.tclk_term = OV3640_CSI2_PHY_TCLK_TERM;
phyconfig.tclk_miss = OV3640_CSI2_PHY_TCLK_MISS;
phyconfig.tclk_settle = OV3640_CSI2_PHY_TCLK_SETTLE;
isp_csi2_phy_config(&phyconfig);
isp_csi2_phy_update(true);

isp_configure_interface(&ov3640_if_config);

if (previous_power == V4L2_POWER_OFF) {

#ifdef CONFIG_TWL4030_CORE
/* turn on analog power */
#if defined(CONFIG_VIDEO_OV3640_CSI2)
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_1_8_V, TWL4030_VAUX4_DEDICATED);
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_DEV_GRP_P1, TWL4030_VAUX4_DEV_GRP);
#else
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_2_8_V, TWL4030_VAUX2_DEDICATED);
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_DEV_GRP_P1, TWL4030_VAUX2_DEV_GRP);
#endif
udelay(100);
#else
#error "no power companion board defined!"
#endif
/* Request and configure gpio pins */
if (omap_request_gpio(OV3640_RESET_GPIO) != 0) {
printk(KERN_ERR "Could not request GPIO %d",
OV3640_RESET_GPIO);
return -EIO;
}
if (omap_request_gpio(OV3640_STANDBY_GPIO) != 0) {
printk(KERN_ERR "Could not request GPIO %d",
OV3640_STANDBY_GPIO);
return -EIO;
}
/* set to output mode */
gpio_direction_output(OV3640_RESET_GPIO, true);
gpio_direction_output(OV3640_STANDBY_GPIO, true);

/* Turn ON Omnivision sensor */
gpio_set_value(OV3640_RESET_GPIO, 1);
gpio_set_value(OV3640_STANDBY_GPIO, 0);
udelay(100);

/* RESET Omnivision sensor */
gpio_set_value(OV3640_RESET_GPIO, 0);
udelay(100);
gpio_set_value(OV3640_RESET_GPIO, 1);

/* Wait 10 ms */
mdelay(10);
enable_fpga_vio_1v8(1);
udelay(100);
}
break;
case V4L2_POWER_OFF:
/* Power Down Sequence */
isp_csi2_complexio_power(ISP_CSI2_POWER_OFF);
#ifdef CONFIG_TWL4030_CORE
#if defined(CONFIG_VIDEO_OV3640_CSI2)
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_DEV_GRP_NONE, TWL4030_VAUX4_DEV_GRP);
#else
twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
VAUX_DEV_GRP_NONE, TWL4030_VAUX2_DEV_GRP);
#endif
#else
#error "no power companion board defined!"
#endif
enable_fpga_vio_1v8(0);
omap_free_gpio(OV3640_RESET_GPIO);
iounmap(fpga_map_addr);
omap_free_gpio(OV3640_STANDBY_GPIO);
break;
case V4L2_POWER_STANDBY:
break;
}
previous_power = power;
return 0;
}

//static struct ov3640_platform_data sdp3430_ov3640_platform_data = {
static struct ov3640_platform_data devkit8000_ov3640_platform_data = {
.power_set = ov3640_sensor_power_set,
.priv_data_set = ov3640_sensor_set_prv_data,
.default_regs = ov3640_common[0],
};

#endif
이 부분은 ov3640의 기초적인 부분은 말하자면 전원 끄고 리셋하고 i2c 데이터 날리는 부분이 되는거 같다. 세세한 사항은 남겨두고 중유한 부분은 static struct ov3640_platform_data devkit8000_ov3640_platform_data 이 구조체가 되겠다. 이 부분이 i2c 드라이버에 등록되어 실제 (ov3640 드라이버 (driver/media/video/ov3640.c)가 구동 될때 i2c 버스에 달랑 등록하는 부분만 있다. 그럼 나머지 부분인 초기화 하고 제어하는 하드웨어 의존적인 부부은 이곳에서 한다.) ov3640 드라이버가 i2c 드라이버에 추가될때 검색하여 실행할 것들을 모아 놓은 것이다. 그리고 아래 부분은 i2c의 2번째 채널에 devkit8000_ov3640_platform_data을 등록하는 부분이다.

(추가 수정사항 :
static struct omap34xxcam_sensor_config ov3640_hwc의 값 초기화에 멤버 변수 ival_default에 대한 초기화 추가
omap34xxcam.c의 버전 차이(자세히 이야기 하자면 변종)로 인한 추가 멤버가 생겼다. Struct v4l2_fract ival_default는 프레임 레이트(fps:frame per second)이 정의되어 있지 않을때 사용해야할 값을 넣어 두고는 곳이 되는것 같다. 그 구성과 사용하는 값은 다음과 같다.
Struct v4l2_fract {
__u32 numerator;
__u32 denominator;
};
.ival_fract = { .numerator = 1, .denominator = 30, },
즉, 30fps의 활동사진을 보여주도록 설정한다.

CONFIG_VIDEO_OMAP3_CAM을 CONFIG_VIDEO_OMAP3로 수정
rowboar의 커널에서 CONFIG_VIDEO_OMAP3_CAM으로 정의되어 사용하는 것이 0xdroid의 경우 CONFIG_VIDEO_OMAP3로 되어 있다.

Struct isp_interface_config ov3640_if_config의 prev_sph, 와 prev_slv의 초기화 제거
drivers/media/video/isp/isp.c의 버전 차이로 인해 prev_sph와 prev_slv가 제거되고 isppreview_set_skip(config->prev_sph, config->prev_slv); 가 사라져 버렸다. 어떤것이 상위 버전인지는 알수가 없다.
omap_request_gpio가 없다는 에러
gpio드라이버의 버전 호환 문제로 인한 에러로 여겨짐

omap_request_gpio(OV3640_RESET_GPIO) => gpio_request(0V3640_RESET_GPIO, "ov3640 reset gpio")

omap_request_gpio(OV3640_STANDBY_GPIO) => gpio_request(OV3640_STANDBY_GPIO, "ov3640 standby gpio")

omap_free_gpio => gpio_free

로 변경하자.

)

//static struct i2c_board_info __initdata sdp3430_i2c_boardinfo_2[] = {
static struct i2c_board_info __initdata devkit8000_i2c_boardinfo_2[] = {
// TODO:
#if 0
#if defined(CONFIG_VIDEO_MT9P012) || defined(CONFIG_VIDEO_MT9P012_MODULE)
{
I2C_BOARD_INFO("mt9p012", MT9P012_I2C_ADDR),
.platform_data = &sdp3430_mt9p012_platform_data,
},
#ifdef CONFIG_VIDEO_DW9710
{
I2C_BOARD_INFO(DW9710_NAME, DW9710_AF_I2C_ADDR),
.platform_data = &sdp3430_dw9710_platform_data,
},
#endif
#endif
#endif

#if defined(CONFIG_VIDEO_OV3640) || defined(CONFIG_VIDEO_OV3640_MODULE)
{
I2C_BOARD_INFO("ov3640", OV3640_I2C_ADDR),
//.platform_data = &sdp3430_ov3640_platform_data,
.platform_data = &devkit8000_ov3640_platform_data,
},
#endif
};
/*
#define TWL4030_VAUX4_DEV_GRP 0x23
#define TWL4030_VAUX4_DEDICATED 0x26

static int __init omap3430_i2c_init(void)
{
omap_register_i2c_bus(1, 2600, sdp3430_i2c_boardinfo,
ARRAY_SIZE(sdp3430_i2c_boardinfo));
omap_register_i2c_bus(2, 400, sdp3430_i2c_boardinfo_2,
ARRAY_SIZE(sdp3430_i2c_boardinfo_2));
omap_register_i2c_bus(3, 400, NULL, 0);
return 0;
}
*/
static int __init devkit8000_i2c_init(void)
{
omap_register_i2c_bus(1, 2600, devkit8000_i2c_boardinfo,
ARRAY_SIZE(devkit8000_i2c_boardinfo));
omap_register_i2c_bus(2, 400, devkit8000_i2c_boardinfo_2,
ARRAY_SIZE(devkit8000_i2c_boardinfo_2));
/* Bus 3 is attached to the DVI port where devices like the pico DLP
* projector don't work reliably with 400kHz */
omap_register_i2c_bus(3, 100, NULL, 0);
return 0;
}

댓글 없음:

댓글 쓰기