/* * Copyright (C) 2004 Samsung Electronics * SW.LEE * - based on Russell King : pcf8583.c * - added smdk24a0, smdk2440 * - added poseidon (s3c24a0+wavecom) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Driver for FIMC2.x Camera Decoder * */ //#include #include #include #include #include #include #include #include #include #include //#define CAMIF_DEBUG #include "../s3c_camif.h" #include "4xa_sensor.h" /* * Samsung's original code: * .camclk = 44000000, / * for 20 fps: 44MHz, for 12 fps (more * stable): 26MHz * / * * Experimenting with the controls yielded the following: * * PLL_CLK gets divided according to TCMD.Div8_r (1,00h) and then by * SEL_MAIN.Half_PCLK_Enable (5,8Fh) until it finally becomes PCLK. * * Div8_r = 0: 1/4, 1: 1/2 (default) * Half_PCLK_Enable = 0: 1/1 (default), 1: 1/2 * * Thus our 26.6 MHz MCLK becomes an 87.78 MHz PLL_CLK and eventually a PCLK * of 43.89 MHz. */ #define CAMCLK 26600000 /* 26.6 MHz */ static struct i2c_driver sensor_driver; /* This is an abstract CIS sensor for MSDMA input. */ camif_cis_t msdma_input = { .itu_fmt = CAMIF_ITU601, .order422 = CAMIF_CBYCRY, /* another case: YCRYCB */ .camclk = CAMCLK, .source_x = 800, .source_y = 600, .win_hor_ofst = 0, .win_ver_ofst = 0, .win_hor_ofst2 = 0, .win_ver_ofst2 = 0, .polarity_pclk = 0, .polarity_vsync = 1, .polarity_href = 0, .reset_type = CAMIF_EX_RESET_AL, .reset_udelay = 5000, }; camif_cis_t interlace_input = { .itu_fmt = CAMIF_ITU601, .order422 = CAMIF_CBYCRY, /* another case: YCRYCB */ .camclk = CAMCLK, .source_x = 800, .source_y = 600, .win_hor_ofst = 0, .win_ver_ofst = 0, .win_hor_ofst2 = 0, .win_ver_ofst2 = 0, .polarity_pclk = 0, .polarity_vsync = 1, .polarity_href = 0, .reset_type = CAMIF_EX_RESET_AL, .reset_udelay = 5000, }; #if defined(CONFIG_VIDEO_SAMSUNG_S5K4BA) static camif_cis_t data = { .itu_fmt = CAMIF_ITU601, .order422 = CAMIF_YCBYCR, .camclk = CAMCLK, .source_x = 800, .source_y = 600, .win_hor_ofst = 0, .win_ver_ofst = 0, .win_hor_ofst2 = 0, .win_ver_ofst2 = 0, .polarity_pclk = 0, .polarity_vsync = 1, .polarity_href = 0, .reset_type = CAMIF_EX_RESET_AL, .reset_udelay = 5000, }; s5k4xa_t s5k4ba_regs_mirror[S5K4BA_REGS]; #else #error No samsung CIS moudule here ! #endif camif_cis_t* get_initialized_cis(void) { if (data.init_sensor == 0) return NULL; return &data; } #define CAM_ID 0x5a static unsigned short ignore[] = { I2C_CLIENT_END }; static unsigned short normal_addr[] = { CAM_ID >> 1, I2C_CLIENT_END }; static const unsigned short *forces[] = { NULL }; static struct i2c_client_address_data addr_data = { .normal_i2c = normal_addr, .probe = ignore, .ignore = ignore, .forces = forces, }; static unsigned char sensor_read(struct i2c_client *client, unsigned char subaddr) { unsigned char buf = subaddr; struct i2c_msg msg = { .addr = client->addr, .flags = 0, .len = 1, .buf = &buf, }; if (i2c_transfer(client->adapter, &msg, 1) != 1) { printk(" I2C write Error\n"); return -EIO; } msg.flags = I2C_M_RD; if (i2c_transfer(client->adapter, &msg, 1) != 1) { printk(" I2C read Error\n"); return -EIO; } return buf; } static int sensor_write(struct i2c_client *client, unsigned char subaddr, unsigned char val) { unsigned char buf[2]; struct i2c_msg msg = { .addr = client->addr, .flags = 0, .len = 2, .buf = buf, }; buf[0] = subaddr; buf[1] = val; return i2c_transfer(client->adapter, &msg, 1) == 1 ? 0 : -EIO; } #if defined(CONFIG_VIDEO_SAMSUNG_S5K4BA) void inline sensor_init(struct i2c_client *sam_client) { int i; i = ARRAY_SIZE(s5k4ba_reg); for (i = 0; i < S5K4BA_INIT_REGS; i++) sensor_write(sam_client, s5k4ba_reg[i].subaddr, s5k4ba_reg[i].value); } #else #error No samsung CIS moudule ! #endif static int s5k4xa_attach(struct i2c_adapter *adap, int addr, int kind) { struct i2c_client *c; c = kmalloc(sizeof(*c), GFP_KERNEL); if (!c) return -ENOMEM; memset(c, 0, sizeof(struct i2c_client)); strcpy(c->name, "S5K4XA"); c->addr = addr; c->adapter = adap; c->driver = &sensor_driver; i2c_set_clientdata(c, &data); data.sensor = c; s3c_camif_register_sensor(c); return i2c_attach_client(c); } static int sensor_attach_adapter(struct i2c_adapter *adap) { extern void om_3d7k_camera_on(void); extern void om_3d7k_camera_off(void); int ret; s3c_camif_open_sensor(&data); om_3d7k_camera_on(); ret = i2c_probe(adap, &addr_data, s5k4xa_attach); om_3d7k_camera_off(); return ret; } static int sensor_detach(struct i2c_client *client) { i2c_detach_client(client); s3c_camif_unregister_sensor(client); return 0; } /* * Purpose: * This function only for SVGA Camera : 4BA */ static int change_sensor_size(struct i2c_client *client, int size) { int i; switch (size) { #if defined(CONFIG_VIDEO_SAMSUNG_S5K4BA) case SENSOR_QSVGA: for (i = 0; i < S5K4BA_QSVGA_REGS; i++) sensor_write(client, s5k4ba_reg_qsvga[i].subaddr, s5k4ba_reg_qsvga[i].value); break; case SENSOR_SVGA: for (i = 0; i < S5K4BA_SVGA_REGS; i++) sensor_write(client, s5k4ba_reg_svga[i].subaddr, s5k4ba_reg_svga[i].value); break; #else #error No samsung CIS moudule ! #endif default: panic("4xa_sensor.c: unexpect value\n"); } return 0; } static int change_sensor_wb(struct i2c_client *client, int type) { printk("[ *** Page 0, 4XA Sensor White Balance Mode ***]\n"); #if defined(CONFIG_VIDEO_SAMSUNG_S5K4BA) sensor_write(client, 0xFC, 0x0); sensor_write(client, 0x30, type); #endif switch(type){ case 0: default: printk(" -> AWB auto mode ]\n"); break; case 1: printk(" -> Indoor 3100 mode ]\n"); break; case 2: printk(" -> Outdoor 5100 mode ]\n"); break; case 3: printk(" -> Indoor 2000 mode ]\n"); break; case 4: printk(" -> AE/AWB halt ]\n"); break; case 5: printk(" -> Cloudy(6000) mode ]\n"); break; case 6: printk(" -> Sunny(8000) mode ]\n"); break; } return 0; } static int sensor_command(struct i2c_client *client, unsigned int cmd, void *arg) { switch (cmd) { case SENSOR_INIT: sensor_init(client); printk(KERN_INFO "External Camera initialized\n"); break; case USER_ADD: break; case USER_EXIT: break; case SENSOR_QSVGA: change_sensor_size(client, SENSOR_QSVGA); break; case SENSOR_VGA: change_sensor_size(client, SENSOR_VGA); break; case SENSOR_SVGA: change_sensor_size(client, SENSOR_SVGA); break; case SENSOR_SXGA: change_sensor_size(client, SENSOR_SXGA); break; case SENSOR_UXGA: change_sensor_size(client, SENSOR_UXGA); break; /* Todo case SENSOR_BRIGHTNESS: change_sensor_setting(); break; */ case SENSOR_WB: printk("[ *** 4XA Sensor White Balance , No mode ***]\n"); change_sensor_wb(client, (int) arg); break; default: panic("4xa_sensor.c: Unexpected Sensor Command\n"); break; } return 0; } static struct i2c_driver sensor_driver = { .driver = { .name = "s5k4xa", }, .id = I2C_DRIVERID_S5K_4XA, .attach_adapter = sensor_attach_adapter, .detach_client = sensor_detach, .command = sensor_command }; static __init int camif_sensor_init(void) { return i2c_add_driver(&sensor_driver); } static __init void camif_sensor_exit(void) { i2c_del_driver(&sensor_driver); } module_init(camif_sensor_init) module_exit(camif_sensor_exit) MODULE_AUTHOR("Jinsung, Yang "); MODULE_DESCRIPTION("I2C Client Driver For FIMC V4L2 Driver"); MODULE_LICENSE("GPL");