linux/drivers/media/common/b2c2/flexcop-hw-filter.c
<<
>>
Prefs
   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
   9static 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
  15void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff)
  16{
  17        flexcop_set_ibi_value(ctrl_208, SMC_Enable_sig, onoff);
  18}
  19
  20static 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
  25void 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
  41void 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
  46static 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
  56static 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), \
  67v208 = fc->read_ibi_reg(fc, ctrl_208); \
  68vpid.vregname.field = onoff ? pid : 0x1fff; \
  69vpid.vregname.trans_field = transval; \
  70v208.ctrl_208.enablefield = onoff; \
  71fc->write_ibi_reg(fc, vregname, vpid); \
  72fc->write_ibi_reg(fc, ctrl_208, v208)
  73
  74static 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
  81static 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
  88static 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
  94static 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
 100static 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
 106static 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
 112static 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
 164static 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, 0, 0x1fe0 * (!onoff));
 169                flexcop_pid_group_filter_ctrl(fc, onoff);
 170                fc->fullts_streaming_state = onoff;
 171        }
 172        return 0;
 173}
 174
 175int 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, dvbdmxfeed->index, dvbdmxfeed->pid, onoff);
 199
 200                if (fc->extra_feedcount > 0)
 201                        flexcop_toggle_fullts_streaming(fc, 1);
 202                else if (dvbdmxfeed->pid == 0x2000)
 203                        flexcop_toggle_fullts_streaming(fc, onoff);
 204                else
 205                        flexcop_toggle_fullts_streaming(fc, 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}
 222EXPORT_SYMBOL(flexcop_pid_feed_control);
 223
 224void 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, i, 0x1fff, 0);
 235
 236        flexcop_pid_group_filter(fc, 0, 0x1fe0);
 237        flexcop_pid_group_filter_ctrl(fc, 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, 1);
 245}
 246