aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/pci/b2c2/flexcop-hw-filter.c
blob: 77e45475f4c7d1be3d9f2bbf38a87e27a8d5e440 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
 * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
 * flexcop-hw-filter.c - pid and mac address filtering and control functions
 * see flexcop.c for copyright information
 */
#include "flexcop.h"

static void flexcop_rcv_data_ctrl(struct flexcop_device *fc, int onoff)
{
	flexcop_set_ibi_value(ctrl_208, Rcv_Data_sig, onoff);
	deb_ts("rcv_data is now: '%s'\n", onoff ? "on" : "off");
}

void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff)
{
	flexcop_set_ibi_value(ctrl_208, SMC_Enable_sig, onoff);
}

static void flexcop_null_filter_ctrl(struct flexcop_device *fc, int onoff)
{
	flexcop_set_ibi_value(ctrl_208, Null_filter_sig, onoff);
}

void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6])
{
	flexcop_ibi_value v418, v41c;
	v41c = fc->read_ibi_reg(fc, mac_address_41c);

	v418.mac_address_418.MAC1 = mac[0];
	v418.mac_address_418.MAC2 = mac[1];
	v418.mac_address_418.MAC3 = mac[2];
	v418.mac_address_418.MAC6 = mac[3];
	v41c.mac_address_41c.MAC7 = mac[4];
	v41c.mac_address_41c.MAC8 = mac[5];

	fc->write_ibi_reg(fc, mac_address_418, v418);
	fc->write_ibi_reg(fc, mac_address_41c, v41c);
}

void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff)
{
	flexcop_set_ibi_value(ctrl_208, MAC_filter_Mode_sig, onoff);
}

static void flexcop_pid_group_filter(struct flexcop_device *fc,
		u16 pid, u16 mask)
{
	/* index_reg_310.extra_index_reg need to 0 or 7 to work */
	flexcop_ibi_value v30c;
	v30c.pid_filter_30c_ext_ind_0_7.Group_PID = pid;
	v30c.pid_filter_30c_ext_ind_0_7.Group_mask = mask;
	fc->write_ibi_reg(fc, pid_filter_30c, v30c);
}

static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff)
{
	flexcop_set_ibi_value(ctrl_208, Mask_filter_sig, onoff);
}

/* this fancy define reduces the code size of the quite similar PID controlling of
 * the first 6 PIDs
 */

#define pid_ctrl(vregname,field,enablefield,trans_field,transval) \
	flexcop_ibi_value vpid = fc->read_ibi_reg(fc, vregname), \
v208 = fc->read_ibi_reg(fc, ctrl_208); \
vpid.vregname.field = onoff ? pid : 0x1fff; \
vpid.vregname.trans_field = transval; \
v208.ctrl_208.enablefield = onoff; \
fc->write_ibi_reg(fc, vregname, vpid); \
fc->write_ibi_reg(fc, ctrl_208, v208);

static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc,
		u16 pid, int onoff)
{
	pid_ctrl(pid_filter_300, Stream1_PID, Stream1_filter_sig,
			Stream1_trans, 0);
}

static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc,
		u16 pid, int onoff)
{
	pid_ctrl(pid_filter_300, Stream2_PID, Stream2_filter_sig,
			Stream2_trans, 0);
}

static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc,
		u16 pid, int onoff)
{
	pid_ctrl(pid_filter_304, PCR_PID, PCR_filter_sig, PCR_trans, 0);
}

static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc,
		u16 pid, int onoff)
{
	pid_ctrl(pid_filter_304, PMT_PID, PMT_filter_sig, PMT_trans, 0);
}

static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc,
		u16 pid, int onoff)
{
	pid_ctrl(pid_filter_308, EMM_PID, EMM_filter_sig, EMM_trans, 0);
}

static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc,
		u16 pid, int onoff)
{
	pid_ctrl(pid_filter_308, ECM_PID, ECM_filter_sig, ECM_trans, 0);
}

static void flexcop_pid_control(struct flexcop_device *fc,
		int index, u16 pid, int onoff)
{
	if (pid == 0x2000)
		return;

	deb_ts("setting pid: %5d %04x at index %d '%s'\n",
			pid, pid, index, onoff ? "on" : "off");

	/* We could use bit magic here to reduce source code size.
	 * I decided against it, but to use the real register names */
	switch (index) {
	case 0:
		flexcop_pid_Stream1_PID_ctrl(fc, pid, onoff);
		break;
	case 1:
		flexcop_pid_Stream2_PID_ctrl(fc, pid, onoff);
		break;
	case 2:
		flexcop_pid_PCR_PID_ctrl(fc, pid, onoff);
		break;
	case 3:
		flexcop_pid_PMT_PID_ctrl(fc, pid, onoff);
		break;
	case 4:
		flexcop_pid_EMM_PID_ctrl(fc, pid, onoff);
		break;
	case 5:
		flexcop_pid_ECM_PID_ctrl(fc, pid, onoff);
		break;
	default:
		if (fc->has_32_hw_pid_filter && index < 38) {
			flexcop_ibi_value vpid, vid;

			/* set the index */
			vid = fc->read_ibi_reg(fc, index_reg_310);
			vid.index_reg_310.index_reg = index - 6;
			fc->write_ibi_reg(fc, index_reg_310, vid);

			vpid = fc->read_ibi_reg(fc, pid_n_reg_314);
			vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff;
			vpid.pid_n_reg_314.PID_enable_bit = onoff;
			fc->write_ibi_reg(fc, pid_n_reg_314, vpid);
		}
		break;
	}
}

static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc, int onoff)
{
	if (fc->fullts_streaming_state != onoff) {
		deb_ts("%s full TS transfer\n",onoff ? "enabling" : "disabling");
		flexcop_pid_group_filter(fc, 0, 0x1fe0 * (!onoff));
		flexcop_pid_group_filter_ctrl(fc, onoff);
		fc->fullts_streaming_state = onoff;
	}
	return 0;
}

int flexcop_pid_feed_control(struct flexcop_device *fc,
		struct dvb_demux_feed *dvbdmxfeed, int onoff)
{
	int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32;

	fc->feedcount += onoff ? 1 : -1; /* the number of PIDs/Feed currently requested */
	if (dvbdmxfeed->index >= max_pid_filter)
		fc->extra_feedcount += onoff ? 1 : -1;

	/* toggle complete-TS-streaming when:
	 * - pid_filtering is not enabled and it is the first or last feed requested
	 * - pid_filtering is enabled,
	 *   - but the number of requested feeds is exceeded
	 *   - or the requested pid is 0x2000 */

	if (!fc->pid_filtering && fc->feedcount == onoff)
		flexcop_toggle_fullts_streaming(fc, onoff);

	if (fc->pid_filtering) {
		flexcop_pid_control \
			(fc, dvbdmxfeed->index, dvbdmxfeed->pid, onoff);

		if (fc->extra_feedcount > 0)
			flexcop_toggle_fullts_streaming(fc, 1);
		else if (dvbdmxfeed->pid == 0x2000)
			flexcop_toggle_fullts_streaming(fc, onoff);
		else
			flexcop_toggle_fullts_streaming(fc, 0);
	}

	/* if it was the first or last feed request change the stream-status */
	if (fc->feedcount == onoff) {
		flexcop_rcv_data_ctrl(fc, onoff);
		if (fc->stream_control) /* device specific stream control */
			fc->stream_control(fc, onoff);

		/* feeding stopped -> reset the flexcop filter*/
		if (onoff == 0) {
			flexcop_reset_block_300(fc);
			flexcop_hw_filter_init(fc);
		}
	}
	return 0;
}
EXPORT_SYMBOL(flexcop_pid_feed_control);

void flexcop_hw_filter_init(struct flexcop_device *fc)
{
	int i;
	flexcop_ibi_value v;
	for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++)
		flexcop_pid_control(fc, i, 0x1fff, 0);

	flexcop_pid_group_filter(fc, 0, 0x1fe0);
	flexcop_pid_group_filter_ctrl(fc, 0);

	v = fc->read_ibi_reg(fc, pid_filter_308);
	v.pid_filter_308.EMM_filter_4 = 1;
	v.pid_filter_308.EMM_filter_6 = 0;
	fc->write_ibi_reg(fc, pid_filter_308, v);

	flexcop_null_filter_ctrl(fc, 1);
}