1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III |
4 | * flexcop-hw-filter.c - pid and mac address filtering and control functions |
5 | * see flexcop.c for copyright information |
6 | */ |
7 | #include "flexcop.h" |
8 | |
9 | static void flexcop_rcv_data_ctrl(struct flexcop_device *fc, int onoff) |
10 | { |
11 | flexcop_set_ibi_value(ctrl_208, Rcv_Data_sig, onoff); |
12 | deb_ts("rcv_data is now: '%s'\n" , onoff ? "on" : "off" ); |
13 | } |
14 | |
15 | void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff) |
16 | { |
17 | flexcop_set_ibi_value(ctrl_208, SMC_Enable_sig, onoff); |
18 | } |
19 | |
20 | static void flexcop_null_filter_ctrl(struct flexcop_device *fc, int onoff) |
21 | { |
22 | flexcop_set_ibi_value(ctrl_208, Null_filter_sig, onoff); |
23 | } |
24 | |
25 | void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]) |
26 | { |
27 | flexcop_ibi_value v418, v41c; |
28 | v41c = fc->read_ibi_reg(fc, mac_address_41c); |
29 | |
30 | v418.mac_address_418.MAC1 = mac[0]; |
31 | v418.mac_address_418.MAC2 = mac[1]; |
32 | v418.mac_address_418.MAC3 = mac[2]; |
33 | v418.mac_address_418.MAC6 = mac[3]; |
34 | v41c.mac_address_41c.MAC7 = mac[4]; |
35 | v41c.mac_address_41c.MAC8 = mac[5]; |
36 | |
37 | fc->write_ibi_reg(fc, mac_address_418, v418); |
38 | fc->write_ibi_reg(fc, mac_address_41c, v41c); |
39 | } |
40 | |
41 | void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff) |
42 | { |
43 | flexcop_set_ibi_value(ctrl_208, MAC_filter_Mode_sig, onoff); |
44 | } |
45 | |
46 | static void flexcop_pid_group_filter(struct flexcop_device *fc, |
47 | u16 pid, u16 mask) |
48 | { |
49 | /* index_reg_310.extra_index_reg need to 0 or 7 to work */ |
50 | flexcop_ibi_value v30c; |
51 | v30c.pid_filter_30c_ext_ind_0_7.Group_PID = pid; |
52 | v30c.pid_filter_30c_ext_ind_0_7.Group_mask = mask; |
53 | fc->write_ibi_reg(fc, pid_filter_30c, v30c); |
54 | } |
55 | |
56 | static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff) |
57 | { |
58 | flexcop_set_ibi_value(ctrl_208, Mask_filter_sig, onoff); |
59 | } |
60 | |
61 | /* this fancy define reduces the code size of the quite similar PID controlling of |
62 | * the first 6 PIDs |
63 | */ |
64 | |
65 | #define pid_ctrl(vregname,field,enablefield,trans_field,transval) \ |
66 | flexcop_ibi_value vpid = fc->read_ibi_reg(fc, vregname), \ |
67 | v208 = fc->read_ibi_reg(fc, ctrl_208); \ |
68 | vpid.vregname.field = onoff ? pid : 0x1fff; \ |
69 | vpid.vregname.trans_field = transval; \ |
70 | v208.ctrl_208.enablefield = onoff; \ |
71 | fc->write_ibi_reg(fc, vregname, vpid); \ |
72 | fc->write_ibi_reg(fc, ctrl_208, v208) |
73 | |
74 | static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc, |
75 | u16 pid, int onoff) |
76 | { |
77 | pid_ctrl(pid_filter_300, Stream1_PID, Stream1_filter_sig, |
78 | Stream1_trans, 0); |
79 | } |
80 | |
81 | static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc, |
82 | u16 pid, int onoff) |
83 | { |
84 | pid_ctrl(pid_filter_300, Stream2_PID, Stream2_filter_sig, |
85 | Stream2_trans, 0); |
86 | } |
87 | |
88 | static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc, |
89 | u16 pid, int onoff) |
90 | { |
91 | pid_ctrl(pid_filter_304, PCR_PID, PCR_filter_sig, PCR_trans, 0); |
92 | } |
93 | |
94 | static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc, |
95 | u16 pid, int onoff) |
96 | { |
97 | pid_ctrl(pid_filter_304, PMT_PID, PMT_filter_sig, PMT_trans, 0); |
98 | } |
99 | |
100 | static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc, |
101 | u16 pid, int onoff) |
102 | { |
103 | pid_ctrl(pid_filter_308, EMM_PID, EMM_filter_sig, EMM_trans, 0); |
104 | } |
105 | |
106 | static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc, |
107 | u16 pid, int onoff) |
108 | { |
109 | pid_ctrl(pid_filter_308, ECM_PID, ECM_filter_sig, ECM_trans, 0); |
110 | } |
111 | |
112 | static void flexcop_pid_control(struct flexcop_device *fc, |
113 | int index, u16 pid, int onoff) |
114 | { |
115 | if (pid == 0x2000) |
116 | return; |
117 | |
118 | deb_ts("setting pid: %5d %04x at index %d '%s'\n" , |
119 | pid, pid, index, onoff ? "on" : "off" ); |
120 | |
121 | /* First 6 can be buggy - skip over them if option set */ |
122 | if (fc->skip_6_hw_pid_filter) |
123 | index += 6; |
124 | |
125 | /* We could use bit magic here to reduce source code size. |
126 | * I decided against it, but to use the real register names */ |
127 | switch (index) { |
128 | case 0: |
129 | flexcop_pid_Stream1_PID_ctrl(fc, pid, onoff); |
130 | break; |
131 | case 1: |
132 | flexcop_pid_Stream2_PID_ctrl(fc, pid, onoff); |
133 | break; |
134 | case 2: |
135 | flexcop_pid_PCR_PID_ctrl(fc, pid, onoff); |
136 | break; |
137 | case 3: |
138 | flexcop_pid_PMT_PID_ctrl(fc, pid, onoff); |
139 | break; |
140 | case 4: |
141 | flexcop_pid_EMM_PID_ctrl(fc, pid, onoff); |
142 | break; |
143 | case 5: |
144 | flexcop_pid_ECM_PID_ctrl(fc, pid, onoff); |
145 | break; |
146 | default: |
147 | if (fc->has_32_hw_pid_filter && index < 38) { |
148 | flexcop_ibi_value vpid, vid; |
149 | |
150 | /* set the index */ |
151 | vid = fc->read_ibi_reg(fc, index_reg_310); |
152 | vid.index_reg_310.index_reg = index - 6; |
153 | fc->write_ibi_reg(fc, index_reg_310, vid); |
154 | |
155 | vpid = fc->read_ibi_reg(fc, pid_n_reg_314); |
156 | vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff; |
157 | vpid.pid_n_reg_314.PID_enable_bit = onoff; |
158 | fc->write_ibi_reg(fc, pid_n_reg_314, vpid); |
159 | } |
160 | break; |
161 | } |
162 | } |
163 | |
164 | static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc, int onoff) |
165 | { |
166 | if (fc->fullts_streaming_state != onoff) { |
167 | deb_ts("%s full TS transfer\n" ,onoff ? "enabling" : "disabling" ); |
168 | flexcop_pid_group_filter(fc, pid: 0, mask: 0x1fe0 * (!onoff)); |
169 | flexcop_pid_group_filter_ctrl(fc, onoff); |
170 | fc->fullts_streaming_state = onoff; |
171 | } |
172 | return 0; |
173 | } |
174 | |
175 | int flexcop_pid_feed_control(struct flexcop_device *fc, |
176 | struct dvb_demux_feed *dvbdmxfeed, int onoff) |
177 | { |
178 | int max_pid_filter = 6; |
179 | |
180 | max_pid_filter -= 6 * fc->skip_6_hw_pid_filter; |
181 | max_pid_filter += 32 * fc->has_32_hw_pid_filter; |
182 | |
183 | fc->feedcount += onoff ? 1 : -1; /* the number of PIDs/Feed currently requested */ |
184 | if (dvbdmxfeed->index >= max_pid_filter) |
185 | fc->extra_feedcount += onoff ? 1 : -1; |
186 | |
187 | /* toggle complete-TS-streaming when: |
188 | * - pid_filtering is not enabled and it is the first or last feed requested |
189 | * - pid_filtering is enabled, |
190 | * - but the number of requested feeds is exceeded |
191 | * - or the requested pid is 0x2000 */ |
192 | |
193 | if (!fc->pid_filtering && fc->feedcount == onoff) |
194 | flexcop_toggle_fullts_streaming(fc, onoff); |
195 | |
196 | if (fc->pid_filtering) { |
197 | flexcop_pid_control \ |
198 | (fc, index: dvbdmxfeed->index, pid: dvbdmxfeed->pid, onoff); |
199 | |
200 | if (fc->extra_feedcount > 0) |
201 | flexcop_toggle_fullts_streaming(fc, onoff: 1); |
202 | else if (dvbdmxfeed->pid == 0x2000) |
203 | flexcop_toggle_fullts_streaming(fc, onoff); |
204 | else |
205 | flexcop_toggle_fullts_streaming(fc, onoff: 0); |
206 | } |
207 | |
208 | /* if it was the first or last feed request change the stream-status */ |
209 | if (fc->feedcount == onoff) { |
210 | flexcop_rcv_data_ctrl(fc, onoff); |
211 | if (fc->stream_control) /* device specific stream control */ |
212 | fc->stream_control(fc, onoff); |
213 | |
214 | /* feeding stopped -> reset the flexcop filter*/ |
215 | if (onoff == 0) { |
216 | flexcop_reset_block_300(fc); |
217 | flexcop_hw_filter_init(fc); |
218 | } |
219 | } |
220 | return 0; |
221 | } |
222 | EXPORT_SYMBOL(flexcop_pid_feed_control); |
223 | |
224 | void flexcop_hw_filter_init(struct flexcop_device *fc) |
225 | { |
226 | int i; |
227 | flexcop_ibi_value v; |
228 | int max_pid_filter = 6; |
229 | |
230 | max_pid_filter -= 6 * fc->skip_6_hw_pid_filter; |
231 | max_pid_filter += 32 * fc->has_32_hw_pid_filter; |
232 | |
233 | for (i = 0; i < max_pid_filter; i++) |
234 | flexcop_pid_control(fc, index: i, pid: 0x1fff, onoff: 0); |
235 | |
236 | flexcop_pid_group_filter(fc, pid: 0, mask: 0x1fe0); |
237 | flexcop_pid_group_filter_ctrl(fc, onoff: 0); |
238 | |
239 | v = fc->read_ibi_reg(fc, pid_filter_308); |
240 | v.pid_filter_308.EMM_filter_4 = 1; |
241 | v.pid_filter_308.EMM_filter_6 = 0; |
242 | fc->write_ibi_reg(fc, pid_filter_308, v); |
243 | |
244 | flexcop_null_filter_ctrl(fc, onoff: 1); |
245 | } |
246 | |