*      Brian Rogers <brian_rogers@comcast.net>
  * modified for AVerMedia Cardbus by
  *      Oldrich Jedlicka <oldium.pro@seznam.cz>
+ * Zilog Transmitter portions/ideas were derived from GPLv2+ sources:
+ *  - drivers/char/pctv_zilogir.[ch] from Hauppauge Broadway product
+ *     Copyright 2011 Hauppauge Computer works
+ *  - drivers/staging/media/lirc/lirc_zilog.c
+ *     Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *     Michal Kochanowicz <mkochano@pld.org.pl>
+ *     Christoph Bartelmus <lirc@bartelmus.de>
+ *     Ulrich Mueller <ulrich.mueller42@web.de>
+ *     Stefan Jahn <stefan@lkcc.org>
+ *     Jerome Brock <jbrock@users.sourceforge.net>
+ *     Thomas Reitmayr (treitmayr@yahoo.com)
+ *     Mark Weaver <mark@npsl.co.uk>
+ *     Jarod Wilson <jarod@redhat.com>
+ *     Copyright (C) 2011 Andy Walls <awalls@md.metrocast.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 #include <media/rc-core.h>
 #include <media/i2c/ir-kbd-i2c.h>
 
+#define FLAG_TX                1
+#define FLAG_HDPVR     2
 
 static int get_key_haup_common(struct IR_i2c *ir, enum rc_proto *protocol,
                               u32 *scancode, u8 *ptoggle, int size)
        int rc;
        struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work);
 
-       rc = ir_key_poll(ir);
-       if (rc == -ENODEV) {
-               rc_unregister_device(ir->rc);
-               ir->rc = NULL;
-               return;
+       /*
+        * If the transmit code is holding the lock, skip polling for
+        * IR, we'll get it to it next time round
+        */
+       if (mutex_trylock(&ir->lock)) {
+               rc = ir_key_poll(ir);
+               mutex_unlock(&ir->lock);
+               if (rc == -ENODEV) {
+                       rc_unregister_device(ir->rc);
+                       ir->rc = NULL;
+                       return;
+               }
        }
 
        schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling_interval));
        cancel_delayed_work_sync(&ir->work);
 }
 
-/* ----------------------------------------------------------------------- */
+/* Zilog Transmit Interface */
+#define XTAL_FREQ              18432000
+
+#define ZILOG_SEND             0x80
+#define ZILOG_UIR_END          0x40
+#define ZILOG_INIT_END         0x20
+#define ZILOG_LIR_END          0x10
+
+#define ZILOG_STATUS_OK                0x80
+#define ZILOG_STATUS_TX                0x40
+#define ZILOG_STATUS_SET       0x20
+
+/*
+ * As you can see here, very few different lengths of pulse and space
+ * can be encoded. This means that the hardware does not work well with
+ * recorded IR. It's best to work with generated IR, like from ir-ctl or
+ * the in-kernel encoders.
+ */
+struct code_block {
+       u8      length;
+       u16     pulse[7];       /* not aligned */
+       u8      carrier_pulse;
+       u8      carrier_space;
+       u16     space[8];       /* not aligned */
+       u8      codes[61];
+       u8      csum[2];
+} __packed;
+
+static int send_data_block(struct IR_i2c *ir, int cmd,
+                          struct code_block *code_block)
+{
+       int i, j, ret;
+       u8 buf[5], *p;
+
+       p = &code_block->length;
+       for (i = 0; p < code_block->csum; i++)
+               code_block->csum[i & 1] ^= *p++;
+
+       p = &code_block->length;
+
+       for (i = 0; i < sizeof(*code_block);) {
+               int tosend = sizeof(*code_block) - i;
+
+               if (tosend > 4)
+                       tosend = 4;
+               buf[0] = i + 1;
+               for (j = 0; j < tosend; ++j)
+                       buf[1 + j] = p[i + j];
+               dev_dbg(&ir->rc->dev, "%*ph", tosend + 1, buf);
+               ret = i2c_master_send(ir->tx_c, buf, tosend + 1);
+               if (ret != tosend + 1) {
+                       dev_dbg(&ir->rc->dev,
+                               "i2c_master_send failed with %d\n", ret);
+                       return ret < 0 ? ret : -EIO;
+               }
+               i += tosend;
+       }
+
+       buf[0] = 0;
+       buf[1] = cmd;
+       ret = i2c_master_send(ir->tx_c, buf, 2);
+       if (ret != 2) {
+               dev_err(&ir->rc->dev, "i2c_master_send failed with %d\n", ret);
+               return ret < 0 ? ret : -EIO;
+       }
+
+       usleep_range(2000, 5000);
+
+       ret = i2c_master_send(ir->tx_c, buf, 1);
+       if (ret != 1) {
+               dev_err(&ir->rc->dev, "i2c_master_send failed with %d\n", ret);
+               return ret < 0 ? ret : -EIO;
+       }
+
+       return 0;
+}
+
+static int zilog_init(struct IR_i2c *ir)
+{
+       struct code_block code_block = { .length = sizeof(code_block) };
+       u8 buf[4];
+       int ret;
+
+       put_unaligned_be16(0x1000, &code_block.pulse[3]);
+
+       ret = send_data_block(ir, ZILOG_INIT_END, &code_block);
+       if (ret)
+               return ret;
+
+       ret = i2c_master_recv(ir->tx_c, buf, 4);
+       if (ret != 4) {
+               dev_err(&ir->c->dev, "failed to retrieve firmware version: %d\n",
+                       ret);
+               return ret < 0 ? ret : -EIO;
+       }
+
+       dev_info(&ir->c->dev, "Zilog/Hauppauge IR blaster firmware version %d.%d.%d\n",
+                buf[1], buf[2], buf[3]);
+
+       return 0;
+}
+
+/*
+ * If the last slot for pulse is the same as the current slot for pulse,
+ * then use slot no 7.
+ */
+static void copy_codes(u8 *dst, u8 *src, unsigned int count)
+{
+       u8 c, last = 0xff;
+
+       while (count--) {
+               c = *src++;
+               if ((c & 0xf0) == last) {
+                       *dst++ = 0x70 | (c & 0xf);
+               } else {
+                       *dst++ = c;
+                       last = c & 0xf0;
+               }
+       }
+}
+
+/*
+ * When looking for repeats, we don't care about the trailing space. This
+ * is set to the shortest possible anyway.
+ */
+static int cmp_no_trail(u8 *a, u8 *b, unsigned int count)
+{
+       while (--count) {
+               if (*a++ != *b++)
+                       return 1;
+       }
+
+       return (*a & 0xf0) - (*b & 0xf0);
+}
+
+static int find_slot(u16 *array, unsigned int size, u16 val)
+{
+       int i;
+
+       for (i = 0; i < size; i++) {
+               if (get_unaligned_be16(&array[i]) == val) {
+                       return i;
+               } else if (!array[i]) {
+                       put_unaligned_be16(val, &array[i]);
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+static int zilog_ir_format(struct rc_dev *rcdev, unsigned int *txbuf,
+                          unsigned int count, struct code_block *code_block)
+{
+       struct IR_i2c *ir = rcdev->priv;
+       int rep, i, l, p = 0, s, c = 0;
+       bool repeating;
+       u8 codes[174];
+
+       code_block->carrier_pulse = DIV_ROUND_CLOSEST(
+                       ir->duty_cycle * XTAL_FREQ / 1000, ir->carrier);
+       code_block->carrier_space = DIV_ROUND_CLOSEST(
+                       (100 - ir->duty_cycle) * XTAL_FREQ / 1000, ir->carrier);
+
+       for (i = 0; i < count; i++) {
+               if (c >= ARRAY_SIZE(codes) - 1) {
+                       dev_warn(&rcdev->dev, "IR too long, cannot transmit\n");
+                       return -EINVAL;
+               }
+
+               /*
+                * Lengths more than 142220us cannot be encoded; also
+                * this checks for multiply overflow
+                */
+               if (txbuf[i] > 142220)
+                       return -EINVAL;
+
+               l = DIV_ROUND_CLOSEST((XTAL_FREQ / 1000) * txbuf[i], 40000);
+
+               if (i & 1) {
+                       s = find_slot(code_block->space,
+                                     ARRAY_SIZE(code_block->space), l);
+                       if (s == -1) {
+                               dev_warn(&rcdev->dev, "Too many different lengths spaces, cannot transmit");
+                               return -EINVAL;
+                       }
+
+                       /* We have a pulse and space */
+                       codes[c++] = (p << 4) | s;
+               } else {
+                       p = find_slot(code_block->pulse,
+                                     ARRAY_SIZE(code_block->pulse), l);
+                       if (p == -1) {
+                               dev_warn(&rcdev->dev, "Too many different lengths pulses, cannot transmit");
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       /* We have to encode the trailing pulse. Find the shortest space */
+       s = 0;
+       for (i = 1; i < ARRAY_SIZE(code_block->space); i++) {
+               u16 d = get_unaligned_be16(&code_block->space[i]);
+
+               if (get_unaligned_be16(&code_block->space[s]) > d)
+                       s = i;
+       }
+
+       codes[c++] = (p << 4) | s;
+
+       dev_dbg(&rcdev->dev, "generated %d codes\n", c);
+
+       /*
+        * Are the last N codes (so pulse + space) repeating 3 times?
+        * if so we can shorten the codes list and use code 0xc0 to repeat
+        * them.
+        */
+       repeating = false;
+
+       for (rep = c / 3; rep >= 1; rep--) {
+               if (!memcmp(&codes[c - rep * 3], &codes[c - rep * 2], rep) &&
+                   !cmp_no_trail(&codes[c - rep], &codes[c - rep * 2], rep)) {
+                       repeating = true;
+                       break;
+               }
+       }
+
+       if (repeating) {
+               /* first copy any leading non-repeating */
+               int leading = c - rep * 3;
+
+               if (leading + rep >= ARRAY_SIZE(code_block->codes) - 3) {
+                       dev_warn(&rcdev->dev, "IR too long, cannot transmit\n");
+                       return -EINVAL;
+               }
+
+               dev_dbg(&rcdev->dev, "found trailing %d repeat\n", rep);
+               copy_codes(code_block->codes, codes, leading);
+               code_block->codes[leading] = 0x82;
+               copy_codes(code_block->codes + leading + 1, codes + leading,
+                          rep);
+               c = leading + 1 + rep;
+               code_block->codes[c++] = 0xc0;
+       } else {
+               if (c >= ARRAY_SIZE(code_block->codes) - 3) {
+                       dev_warn(&rcdev->dev, "IR too long, cannot transmit\n");
+                       return -EINVAL;
+               }
+
+               dev_dbg(&rcdev->dev, "found no trailing repeat\n");
+               code_block->codes[0] = 0x82;
+               copy_codes(code_block->codes + 1, codes, c);
+               c++;
+               code_block->codes[c++] = 0xc4;
+       }
+
+       while (c < ARRAY_SIZE(code_block->codes))
+               code_block->codes[c++] = 0x83;
+
+       return 0;
+}
+
+static int zilog_tx(struct rc_dev *rcdev, unsigned int *txbuf,
+                   unsigned int count)
+{
+       struct IR_i2c *ir = rcdev->priv;
+       struct code_block code_block = { .length = sizeof(code_block) };
+       u8 buf[2];
+       int ret, i;
+
+       ret = zilog_ir_format(rcdev, txbuf, count, &code_block);
+       if (ret)
+               return ret;
+
+       ret = mutex_lock_interruptible(&ir->lock);
+       if (ret)
+               return ret;
+
+       ret = send_data_block(ir, ZILOG_UIR_END, &code_block);
+       if (ret)
+               goto out_unlock;
+
+       ret = i2c_master_recv(ir->tx_c, buf, 1);
+       if (ret != 1) {
+               dev_err(&ir->rc->dev, "i2c_master_recv failed with %d\n", ret);
+               goto out_unlock;
+       }
+
+       dev_dbg(&ir->rc->dev, "code set status: %02x\n", buf[0]);
+
+       if (buf[0] != (ZILOG_STATUS_OK | ZILOG_STATUS_SET)) {
+               dev_err(&ir->rc->dev, "unexpected IR TX response %02x\n",
+                       buf[0]);
+               ret = -EIO;
+               goto out_unlock;
+       }
+
+       buf[0] = 0x00;
+       buf[1] = ZILOG_SEND;
+
+       ret = i2c_master_send(ir->tx_c, buf, 2);
+       if (ret != 2) {
+               dev_err(&ir->rc->dev, "i2c_master_send failed with %d\n", ret);
+               if (ret >= 0)
+                       ret = -EIO;
+               goto out_unlock;
+       }
+
+       dev_dbg(&ir->rc->dev, "send command sent\n");
+
+       /*
+        * This bit NAKs until the device is ready, so we retry it
+        * sleeping a bit each time.  This seems to be what the windows
+        * driver does, approximately.
+        * Try for up to 1s.
+        */
+       for (i = 0; i < 20; ++i) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(msecs_to_jiffies(50));
+               ret = i2c_master_send(ir->tx_c, buf, 1);
+               if (ret == 1)
+                       break;
+               dev_dbg(&ir->rc->dev,
+                       "NAK expected: i2c_master_send failed with %d (try %d)\n",
+                       ret, i + 1);
+       }
+
+       if (ret != 1) {
+               dev_err(&ir->rc->dev,
+                       "IR TX chip never got ready: last i2c_master_send failed with %d\n",
+                       ret);
+               if (ret >= 0)
+                       ret = -EIO;
+               goto out_unlock;
+       }
+
+       i = i2c_master_recv(ir->tx_c, buf, 1);
+       if (i != 1) {
+               dev_err(&ir->rc->dev, "i2c_master_recv failed with %d\n", ret);
+               ret = -EIO;
+               goto out_unlock;
+       } else if (buf[0] != ZILOG_STATUS_OK) {
+               dev_err(&ir->rc->dev, "unexpected IR TX response #2: %02x\n",
+                       buf[0]);
+               ret = -EIO;
+               goto out_unlock;
+       }
+       dev_dbg(&ir->rc->dev, "transmit complete\n");
+
+       /* Oh good, it worked */
+       ret = count;
+out_unlock:
+       mutex_unlock(&ir->lock);
+
+       return ret;
+}
+
+static int zilog_tx_carrier(struct rc_dev *dev, u32 carrier)
+{
+       struct IR_i2c *ir = dev->priv;
+
+       if (carrier > 500000 || carrier < 20000)
+               return -EINVAL;
+
+       ir->carrier = carrier;
+
+       return 0;
+}
+
+static int zilog_tx_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
+{
+       struct IR_i2c *ir = dev->priv;
+
+       ir->duty_cycle = duty_cycle;
+
+       return 0;
+}
 
 static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
        if (!rc->driver_name)
                rc->driver_name = KBUILD_MODNAME;
 
+       mutex_init(&ir->lock);
+
        INIT_DELAYED_WORK(&ir->work, ir_work);
 
+       if (id->driver_data & FLAG_TX) {
+               ir->tx_c = i2c_new_dummy(client->adapter, 0x70);
+               if (!ir->tx_c) {
+                       dev_err(&client->dev, "failed to setup tx i2c address");
+               } else if (!zilog_init(ir)) {
+                       ir->carrier = 38000;
+                       ir->duty_cycle = 40;
+                       rc->tx_ir = zilog_tx;
+                       rc->s_tx_carrier = zilog_tx_carrier;
+                       rc->s_tx_duty_cycle = zilog_tx_duty_cycle;
+               }
+       }
+
        err = rc_register_device(rc);
        if (err)
                goto err_out_free;
        return 0;
 
  err_out_free:
+       if (ir->tx_c)
+               i2c_unregister_device(ir->tx_c);
+
        /* Only frees rc if it were allocated internally */
        rc_free_device(rc);
        return err;
        /* kill outstanding polls */
        cancel_delayed_work_sync(&ir->work);
 
+       if (ir->tx_c)
+               i2c_unregister_device(ir->tx_c);
+
        /* unregister device */
        rc_unregister_device(ir->rc);
 
        /* Generic entry for any IR receiver */
        { "ir_video", 0 },
        /* IR device specific entries should be added here */
-       { "ir_z8f0811_haup", 0 },
-       { "ir_z8f0811_hdpvr", 0 },
+       { "ir_z8f0811_haup", FLAG_TX },
+       { "ir_z8f0811_hdpvr", FLAG_TX | FLAG_HDPVR },
        { }
 };