cx231xx-input.c 6.3 KB
Newer Older
1 2 3 4
/*
  handle cx231xx IR remotes via linux kernel input layer.

  Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
5
		Based on em28xx driver
6

7
		< This is a place holder for IR now.>
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

  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
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/usb.h>
30
#include <linux/slab.h>
31 32 33 34 35 36 37 38 39

#include "cx231xx.h"

static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");

#define i2cdprintk(fmt, arg...) \
	if (ir_debug) { \
40
		printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
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
	}

#define dprintk(fmt, arg...) \
	if (ir_debug) { \
		printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
	}

/**********************************************************
 Polling structure used by cx231xx IR's
 **********************************************************/

struct cx231xx_ir_poll_result {
	unsigned int toggle_bit:1;
	unsigned int read_count:7;
	u8 rc_address;
	u8 rc_data[4];
};

struct cx231xx_IR {
	struct cx231xx *dev;
	struct input_dev *input;
	struct ir_input_state ir;
	char name[32];
	char phys[32];

	/* poll external decoder */
	int polling;
	struct work_struct work;
	struct timer_list timer;
	unsigned int last_toggle:1;
	unsigned int last_readcount;
	unsigned int repeat_interval;

74
	int (*get_key) (struct cx231xx_IR *, struct cx231xx_ir_poll_result *);
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
};

/**********************************************************
 Polling code for cx231xx
 **********************************************************/

static void cx231xx_ir_handle_key(struct cx231xx_IR *ir)
{
	int result;
	int do_sendkey = 0;
	struct cx231xx_ir_poll_result poll_result;

	/* read the registers containing the IR status */
	result = ir->get_key(ir, &poll_result);
	if (result < 0) {
		dprintk("ir->get_key() failed %d\n", result);
		return;
	}

	dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n",
		poll_result.toggle_bit, poll_result.read_count,
		ir->last_readcount, poll_result.rc_data[0]);

	if (ir->dev->chip_id == CHIP_ID_EM2874) {
		/* The em2874 clears the readcount field every time the
		   register is read.  The em2860/2880 datasheet says that it
		   is supposed to clear the readcount, but it doesn't.  So with
		   the em2874, we are looking for a non-zero read count as
		   opposed to a readcount that is incrementing */
		ir->last_readcount = 0;
	}

	if (poll_result.read_count == 0) {
		/* The button has not been pressed since the last read */
	} else if (ir->last_toggle != poll_result.toggle_bit) {
		/* A button has been pressed */
		dprintk("button has been pressed\n");
		ir->last_toggle = poll_result.toggle_bit;
		ir->repeat_interval = 0;
		do_sendkey = 1;
	} else if (poll_result.toggle_bit == ir->last_toggle &&
		   poll_result.read_count > 0 &&
		   poll_result.read_count != ir->last_readcount) {
		/* The button is still being held down */
		dprintk("button being held down\n");

		/* Debouncer for first keypress */
		if (ir->repeat_interval++ > 9) {
			/* Start repeating after 1 second */
			do_sendkey = 1;
		}
	}

	if (do_sendkey) {
		dprintk("sending keypress\n");
130
		ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0]);
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
		ir_input_nokey(ir->input, &ir->ir);
	}

	ir->last_readcount = poll_result.read_count;
	return;
}

static void ir_timer(unsigned long data)
{
	struct cx231xx_IR *ir = (struct cx231xx_IR *)data;

	schedule_work(&ir->work);
}

static void cx231xx_ir_work(struct work_struct *work)
{
	struct cx231xx_IR *ir = container_of(work, struct cx231xx_IR, work);

	cx231xx_ir_handle_key(ir);
	mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
}

void cx231xx_ir_start(struct cx231xx_IR *ir)
{
	setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
	INIT_WORK(&ir->work, cx231xx_ir_work);
	schedule_work(&ir->work);
}

static void cx231xx_ir_stop(struct cx231xx_IR *ir)
{
	del_timer_sync(&ir->timer);
	flush_scheduled_work();
}

int cx231xx_ir_init(struct cx231xx *dev)
{
	struct cx231xx_IR *ir;
	struct input_dev *input_dev;
	u8 ir_config;
	int err = -ENOMEM;

	if (dev->board.ir_codes == NULL) {
		/* No remote control support */
		return 0;
	}

	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
	input_dev = input_allocate_device();
	if (!ir || !input_dev)
		goto err_out_free;

	ir->input = input_dev;

	/* Setup the proper handler based on the chip */
	switch (dev->chip_id) {
187 188 189
	default:
		printk("Unrecognized cx231xx chip id: IR not supported\n");
		goto err_out_free;
190 191 192
	}

	/* This is how often we ask the chip for IR information */
193
	ir->polling = 100;	/* ms */
194 195

	/* init input device */
196
	snprintf(ir->name, sizeof(ir->name), "cx231xx IR (%s)", dev->name);
197 198 199 200

	usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
	strlcat(ir->phys, "/input0", sizeof(ir->phys));

201
	err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER);
202 203 204
	if (err < 0)
		goto err_out_free;

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
	input_dev->name = ir->name;
	input_dev->phys = ir->phys;
	input_dev->id.bustype = BUS_USB;
	input_dev->id.version = 1;
	input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
	input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);

	input_dev->dev.parent = &dev->udev->dev;
	/* record handles to ourself */
	ir->dev = dev;
	dev->ir = ir;

	cx231xx_ir_start(ir);

	/* all done */
220
	err = ir_input_register(ir->input, dev->board.ir_codes, NULL);
221 222 223 224
	if (err)
		goto err_out_stop;

	return 0;
225
err_out_stop:
226 227
	cx231xx_ir_stop(ir);
	dev->ir = NULL;
228
err_out_free:
229 230 231 232 233 234 235 236 237 238 239 240 241
	kfree(ir);
	return err;
}

int cx231xx_ir_fini(struct cx231xx *dev)
{
	struct cx231xx_IR *ir = dev->ir;

	/* skip detach on non attached boards */
	if (!ir)
		return 0;

	cx231xx_ir_stop(ir);
242
	ir_input_unregister(ir->input);
243 244 245 246 247 248
	kfree(ir);

	/* done */
	dev->ir = NULL;
	return 0;
}