]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/staging/media/cxd2099/cxd2099.c
Merge tag 'for-linus-20170812' of git://git.infradead.org/linux-mtd
[karo-tx-linux.git] / drivers / staging / media / cxd2099 / cxd2099.c
index 370ecb9595431c4283fab8028ad67b85859ed4a0..f28916ea69f1ccadd21506c8cf8f3dec8394534d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * cxd2099.c: Driver for the CXD2099AR Common Interface Controller
  *
- * Copyright (C) 2010-2011 Digital Devices GmbH
+ * Copyright (C) 2010-2013 Digital Devices GmbH
  *
  *
  * This program is free software; you can redistribute it and/or
 
 #include "cxd2099.h"
 
-#define MAX_BUFFER_SIZE 248
+/* comment this line to deactivate the cxd2099ar buffer mode */
+#define BUFFER_MODE 1
+
+static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount);
 
 struct cxd {
        struct dvb_ca_en50221 en;
@@ -48,6 +51,7 @@ struct cxd {
        int    mode;
        int    ready;
        int    dr;
+       int    write_busy;
        int    slot_stat;
 
        u8     amem[1024];
@@ -55,6 +59,9 @@ struct cxd {
 
        int    cammode;
        struct mutex lock;
+
+       u8     rbuf[1028];
+       u8     wbuf[1028];
 };
 
 static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr,
@@ -73,7 +80,7 @@ static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr,
 }
 
 static int i2c_write(struct i2c_adapter *adapter, u8 adr,
-                    u8 *data, u8 len)
+                    u8 *data, u16 len)
 {
        struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len};
 
@@ -100,12 +107,12 @@ static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr,
 }
 
 static int i2c_read(struct i2c_adapter *adapter, u8 adr,
-                   u8 reg, u8 *data, u8 n)
+                   u8 reg, u8 *data, u16 n)
 {
        struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
-                                .buf = &reg, .len = 1},
-                               {.addr = adr, .flags = I2C_M_RD,
-                                .buf = data, .len = n} };
+                                  .buf = &reg, .len = 1},
+                                 {.addr = adr, .flags = I2C_M_RD,
+                                  .buf = data, .len = n} };
 
        if (i2c_transfer(adapter, msgs, 2) != 2) {
                dev_err(&adapter->dev, "error in i2c_read\n");
@@ -114,14 +121,26 @@ static int i2c_read(struct i2c_adapter *adapter, u8 adr,
        return 0;
 }
 
-static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n)
+static int read_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
 {
-       int status;
+       int status = 0;
 
-       status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
+       if (ci->lastaddress != adr)
+               status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
        if (!status) {
                ci->lastaddress = adr;
-               status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, n);
+
+               while (n) {
+                       int len = n;
+
+                       if (ci->cfg.max_i2c && (len > ci->cfg.max_i2c))
+                               len = ci->cfg.max_i2c;
+                       status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, len);
+                       if (status)
+                               return status;
+                       data += len;
+                       n -= len;
+               }
        }
        return status;
 }
@@ -182,16 +201,16 @@ static int write_io(struct cxd *ci, u16 address, u8 val)
 
 static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask)
 {
-       int status;
+       int status = 0;
 
-       status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg);
+       if (ci->lastaddress != reg)
+               status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg);
        if (!status && reg >= 6 && reg <= 8 && mask != 0xff)
                status = i2c_read_reg(ci->i2c, ci->cfg.adr, 1, &ci->regs[reg]);
+       ci->lastaddress = reg;
        ci->regs[reg] = (ci->regs[reg] & (~mask)) | val;
-       if (!status) {
-               ci->lastaddress = reg;
+       if (!status)
                status = i2c_write_reg(ci->i2c, ci->cfg.adr, 1, ci->regs[reg]);
-       }
        if (reg == 0x20)
                ci->regs[reg] &= 0x7f;
        return status;
@@ -203,16 +222,29 @@ static int write_reg(struct cxd *ci, u8 reg, u8 val)
 }
 
 #ifdef BUFFER_MODE
-static int write_block(struct cxd *ci, u8 adr, u8 *data, int n)
+static int write_block(struct cxd *ci, u8 adr, u8 *data, u16 n)
 {
-       int status;
-       u8 buf[256] = {1};
-
-       status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
-       if (!status) {
-               ci->lastaddress = adr;
-               memcpy(buf + 1, data, n);
-               status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
+       int status = 0;
+       u8 *buf = ci->wbuf;
+
+       if (ci->lastaddress != adr)
+               status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
+       if (status)
+               return status;
+
+       ci->lastaddress = adr;
+       buf[0] = 1;
+       while (n) {
+               int len = n;
+
+               if (ci->cfg.max_i2c && (len + 1 > ci->cfg.max_i2c))
+                       len = ci->cfg.max_i2c - 1;
+               memcpy(buf + 1, data, len);
+               status = i2c_write(ci->i2c, ci->cfg.adr, buf, len + 1);
+               if (status)
+                       return status;
+               n -= len;
+               data += len;
        }
        return status;
 }
@@ -238,6 +270,8 @@ static void set_mode(struct cxd *ci, int mode)
 
 static void cam_mode(struct cxd *ci, int mode)
 {
+       u8 dummy;
+
        if (mode == ci->cammode)
                return;
 
@@ -246,16 +280,15 @@ static void cam_mode(struct cxd *ci, int mode)
                write_regm(ci, 0x20, 0x80, 0x80);
                break;
        case 0x01:
-#ifdef BUFFER_MODE
                if (!ci->en.read_data)
                        return;
+               ci->write_busy = 0;
                dev_info(&ci->i2c->dev, "enable cam buffer mode\n");
-               /* write_reg(ci, 0x0d, 0x00); */
-               /* write_reg(ci, 0x0e, 0x01); */
+               write_reg(ci, 0x0d, 0x00);
+               write_reg(ci, 0x0e, 0x01);
                write_regm(ci, 0x08, 0x40, 0x40);
-               /* read_reg(ci, 0x12, &dummy); */
+               read_reg(ci, 0x12, &dummy);
                write_regm(ci, 0x08, 0x80, 0x80);
-#endif
                break;
        default:
                break;
@@ -325,7 +358,10 @@ static int init(struct cxd *ci)
                if (status < 0)
                        break;
 
-               if (ci->cfg.clock_mode) {
+               if (ci->cfg.clock_mode == 2) {
+                       /* bitrate*2^13/ 72000 */
+                       u32 reg = ((ci->cfg.bitrate << 13) + 71999) / 72000;
+
                        if (ci->cfg.polarity) {
                                status = write_reg(ci, 0x09, 0x6f);
                                if (status < 0)
@@ -335,6 +371,25 @@ static int init(struct cxd *ci)
                                if (status < 0)
                                        break;
                        }
+                       status = write_reg(ci, 0x20, 0x08);
+                       if (status < 0)
+                               break;
+                       status = write_reg(ci, 0x21, (reg >> 8) & 0xff);
+                       if (status < 0)
+                               break;
+                       status = write_reg(ci, 0x22, reg & 0xff);
+                       if (status < 0)
+                               break;
+               } else if (ci->cfg.clock_mode == 1) {
+                       if (ci->cfg.polarity) {
+                               status = write_reg(ci, 0x09, 0x6f); /* D */
+                               if (status < 0)
+                                       break;
+                       } else {
+                               status = write_reg(ci, 0x09, 0x6d);
+                               if (status < 0)
+                                       break;
+                       }
                        status = write_reg(ci, 0x20, 0x68);
                        if (status < 0)
                                break;
@@ -346,7 +401,7 @@ static int init(struct cxd *ci)
                                break;
                } else {
                        if (ci->cfg.polarity) {
-                               status = write_reg(ci, 0x09, 0x4f);
+                               status = write_reg(ci, 0x09, 0x4f); /* C */
                                if (status < 0)
                                        break;
                        } else {
@@ -354,7 +409,6 @@ static int init(struct cxd *ci)
                                if (status < 0)
                                        break;
                        }
-
                        status = write_reg(ci, 0x20, 0x28);
                        if (status < 0)
                                break;
@@ -401,7 +455,6 @@ static int read_attribute_mem(struct dvb_ca_en50221 *ca,
        set_mode(ci, 1);
        read_pccard(ci, address, &val, 1);
        mutex_unlock(&ci->lock);
-       /* printk(KERN_INFO "%02x:%02x\n", address,val); */
        return val;
 }
 
@@ -446,6 +499,9 @@ static int slot_reset(struct dvb_ca_en50221 *ca, int slot)
 {
        struct cxd *ci = ca->data;
 
+       if (ci->cammode)
+               read_data(ca, slot, ci->rbuf, 0);
+
        mutex_lock(&ci->lock);
        cam_mode(ci, 0);
        write_reg(ci, 0x00, 0x21);
@@ -465,7 +521,6 @@ static int slot_reset(struct dvb_ca_en50221 *ca, int slot)
                }
        }
        mutex_unlock(&ci->lock);
-       /* msleep(500); */
        return 0;
 }
 
@@ -474,11 +529,19 @@ static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
        struct cxd *ci = ca->data;
 
        dev_info(&ci->i2c->dev, "%s\n", __func__);
+       if (ci->cammode)
+               read_data(ca, slot, ci->rbuf, 0);
        mutex_lock(&ci->lock);
+       write_reg(ci, 0x00, 0x21);
+       write_reg(ci, 0x06, 0x1F);
+       msleep(300);
+
        write_regm(ci, 0x09, 0x08, 0x08);
        write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */
        write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */
+
        ci->mode = -1;
+       ci->write_busy = 0;
        mutex_unlock(&ci->lock);
        return 0;
 }
@@ -490,9 +553,7 @@ static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
        mutex_lock(&ci->lock);
        write_regm(ci, 0x09, 0x00, 0x08);
        set_mode(ci, 0);
-#ifdef BUFFER_MODE
        cam_mode(ci, 1);
-#endif
        mutex_unlock(&ci->lock);
        return 0;
 }
@@ -506,12 +567,10 @@ static int campoll(struct cxd *ci)
                return 0;
        write_reg(ci, 0x05, istat);
 
-       if (istat & 0x40) {
+       if (istat & 0x40)
                ci->dr = 1;
-               dev_info(&ci->i2c->dev, "DR\n");
-       }
        if (istat & 0x20)
-               dev_info(&ci->i2c->dev, "WC\n");
+               ci->write_busy = 0;
 
        if (istat & 2) {
                u8 slotstat;
@@ -519,7 +578,8 @@ static int campoll(struct cxd *ci)
                read_reg(ci, 0x01, &slotstat);
                if (!(2 & slotstat)) {
                        if (!ci->slot_stat) {
-                               ci->slot_stat = DVB_CA_EN50221_POLL_CAM_PRESENT;
+                               ci->slot_stat |=
+                                             DVB_CA_EN50221_POLL_CAM_PRESENT;
                                write_regm(ci, 0x03, 0x08, 0x08);
                        }
 
@@ -531,8 +591,8 @@ static int campoll(struct cxd *ci)
                                ci->ready = 0;
                        }
                }
-               if (istat & 8 &&
-                   ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) {
+               if ((istat & 8) &&
+                   (ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT)) {
                        ci->ready = 1;
                        ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY;
                }
@@ -553,7 +613,6 @@ static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
        return ci->slot_stat;
 }
 
-#ifdef BUFFER_MODE
 static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
 {
        struct cxd *ci = ca->data;
@@ -564,30 +623,38 @@ static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
        campoll(ci);
        mutex_unlock(&ci->lock);
 
-       dev_info(&ci->i2c->dev, "%s\n", __func__);
        if (!ci->dr)
                return 0;
 
        mutex_lock(&ci->lock);
        read_reg(ci, 0x0f, &msb);
        read_reg(ci, 0x10, &lsb);
-       len = (msb << 8) | lsb;
+       len = ((u16)msb << 8) | lsb;
+       if (len > ecount || len < 2) {
+               /* read it anyway or cxd may hang */
+               read_block(ci, 0x12, ci->rbuf, len);
+               mutex_unlock(&ci->lock);
+               return -EIO;
+       }
        read_block(ci, 0x12, ebuf, len);
        ci->dr = 0;
        mutex_unlock(&ci->lock);
-
        return len;
 }
 
+#ifdef BUFFER_MODE
+
 static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
 {
        struct cxd *ci = ca->data;
 
+       if (ci->write_busy)
+               return -EAGAIN;
        mutex_lock(&ci->lock);
-       dev_info(&ci->i2c->dev, "%s %d\n", __func__, ecount);
        write_reg(ci, 0x0d, ecount >> 8);
        write_reg(ci, 0x0e, ecount & 0xff);
        write_block(ci, 0x11, ebuf, ecount);
+       ci->write_busy = 1;
        mutex_unlock(&ci->lock);
        return ecount;
 }