linux/drivers/media/dvb-frontends/sp2.c
<<
>>
Prefs
   1/*
   2 * CIMaX SP2/SP2HF (Atmel T90FJR) CI driver
   3 *
   4 * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi>
   5 *
   6 * Heavily based on CIMax2(R) SP2 driver in conjunction with NetUp Dual
   7 * DVB-S2 CI card (cimax2) with following copyrights:
   8 *
   9 *  Copyright (C) 2009 NetUP Inc.
  10 *  Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
  11 *  Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
  12 *
  13 *    This program is free software; you can redistribute it and/or modify
  14 *    it under the terms of the GNU General Public License as published by
  15 *    the Free Software Foundation; either version 2 of the License, or
  16 *    (at your option) any later version.
  17 *
  18 *    This program is distributed in the hope that it will be useful,
  19 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 *    GNU General Public License for more details.
  22 */
  23
  24#include "sp2_priv.h"
  25
  26static int sp2_read_i2c(struct sp2 *s, u8 reg, u8 *buf, int len)
  27{
  28        int ret;
  29        struct i2c_client *client = s->client;
  30        struct i2c_adapter *adap = client->adapter;
  31        struct i2c_msg msg[] = {
  32                {
  33                        .addr = client->addr,
  34                        .flags = 0,
  35                        .buf = &reg,
  36                        .len = 1
  37                }, {
  38                        .addr = client->addr,
  39                        .flags  = I2C_M_RD,
  40                        .buf = buf,
  41                        .len = len
  42                }
  43        };
  44
  45        ret = i2c_transfer(adap, msg, 2);
  46
  47        if (ret != 2) {
  48                dev_err(&client->dev, "i2c read error, reg = 0x%02x, status = %d\n",
  49                                reg, ret);
  50                if (ret < 0)
  51                        return ret;
  52                else
  53                        return -EIO;
  54        }
  55
  56        dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %02x\n",
  57                                client->addr, reg, buf[0]);
  58
  59        return 0;
  60}
  61
  62static int sp2_write_i2c(struct sp2 *s, u8 reg, u8 *buf, int len)
  63{
  64        int ret;
  65        u8 buffer[35];
  66        struct i2c_client *client = s->client;
  67        struct i2c_adapter *adap = client->adapter;
  68        struct i2c_msg msg = {
  69                .addr = client->addr,
  70                .flags = 0,
  71                .buf = &buffer[0],
  72                .len = len + 1
  73        };
  74
  75        if ((len + 1) > sizeof(buffer)) {
  76                dev_err(&client->dev, "i2c wr reg=%02x: len=%d is too big!\n",
  77                                reg, len);
  78                return -EINVAL;
  79        }
  80
  81        buffer[0] = reg;
  82        memcpy(&buffer[1], buf, len);
  83
  84        ret = i2c_transfer(adap, &msg, 1);
  85
  86        if (ret != 1) {
  87                dev_err(&client->dev, "i2c write error, reg = 0x%02x, status = %d\n",
  88                                reg, ret);
  89                if (ret < 0)
  90                        return ret;
  91                else
  92                        return -EIO;
  93        }
  94
  95        return 0;
  96}
  97
  98static int sp2_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, u8 acs,
  99                        u8 read, int addr, u8 data)
 100{
 101        struct sp2 *s = en50221->data;
 102        u8 store;
 103        int mem, ret;
 104        int (*ci_op_cam)(void*, u8, int, u8, int*) = s->ci_control;
 105
 106        dev_dbg(&s->client->dev, "slot=%d, acs=0x%02x, addr=0x%04x, data = 0x%02x",
 107                        slot, acs, addr, data);
 108
 109        if (slot != 0)
 110                return -EINVAL;
 111
 112        /*
 113         * change module access type between IO space and attribute memory
 114         * when needed
 115         */
 116        if (s->module_access_type != acs) {
 117                ret = sp2_read_i2c(s, 0x00, &store, 1);
 118
 119                if (ret)
 120                        return ret;
 121
 122                store &= ~(SP2_MOD_CTL_ACS1 | SP2_MOD_CTL_ACS0);
 123                store |= acs;
 124
 125                ret = sp2_write_i2c(s, 0x00, &store, 1);
 126                if (ret)
 127                        return ret;
 128        }
 129
 130        s->module_access_type = acs;
 131
 132        /* implementation of ci_op_cam is device specific */
 133        if (ci_op_cam) {
 134                ret = ci_op_cam(s->priv, read, addr, data, &mem);
 135        } else {
 136                dev_err(&s->client->dev, "callback not defined");
 137                return -EINVAL;
 138        }
 139
 140        if (ret)
 141                return ret;
 142
 143        if (read) {
 144                dev_dbg(&s->client->dev, "cam read, addr=0x%04x, data = 0x%04x",
 145                                addr, mem);
 146                return mem;
 147        } else {
 148                return 0;
 149        }
 150}
 151
 152int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
 153                                int slot, int addr)
 154{
 155        return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS,
 156                        SP2_CI_RD, addr, 0);
 157}
 158
 159int sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
 160                                int slot, int addr, u8 data)
 161{
 162        return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS,
 163                        SP2_CI_WR, addr, data);
 164}
 165
 166int sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221,
 167                                int slot, u8 addr)
 168{
 169        return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS,
 170                        SP2_CI_RD, addr, 0);
 171}
 172
 173int sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221,
 174                                int slot, u8 addr, u8 data)
 175{
 176        return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS,
 177                        SP2_CI_WR, addr, data);
 178}
 179
 180int sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
 181{
 182        struct sp2 *s = en50221->data;
 183        u8 buf;
 184        int ret;
 185
 186        dev_dbg(&s->client->dev, "slot: %d\n", slot);
 187
 188        if (slot != 0)
 189                return -EINVAL;
 190
 191        /* RST on */
 192        buf = SP2_MOD_CTL_RST;
 193        ret = sp2_write_i2c(s, 0x00, &buf, 1);
 194
 195        if (ret)
 196                return ret;
 197
 198        usleep_range(500, 600);
 199
 200        /* RST off */
 201        buf = 0x00;
 202        ret = sp2_write_i2c(s, 0x00, &buf, 1);
 203
 204        if (ret)
 205                return ret;
 206
 207        msleep(1000);
 208
 209        return 0;
 210}
 211
 212int sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
 213{
 214        struct sp2 *s = en50221->data;
 215
 216        dev_dbg(&s->client->dev, "slot:%d\n", slot);
 217
 218        /* not implemented */
 219        return 0;
 220}
 221
 222int sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot)
 223{
 224        struct sp2 *s = en50221->data;
 225        u8 buf;
 226
 227        dev_dbg(&s->client->dev, "slot:%d\n", slot);
 228
 229        if (slot != 0)
 230                return -EINVAL;
 231
 232        sp2_read_i2c(s, 0x00, &buf, 1);
 233
 234        /* disable bypass and enable TS */
 235        buf |= (SP2_MOD_CTL_TSOEN | SP2_MOD_CTL_TSIEN);
 236        return sp2_write_i2c(s, 0, &buf, 1);
 237}
 238
 239int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221,
 240                                int slot, int open)
 241{
 242        struct sp2 *s = en50221->data;
 243        u8 buf[2];
 244        int ret;
 245
 246        dev_dbg(&s->client->dev, "slot:%d open:%d\n", slot, open);
 247
 248        /*
 249         * CAM module INSERT/REMOVE processing. Slow operation because of i2c
 250         * transfers. Throttle read to one per sec.
 251         */
 252        if (time_after(jiffies, s->next_status_checked_time)) {
 253                ret = sp2_read_i2c(s, 0x00, buf, 1);
 254                s->next_status_checked_time = jiffies + msecs_to_jiffies(1000);
 255
 256                if (ret)
 257                        return 0;
 258
 259                if (buf[0] & SP2_MOD_CTL_DET)
 260                        s->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
 261                                        DVB_CA_EN50221_POLL_CAM_READY;
 262                else
 263                        s->status = 0;
 264        }
 265
 266        return s->status;
 267}
 268
 269static int sp2_init(struct sp2 *s)
 270{
 271        int ret = 0;
 272        u8 buf;
 273        u8 cimax_init[34] = {
 274                0x00, /* module A control*/
 275                0x00, /* auto select mask high A */
 276                0x00, /* auto select mask low A */
 277                0x00, /* auto select pattern high A */
 278                0x00, /* auto select pattern low A */
 279                0x44, /* memory access time A, 600 ns */
 280                0x00, /* invert input A */
 281                0x00, /* RFU */
 282                0x00, /* RFU */
 283                0x00, /* module B control*/
 284                0x00, /* auto select mask high B */
 285                0x00, /* auto select mask low B */
 286                0x00, /* auto select pattern high B */
 287                0x00, /* auto select pattern low B */
 288                0x44, /* memory access time B, 600 ns */
 289                0x00, /* invert input B */
 290                0x00, /* RFU */
 291                0x00, /* RFU */
 292                0x00, /* auto select mask high Ext */
 293                0x00, /* auto select mask low Ext */
 294                0x00, /* auto select pattern high Ext */
 295                0x00, /* auto select pattern low Ext */
 296                0x00, /* RFU */
 297                0x02, /* destination - module A */
 298                0x01, /* power control reg, VCC power on */
 299                0x00, /* RFU */
 300                0x00, /* int status read only */
 301                0x00, /* Interrupt Mask Register */
 302                0x05, /* EXTINT=active-high, INT=push-pull */
 303                0x00, /* USCG1 */
 304                0x04, /* ack active low */
 305                0x00, /* LOCK = 0 */
 306                0x22, /* unknown */
 307                0x00, /* synchronization? */
 308        };
 309
 310        dev_dbg(&s->client->dev, "\n");
 311
 312        s->ca.owner = THIS_MODULE;
 313        s->ca.read_attribute_mem = sp2_ci_read_attribute_mem;
 314        s->ca.write_attribute_mem = sp2_ci_write_attribute_mem;
 315        s->ca.read_cam_control = sp2_ci_read_cam_control;
 316        s->ca.write_cam_control = sp2_ci_write_cam_control;
 317        s->ca.slot_reset = sp2_ci_slot_reset;
 318        s->ca.slot_shutdown = sp2_ci_slot_shutdown;
 319        s->ca.slot_ts_enable = sp2_ci_slot_ts_enable;
 320        s->ca.poll_slot_status = sp2_ci_poll_slot_status;
 321        s->ca.data = s;
 322        s->module_access_type = 0;
 323
 324        /* initialize all regs */
 325        ret = sp2_write_i2c(s, 0x00, &cimax_init[0], 34);
 326        if (ret)
 327                goto err;
 328
 329        /* lock registers */
 330        buf = 1;
 331        ret = sp2_write_i2c(s, 0x1f, &buf, 1);
 332        if (ret)
 333                goto err;
 334
 335        /* power on slots */
 336        ret = sp2_write_i2c(s, 0x18, &buf, 1);
 337        if (ret)
 338                goto err;
 339
 340        ret = dvb_ca_en50221_init(s->dvb_adap, &s->ca, 0, 1);
 341        if (ret)
 342                goto err;
 343
 344        return 0;
 345
 346err:
 347        dev_dbg(&s->client->dev, "init failed=%d\n", ret);
 348        return ret;
 349}
 350
 351static int sp2_exit(struct i2c_client *client)
 352{
 353        struct sp2 *s;
 354
 355        dev_dbg(&client->dev, "\n");
 356
 357        if (client == NULL)
 358                return 0;
 359
 360        s = i2c_get_clientdata(client);
 361        if (s == NULL)
 362                return 0;
 363
 364        if (s->ca.data == NULL)
 365                return 0;
 366
 367        dvb_ca_en50221_release(&s->ca);
 368
 369        return 0;
 370}
 371
 372static int sp2_probe(struct i2c_client *client,
 373                const struct i2c_device_id *id)
 374{
 375        struct sp2_config *cfg = client->dev.platform_data;
 376        struct sp2 *s;
 377        int ret;
 378
 379        dev_dbg(&client->dev, "\n");
 380
 381        s = kzalloc(sizeof(struct sp2), GFP_KERNEL);
 382        if (!s) {
 383                ret = -ENOMEM;
 384                dev_err(&client->dev, "kzalloc() failed\n");
 385                goto err;
 386        }
 387
 388        s->client = client;
 389        s->dvb_adap = cfg->dvb_adap;
 390        s->priv = cfg->priv;
 391        s->ci_control = cfg->ci_control;
 392
 393        i2c_set_clientdata(client, s);
 394
 395        ret = sp2_init(s);
 396        if (ret)
 397                goto err;
 398
 399        dev_info(&s->client->dev, "CIMaX SP2 successfully attached\n");
 400        return 0;
 401err:
 402        dev_dbg(&client->dev, "init failed=%d\n", ret);
 403        kfree(s);
 404
 405        return ret;
 406}
 407
 408static int sp2_remove(struct i2c_client *client)
 409{
 410        struct si2157 *s = i2c_get_clientdata(client);
 411
 412        dev_dbg(&client->dev, "\n");
 413
 414        sp2_exit(client);
 415        if (s != NULL)
 416                kfree(s);
 417
 418        return 0;
 419}
 420
 421static const struct i2c_device_id sp2_id[] = {
 422        {"sp2", 0},
 423        {}
 424};
 425MODULE_DEVICE_TABLE(i2c, sp2_id);
 426
 427static struct i2c_driver sp2_driver = {
 428        .driver = {
 429                .owner  = THIS_MODULE,
 430                .name   = "sp2",
 431        },
 432        .probe          = sp2_probe,
 433        .remove         = sp2_remove,
 434        .id_table       = sp2_id,
 435};
 436
 437module_i2c_driver(sp2_driver);
 438
 439MODULE_DESCRIPTION("CIMaX SP2/HF CI driver");
 440MODULE_AUTHOR("Olli Salonen <olli.salonen@iki.fi>");
 441MODULE_LICENSE("GPL");
 442