⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cx18-i2c.c

📁 trident tm5600的linux驱动
💻 C
字号:
/* *  cx18 I2C functions * *  Derived from ivtv-i2c.c * *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl> * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *  02111-1307  USA */#include "cx18-driver.h"#include "cx18-io.h"#include "cx18-cards.h"#include "cx18-gpio.h"#include "cx18-av-core.h"#include "cx18-i2c.h"#define CX18_REG_I2C_1_WR   0xf15000#define CX18_REG_I2C_1_RD   0xf15008#define CX18_REG_I2C_2_WR   0xf25100#define CX18_REG_I2C_2_RD   0xf25108#define SETSCL_BIT      0x0001#define SETSDL_BIT      0x0002#define GETSCL_BIT      0x0004#define GETSDL_BIT      0x0008#define CX18_CS5345_I2C_ADDR		0x4c/* This array should match the CX18_HW_ defines */static const u8 hw_driverids[] = {	I2C_DRIVERID_TUNER,	I2C_DRIVERID_TVEEPROM,	I2C_DRIVERID_CS5345,	0, 		/* CX18_HW_GPIO dummy driver ID */	0 		/* CX18_HW_CX23418 dummy driver ID */};/* This array should match the CX18_HW_ defines */static const u8 hw_addrs[] = {	0,	0,	CX18_CS5345_I2C_ADDR,	0, 		/* CX18_HW_GPIO dummy driver ID */	0,		/* CX18_HW_CX23418 dummy driver ID */};/* This array should match the CX18_HW_ defines *//* This might well become a card-specific array */static const u8 hw_bus[] = {	0,	0,	0,	0, 		/* CX18_HW_GPIO dummy driver ID */	0,		/* CX18_HW_CX23418 dummy driver ID */};/* This array should match the CX18_HW_ defines */static const char * const hw_devicenames[] = {	"tuner",	"tveeprom",	"cs5345",	"gpio",	"cx23418",};int cx18_i2c_register(struct cx18 *cx, unsigned idx){#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)	struct i2c_board_info info;	struct i2c_client *c;	u8 id, bus;	int i;	CX18_DEBUG_I2C("i2c client register\n");	if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)		return -1;	id = hw_driverids[idx];	bus = hw_bus[idx];	memset(&info, 0, sizeof(info));#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)	strlcpy(info.driver_name, hw_devicenames[idx],			sizeof(info.driver_name));#else	strlcpy(info.type, hw_devicenames[idx], sizeof(info.type));#endif	info.addr = hw_addrs[idx];	for (i = 0; i < I2C_CLIENTS_MAX; i++)		if (cx->i2c_clients[i] == NULL)			break;	if (i == I2C_CLIENTS_MAX) {		CX18_ERR("insufficient room for new I2C client!\n");		return -ENOMEM;	}	if (id != I2C_DRIVERID_TUNER) {		c = i2c_new_device(&cx->i2c_adap[bus], &info);		if (c->driver == NULL)			i2c_unregister_device(c);		else			cx->i2c_clients[i] = c;		return cx->i2c_clients[i] ? 0 : -ENODEV;	}	/* special tuner handling */	c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio);	if (c && c->driver == NULL)		i2c_unregister_device(c);	else if (c)		cx->i2c_clients[i++] = c;	c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod);	if (c && c->driver == NULL)		i2c_unregister_device(c);	else if (c)		cx->i2c_clients[i++] = c;	c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv);	if (c && c->driver == NULL)		i2c_unregister_device(c);	else if (c)		cx->i2c_clients[i++] = c;	return 0;#else	return 0;#endif}static int attach_inform(struct i2c_client *client){#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)	struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);	int i;	CX18_DEBUG_I2C("i2c client attach\n");	for (i = 0; i < I2C_CLIENTS_MAX; i++) {		if (cx->i2c_clients[i] == NULL) {			cx->i2c_clients[i] = client;			break;		}	}	if (i == I2C_CLIENTS_MAX)		CX18_ERR("Insufficient room for new I2C client\n");#endif	return 0;}static int detach_inform(struct i2c_client *client){	int i;	struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);	CX18_DEBUG_I2C("i2c client detach\n");	for (i = 0; i < I2C_CLIENTS_MAX; i++) {		if (cx->i2c_clients[i] == client) {			cx->i2c_clients[i] = NULL;			break;		}	}	CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n",		   client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");	return 0;}static void cx18_setscl(void *data, int state){	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;	u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;	u32 r = cx18_read_reg(cx, addr);	if (state)		cx18_write_reg_sync(cx, r | SETSCL_BIT, addr);	else		cx18_write_reg_sync(cx, r & ~SETSCL_BIT, addr);}static void cx18_setsda(void *data, int state){	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;	u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;	u32 r = cx18_read_reg(cx, addr);	if (state)		cx18_write_reg_sync(cx, r | SETSDL_BIT, addr);	else		cx18_write_reg_sync(cx, r & ~SETSDL_BIT, addr);}static int cx18_getscl(void *data){	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;	u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;	return cx18_read_reg(cx, addr) & GETSCL_BIT;}static int cx18_getsda(void *data){	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;	u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;	return cx18_read_reg(cx, addr) & GETSDL_BIT;}/* template for i2c-bit-algo */static struct i2c_adapter cx18_i2c_adap_template = {	.name = "cx18 i2c driver",	.id = I2C_HW_B_CX2341X,	.algo = NULL,                   /* set by i2c-algo-bit */	.algo_data = NULL,              /* filled from template */	.client_register = attach_inform,	.client_unregister = detach_inform,	.owner = THIS_MODULE,#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)	.class = I2C_CLASS_TV_ANALOG,#endif};#define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */#define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */static struct i2c_algo_bit_data cx18_i2c_algo_template = {	.setsda		= cx18_setsda,	.setscl		= cx18_setscl,	.getsda		= cx18_getsda,	.getscl		= cx18_getscl,	.udelay		= CX18_SCL_PERIOD/2,       /* 1/2 clock period in usec*/	.timeout	= CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */};static struct i2c_client cx18_i2c_client_template = {	.name = "cx18 internal",};int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg){	struct i2c_client *client;	int retval;	int i;	CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);	for (i = 0; i < I2C_CLIENTS_MAX; i++) {		client = cx->i2c_clients[i];		if (client == NULL || client->driver == NULL ||				client->driver->command == NULL)			continue;		if (addr == client->addr) {			retval = client->driver->command(client, cmd, arg);			return retval;		}	}	if (cmd != VIDIOC_G_CHIP_IDENT)		CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n",			       addr, cmd);	return -ENODEV;}/* Find the i2c device based on the driver ID and return   its i2c address or -ENODEV if no matching device was found. */static int cx18_i2c_id_addr(struct cx18 *cx, u32 id){	struct i2c_client *client;	int retval = -ENODEV;	int i;	for (i = 0; i < I2C_CLIENTS_MAX; i++) {		client = cx->i2c_clients[i];		if (client == NULL || client->driver == NULL)			continue;		if (id == client->driver->id) {			retval = client->addr;			break;		}	}	return retval;}/* Find the i2c device name matching the DRIVERID */static const char *cx18_i2c_id_name(u32 id){	int i;	for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)		if (hw_driverids[i] == id)			return hw_devicenames[i];	return "unknown device";}/* Find the i2c device name matching the CX18_HW_ flag */static const char *cx18_i2c_hw_name(u32 hw){	int i;	for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)		if (1 << i == hw)			return hw_devicenames[i];	return "unknown device";}/* Find the i2c device matching the CX18_HW_ flag and return   its i2c address or -ENODEV if no matching device was found. */int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw){	int i;	for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)		if (1 << i == hw)			return cx18_i2c_id_addr(cx, hw_driverids[i]);	return -ENODEV;}/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing.   If hw == CX18_HW_GPIO then call the gpio handler. */int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg){	int addr;	if (hw == 0)		return 0;	if (hw == CX18_HW_GPIO)		return cx18_gpio(cx, cmd, arg);	if (hw == CX18_HW_CX23418)		return cx18_av_cmd(cx, cmd, arg);	addr = cx18_i2c_hw_addr(cx, hw);	if (addr < 0) {		CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n",			       hw, cx18_i2c_hw_name(hw), cmd);		return addr;	}	return cx18_call_i2c_client(cx, addr, cmd, arg);}/* Calls i2c device based on I2C driver ID. */int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg){	int addr;	addr = cx18_i2c_id_addr(cx, id);	if (addr < 0) {		if (cmd != VIDIOC_G_CHIP_IDENT)			CX18_ERR("i2c ID 0x%08x (%s) not found for cmd 0x%x!\n",				id, cx18_i2c_id_name(id), cmd);		return addr;	}	return cx18_call_i2c_client(cx, addr, cmd, arg);}/* broadcast cmd for all I2C clients and for the gpio subsystem */void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg){	if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) {		CX18_ERR("adapter is not set\n");		return;	}	cx18_av_cmd(cx, cmd, arg);	i2c_clients_command(&cx->i2c_adap[0], cmd, arg);	i2c_clients_command(&cx->i2c_adap[1], cmd, arg);	if (cx->hw_flags & CX18_HW_GPIO)		cx18_gpio(cx, cmd, arg);}/* init + register i2c algo-bit adapter */int init_cx18_i2c(struct cx18 *cx){	int i;	CX18_DEBUG_I2C("i2c init\n");	/* Sanity checks for the I2C hardware arrays. They must be the	 * same size and GPIO/CX23418 must be the last entries.	 */	if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) ||	    ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||	    CX18_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 2)) ||	    CX18_HW_CX23418 != (1 << (ARRAY_SIZE(hw_addrs) - 1)) ||	    hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) {		CX18_ERR("Mismatched I2C hardware arrays\n");		return -ENODEV;	}	for (i = 0; i < 2; i++) {		memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,			sizeof(struct i2c_adapter));		memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,			sizeof(struct i2c_algo_bit_data));		cx->i2c_algo_cb_data[i].cx = cx;		cx->i2c_algo_cb_data[i].bus_index = i;		cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];		cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];		sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),				" #%d-%d", cx->num, i);		i2c_set_adapdata(&cx->i2c_adap[i], cx);		memcpy(&cx->i2c_client[i], &cx18_i2c_client_template,			sizeof(struct i2c_client));		sprintf(cx->i2c_client[i].name +				strlen(cx->i2c_client[i].name), "%d", i);		cx->i2c_client[i].adapter = &cx->i2c_adap[i];#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)		cx->i2c_adap[i].dev.parent = &cx->dev->dev;#endif	}	if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) {		/* Reset/Unreset I2C hardware block */		/* Clock select 220MHz */		cx18_write_reg(cx, 0x10000000, 0xc71004);		/* Clock Enable */		cx18_write_reg_sync(cx, 0x10001000, 0xc71024);	}	/* courtesy of Steven Toth <stoth@hauppauge.com> */	cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c);	mdelay(10);	cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c);	mdelay(10);	cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c);	mdelay(10);	/* Set to edge-triggered intrs. */	cx18_write_reg_sync(cx, 0x00c00000, 0xc730c8);	/* Clear any stale intrs */	cx18_write_reg_sync(cx, 0x00c00000, 0xc730c4);	/* Hw I2C1 Clock Freq ~100kHz */	cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);	cx18_setscl(&cx->i2c_algo_cb_data[0], 1);	cx18_setsda(&cx->i2c_algo_cb_data[0], 1);	/* Hw I2C2 Clock Freq ~100kHz */	cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR);	cx18_setscl(&cx->i2c_algo_cb_data[1], 1);	cx18_setsda(&cx->i2c_algo_cb_data[1], 1);	cx18_reset_i2c_slaves_gpio(cx);	return i2c_bit_add_bus(&cx->i2c_adap[0]) ||		i2c_bit_add_bus(&cx->i2c_adap[1]);}void exit_cx18_i2c(struct cx18 *cx){	int i;	CX18_DEBUG_I2C("i2c exit\n");	cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_1_WR) | 4,							CX18_REG_I2C_1_WR);	cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_2_WR) | 4,							CX18_REG_I2C_2_WR);	for (i = 0; i < 2; i++) {#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)		i2c_del_adapter(&cx->i2c_adap[i]);#else		i2c_bit_del_bus(&cx->i2c_adap[i]);#endif	}}/*   Hauppauge HVR1600 should have:   32 cx24227   98 unknown   a0 eeprom   c2 tuner   e? zilog ir   */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -