#include "ivtv.h" /* i2c implementation for iTVC15 chip, ivtv project. * Author: Kevin Thayer (nufan_wfk at yahoo.com) * License: GPL * http://www.sourceforge.net/projects/ivtv/ */ /* moved here from ivtv.h */ static int writeregs(struct i2c_client *client, const unsigned char *regs); static int attach_inform(struct i2c_client *client); static int detach_inform(struct i2c_client *client); int writereg(struct i2c_client *client, unsigned char reg, unsigned char data) { int ret; unsigned char msg[] = {0x1f, 0x00}; printk("<1>writing reg 0x%02x, data 0x%02x\n", reg,data); msg[0]=reg; msg[1]=data; ret=i2c_master_send(client, msg, 2); if (ret!=2) printk("writereg error\n"); return ret; } static int writeregs(struct i2c_client *client, const unsigned char *regs) { unsigned char reg, data; while (*regs!=0x00) { reg =*(regs++); data=*(regs++); if (writereg(client, reg, data)<0) return -1; } return 0; } static struct i2c_adapter ivtv_i2c_adapter_template = { .name = "ivtv i2c driver", .id = I2C_HW_B_BT848, /*algo-bit is OR'd with this*/ .algo = NULL, /*set by i2c-algo-bit*/ .algo_data = NULL, /*filled from template*/ .client_register = attach_inform, .client_unregister = detach_inform, #ifndef NEW_I2C /* pre i2c-2.8.0 */ .inc_use = ivtv_i2c_inc, /*inc usage*/ .dec_use = ivtv_i2c_dec, /*dec usage*/ #else /* i2c-2.8.0 and later */ .owner = THIS_MODULE, #endif }; static struct i2c_algo_bit_data ivtv_i2c_algo_template = { NULL, /*??*/ ivtv_setsda, /*setsda function*/ ivtv_setscl, /*"*/ ivtv_getsda, /*"*/ ivtv_getscl, /*"*/ 5, /*udelay or mdelay*/ 5, /*whatever above isn't*/ 200 /*timeout*/ }; void ivtv_setscl(void *data, int state) { struct ivtv *itv = (struct ivtv *)data; if (state) itv->i2c_state |= 0x01; else itv->i2c_state &= ~0x01; /* write them out */ /* write bits are inverted */ writel(~itv->i2c_state,(itv->reg_mem + IVTV_REG_I2C_SETSCL_OFFSET)); } void ivtv_setsda(void *data, int state) { struct ivtv *itv = (struct ivtv *)data; if (state) itv->i2c_state |= 0x01; else itv->i2c_state &= ~0x01; /* write them out */ /* write bits are inverted */ writel(~itv->i2c_state,(itv->reg_mem + IVTV_REG_I2C_SETSDA_OFFSET)); } int ivtv_getscl(void *data) { struct ivtv *itv = (struct ivtv *)data; return readb(itv->reg_mem + IVTV_REG_I2C_GETSCL_OFFSET); } int ivtv_getsda(void *data) { struct ivtv *itv = (struct ivtv *)data; return readb(itv->reg_mem + IVTV_REG_I2C_GETSDA_OFFSET); } static struct i2c_client ivtv_i2c_client_template = { name: "ivtv internal use only", id: -1, }; static int attach_inform(struct i2c_client *client) { struct ivtv *itv = (struct ivtv*)client->adapter->data; int i; IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c client attach\n"); for (i = 0; i < I2C_CLIENTS_MAX; i++) { if (itv->i2c_clients[i] == NULL) { itv->i2c_clients[i] = client; break; } } IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c attach [client=%s,%s]\n", client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); return 0; } static int detach_inform(struct i2c_client *client) { struct ivtv *itv = (struct ivtv*)client->adapter->data; int i; IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c client detach\n"); for (i = 0; i < I2C_CLIENTS_MAX; i++) { if (itv->i2c_clients[i] == client) { itv->i2c_clients[i] = NULL; break; } } IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c detach [client=%s,%s]\n", client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); return 0; } void ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg) { int i; IVTV_DEBUG(IVTV_DEBUG_I2C, "call_i2c_client\n"); for (i = 0; i < I2C_CLIENTS_MAX; i++) { if (NULL == itv->i2c_clients[i]) continue; if (NULL == itv->i2c_clients[i]->driver->command) continue; if (addr == itv->i2c_clients[i]->addr) { itv->i2c_clients[i]->driver->command( itv->i2c_clients[i],cmd,arg); return; } } IVTV_DEBUG(IVTV_DEBUG_ERR, "i2c client addr: %d not found!\n",addr); } int ivtv_i2c_direct(struct ivtv *itv, int addr, const unsigned char *regs) { int i, ret=0; IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c_direct\n"); for (i = 0; i < I2C_CLIENTS_MAX; i++) { if (NULL == itv->i2c_clients[i]) continue; if (addr == itv->i2c_clients[i]->addr) { ret = writeregs(itv->i2c_clients[i], regs); break; } } if (ret) { IVTV_DEBUG(IVTV_DEBUG_ERR, "error %d writing reg\n", ret); return -EIO; } return 0; } #ifndef NEW_I2C /* pre i2c-2.8.0 */ void ivtv_i2c_inc(struct i2c_adapter *adapter) { IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c increasing usage count\n"); MOD_INC_USE_COUNT; } void ivtv_i2c_dec(struct i2c_adapter *adapter) { IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c decreasing usage count\n"); MOD_DEC_USE_COUNT; } #else /* i2c-2.8.0 and later */ #endif /* init + register i2c algo-bit adapter */ int __devinit init_ivtv_i2c(struct ivtv *itv) { IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c init\n"); memcpy(&itv->i2c_adap, &ivtv_i2c_adapter_template, sizeof(struct i2c_adapter)); memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, sizeof(struct i2c_algo_bit_data)); memcpy(&itv->i2c_client, &ivtv_i2c_client_template, sizeof(struct i2c_client)); sprintf(itv->i2c_adap.name+strlen(itv->i2c_adap.name), " #%d", itv->num); itv->i2c_algo.data = itv; itv->i2c_adap.data = itv; itv->i2c_adap.algo_data = &itv->i2c_algo; itv->i2c_client.adapter = &itv->i2c_adap; IVTV_DEBUG(IVTV_DEBUG_I2C, "setting scl and sda to 1\n"); ivtv_setscl(itv,1); ivtv_setsda(itv,1); itv->i2c_rc = i2c_bit_add_bus(&itv->i2c_adap); return itv->i2c_rc; } void __devexit exit_ivtv_i2c(struct ivtv *itv) { IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c exit\n"); i2c_bit_del_bus(&itv->i2c_adap); }