Commit | Line | Data |
---|---|---|
42afd061 PB |
1 | #include <linux/i2c.h> |
2 | ||
3 | #include "dibx000_common.h" | |
4 | ||
5 | static int debug; | |
6 | module_param(debug, int, 0644); | |
7 | MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); | |
8 | ||
9 | #define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiBX000: "); printk(args); } } while (0) | |
10 | ||
11 | static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val) | |
12 | { | |
13 | u8 b[4] = { | |
14 | (reg >> 8) & 0xff, reg & 0xff, | |
15 | (val >> 8) & 0xff, val & 0xff, | |
16 | }; | |
17 | struct i2c_msg msg = { | |
77e2c0f5 | 18 | .addr = mst->i2c_addr,.flags = 0,.buf = b,.len = 4 |
42afd061 PB |
19 | }; |
20 | return i2c_transfer(mst->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; | |
21 | } | |
22 | ||
23 | ||
77e2c0f5 PB |
24 | static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, |
25 | enum dibx000_i2c_interface intf) | |
42afd061 PB |
26 | { |
27 | if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) { | |
77e2c0f5 | 28 | dprintk("selecting interface: %d\n", intf); |
42afd061 PB |
29 | mst->selected_interface = intf; |
30 | return dibx000_write_word(mst, mst->base_reg + 4, intf); | |
31 | } | |
32 | return 0; | |
33 | } | |
34 | ||
77e2c0f5 PB |
35 | static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], |
36 | u8 addr, int onoff) | |
42afd061 PB |
37 | { |
38 | u16 val; | |
39 | ||
40 | ||
41 | if (onoff) | |
77e2c0f5 | 42 | val = addr << 8; // bit 7 = use master or not, if 0, the gate is open |
42afd061 PB |
43 | else |
44 | val = 1 << 7; | |
45 | ||
46 | if (mst->device_rev > DIB7000) | |
47 | val <<= 1; | |
48 | ||
49 | tx[0] = (((mst->base_reg + 1) >> 8) & 0xff); | |
77e2c0f5 | 50 | tx[1] = ((mst->base_reg + 1) & 0xff); |
42afd061 PB |
51 | tx[2] = val >> 8; |
52 | tx[3] = val & 0xff; | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
57 | static u32 dibx000_i2c_func(struct i2c_adapter *adapter) | |
58 | { | |
59 | return I2C_FUNC_I2C; | |
60 | } | |
61 | ||
77e2c0f5 PB |
62 | static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, |
63 | struct i2c_msg msg[], int num) | |
42afd061 PB |
64 | { |
65 | struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); | |
66 | struct i2c_msg m[2 + num]; | |
67 | u8 tx_open[4], tx_close[4]; | |
68 | ||
77e2c0f5 | 69 | memset(m, 0, sizeof(struct i2c_msg) * (2 + num)); |
42afd061 PB |
70 | |
71 | dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); | |
72 | ||
77e2c0f5 | 73 | dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1); |
42afd061 | 74 | m[0].addr = mst->i2c_addr; |
77e2c0f5 PB |
75 | m[0].buf = tx_open; |
76 | m[0].len = 4; | |
42afd061 PB |
77 | |
78 | memcpy(&m[1], msg, sizeof(struct i2c_msg) * num); | |
79 | ||
80 | dibx000_i2c_gate_ctrl(mst, tx_close, 0, 0); | |
77e2c0f5 PB |
81 | m[num + 1].addr = mst->i2c_addr; |
82 | m[num + 1].buf = tx_close; | |
83 | m[num + 1].len = 4; | |
42afd061 | 84 | |
77e2c0f5 | 85 | return i2c_transfer(mst->i2c_adap, m, 2 + num) == 2 + num ? num : -EIO; |
42afd061 PB |
86 | } |
87 | ||
88 | static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { | |
77e2c0f5 | 89 | .master_xfer = dibx000_i2c_gated_tuner_xfer, |
42afd061 PB |
90 | .functionality = dibx000_i2c_func, |
91 | }; | |
92 | ||
77e2c0f5 PB |
93 | struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, |
94 | enum dibx000_i2c_interface intf, | |
95 | int gating) | |
42afd061 PB |
96 | { |
97 | struct i2c_adapter *i2c = NULL; | |
98 | ||
99 | switch (intf) { | |
77e2c0f5 PB |
100 | case DIBX000_I2C_INTERFACE_TUNER: |
101 | if (gating) | |
102 | i2c = &mst->gated_tuner_i2c_adap; | |
103 | break; | |
104 | default: | |
105 | printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n"); | |
106 | break; | |
42afd061 PB |
107 | } |
108 | ||
109 | return i2c; | |
110 | } | |
77e2c0f5 | 111 | |
42afd061 PB |
112 | EXPORT_SYMBOL(dibx000_get_i2c_adapter); |
113 | ||
77e2c0f5 PB |
114 | void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst) |
115 | { | |
116 | /* initialize the i2c-master by closing the gate */ | |
117 | u8 tx[4]; | |
118 | struct i2c_msg m = {.addr = mst->i2c_addr,.buf = tx,.len = 4 }; | |
119 | ||
120 | dibx000_i2c_gate_ctrl(mst, tx, 0, 0); | |
121 | i2c_transfer(mst->i2c_adap, &m, 1); | |
122 | mst->selected_interface = 0xff; // the first time force a select of the I2C | |
123 | dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); | |
124 | } | |
125 | ||
126 | EXPORT_SYMBOL(dibx000_reset_i2c_master); | |
127 | ||
128 | static int i2c_adapter_init(struct i2c_adapter *i2c_adap, | |
129 | struct i2c_algorithm *algo, const char *name, | |
130 | struct dibx000_i2c_master *mst) | |
42afd061 | 131 | { |
2096b956 | 132 | strncpy(i2c_adap->name, name, sizeof(i2c_adap->name)); |
77e2c0f5 | 133 | i2c_adap->class = I2C_CLASS_TV_DIGITAL, i2c_adap->algo = algo; |
42afd061 PB |
134 | i2c_adap->algo_data = NULL; |
135 | i2c_set_adapdata(i2c_adap, mst); | |
136 | if (i2c_add_adapter(i2c_adap) < 0) | |
137 | return -ENODEV; | |
138 | return 0; | |
139 | } | |
140 | ||
77e2c0f5 PB |
141 | int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, |
142 | struct i2c_adapter *i2c_adap, u8 i2c_addr) | |
42afd061 PB |
143 | { |
144 | u8 tx[4]; | |
77e2c0f5 | 145 | struct i2c_msg m = {.addr = i2c_addr >> 1,.buf = tx,.len = 4 }; |
42afd061 PB |
146 | |
147 | mst->device_rev = device_rev; | |
77e2c0f5 PB |
148 | mst->i2c_adap = i2c_adap; |
149 | mst->i2c_addr = i2c_addr >> 1; | |
42afd061 | 150 | |
77e2c0f5 | 151 | if (device_rev == DIB7000P || device_rev == DIB8000) |
42afd061 PB |
152 | mst->base_reg = 1024; |
153 | else | |
154 | mst->base_reg = 768; | |
155 | ||
77e2c0f5 PB |
156 | if (i2c_adapter_init |
157 | (&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, | |
158 | "DiBX000 tuner I2C bus", mst) != 0) | |
159 | printk(KERN_ERR | |
160 | "DiBX000: could not initialize the tuner i2c_adapter\n"); | |
42afd061 PB |
161 | |
162 | /* initialize the i2c-master by closing the gate */ | |
163 | dibx000_i2c_gate_ctrl(mst, tx, 0, 0); | |
164 | ||
165 | return i2c_transfer(i2c_adap, &m, 1) == 1; | |
166 | } | |
77e2c0f5 | 167 | |
42afd061 PB |
168 | EXPORT_SYMBOL(dibx000_init_i2c_master); |
169 | ||
170 | void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst) | |
171 | { | |
172 | i2c_del_adapter(&mst->gated_tuner_i2c_adap); | |
173 | } | |
77e2c0f5 | 174 | |
42afd061 | 175 | EXPORT_SYMBOL(dibx000_exit_i2c_master); |
c6d74c2c PB |
176 | |
177 | MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>"); | |
178 | MODULE_DESCRIPTION("Common function the DiBcom demodulator family"); | |
179 | MODULE_LICENSE("GPL"); |