Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
  4 *
  5 * Copyright 2005-2008 Analog Devices Inc.
  6 */
  7
  8#include <linux/module.h>
  9#include <linux/input.h>
 10#include <linux/interrupt.h>
 11#include <linux/i2c.h>
 12#include <linux/slab.h>
 13#include <linux/workqueue.h>
 14
 15#define DRV_NAME "pcf8574_keypad"
 16
 17static const unsigned char pcf8574_kp_btncode[] = {
 18	[0] = KEY_RESERVED,
 19	[1] = KEY_ENTER,
 20	[2] = KEY_BACKSLASH,
 21	[3] = KEY_0,
 22	[4] = KEY_RIGHTBRACE,
 23	[5] = KEY_C,
 24	[6] = KEY_9,
 25	[7] = KEY_8,
 26	[8] = KEY_7,
 27	[9] = KEY_B,
 28	[10] = KEY_6,
 29	[11] = KEY_5,
 30	[12] = KEY_4,
 31	[13] = KEY_A,
 32	[14] = KEY_3,
 33	[15] = KEY_2,
 34	[16] = KEY_1
 35};
 36
 37struct kp_data {
 38	unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
 39	struct input_dev *idev;
 40	struct i2c_client *client;
 41	char name[64];
 42	char phys[32];
 43	unsigned char laststate;
 44};
 45
 46static short read_state(struct kp_data *lp)
 47{
 48	unsigned char x, y, a, b;
 49
 50	i2c_smbus_write_byte(lp->client, 240);
 51	x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));
 52
 53	i2c_smbus_write_byte(lp->client, 15);
 54	y = 0xF & (~i2c_smbus_read_byte(lp->client));
 55
 56	for (a = 0; x > 0; a++)
 57		x = x >> 1;
 58	for (b = 0; y > 0; b++)
 59		y = y >> 1;
 60
 61	return ((a - 1) * 4) + b;
 62}
 63
 64static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
 65{
 66	struct kp_data *lp = dev_id;
 67	unsigned char nextstate = read_state(lp);
 68
 69	if (lp->laststate != nextstate) {
 70		int key_down = nextstate < ARRAY_SIZE(lp->btncode);
 71		unsigned short keycode = key_down ?
 72			lp->btncode[nextstate] : lp->btncode[lp->laststate];
 73
 74		input_report_key(lp->idev, keycode, key_down);
 75		input_sync(lp->idev);
 76
 77		lp->laststate = nextstate;
 78	}
 79
 80	return IRQ_HANDLED;
 81}
 82
 83static int pcf8574_kp_probe(struct i2c_client *client)
 84{
 85	int i, ret;
 86	struct input_dev *idev;
 87	struct kp_data *lp;
 88
 89	if (i2c_smbus_write_byte(client, 240) < 0) {
 90		dev_err(&client->dev, "probe: write fail\n");
 91		return -ENODEV;
 92	}
 93
 94	lp = kzalloc(sizeof(*lp), GFP_KERNEL);
 95	if (!lp)
 96		return -ENOMEM;
 97
 98	idev = input_allocate_device();
 99	if (!idev) {
100		dev_err(&client->dev, "Can't allocate input device\n");
101		ret = -ENOMEM;
102		goto fail_allocate;
103	}
104
105	lp->idev = idev;
106	lp->client = client;
107
108	idev->evbit[0] = BIT_MASK(EV_KEY);
109	idev->keycode = lp->btncode;
110	idev->keycodesize = sizeof(lp->btncode[0]);
111	idev->keycodemax = ARRAY_SIZE(lp->btncode);
112
113	for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
114		if (lp->btncode[i] <= KEY_MAX) {
115			lp->btncode[i] = pcf8574_kp_btncode[i];
116			__set_bit(lp->btncode[i], idev->keybit);
117		}
118	}
119	__clear_bit(KEY_RESERVED, idev->keybit);
120
121	sprintf(lp->name, DRV_NAME);
122	sprintf(lp->phys, "kp_data/input0");
123
124	idev->name = lp->name;
125	idev->phys = lp->phys;
126	idev->id.bustype = BUS_I2C;
127	idev->id.vendor = 0x0001;
128	idev->id.product = 0x0001;
129	idev->id.version = 0x0100;
130
131	lp->laststate = read_state(lp);
132
133	ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,
134				   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
135				   DRV_NAME, lp);
136	if (ret) {
137		dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
138		goto fail_free_device;
139	}
140
141	ret = input_register_device(idev);
142	if (ret) {
143		dev_err(&client->dev, "input_register_device() failed\n");
144		goto fail_free_irq;
145	}
146
147	i2c_set_clientdata(client, lp);
148	return 0;
149
150 fail_free_irq:
151	free_irq(client->irq, lp);
152 fail_free_device:
153	input_free_device(idev);
154 fail_allocate:
155	kfree(lp);
156
157	return ret;
158}
159
160static void pcf8574_kp_remove(struct i2c_client *client)
161{
162	struct kp_data *lp = i2c_get_clientdata(client);
163
164	free_irq(client->irq, lp);
165
166	input_unregister_device(lp->idev);
167	kfree(lp);
168}
169
 
170static int pcf8574_kp_resume(struct device *dev)
171{
172	struct i2c_client *client = to_i2c_client(dev);
173
174	enable_irq(client->irq);
175
176	return 0;
177}
178
179static int pcf8574_kp_suspend(struct device *dev)
180{
181	struct i2c_client *client = to_i2c_client(dev);
182
183	disable_irq(client->irq);
184
185	return 0;
186}
187
188static DEFINE_SIMPLE_DEV_PM_OPS(pcf8574_kp_pm_ops,
189				pcf8574_kp_suspend, pcf8574_kp_resume);
 
 
 
 
 
 
 
190
191static const struct i2c_device_id pcf8574_kp_id[] = {
192	{ DRV_NAME, 0 },
193	{ }
194};
195MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
196
197static struct i2c_driver pcf8574_kp_driver = {
198	.driver = {
199		.name  = DRV_NAME,
200		.pm = pm_sleep_ptr(&pcf8574_kp_pm_ops),
 
 
201	},
202	.probe    = pcf8574_kp_probe,
203	.remove   = pcf8574_kp_remove,
204	.id_table = pcf8574_kp_id,
205};
206
207module_i2c_driver(pcf8574_kp_driver);
208
209MODULE_AUTHOR("Michael Hennerich");
210MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
211MODULE_LICENSE("GPL");
v6.2
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
  4 *
  5 * Copyright 2005-2008 Analog Devices Inc.
  6 */
  7
  8#include <linux/module.h>
  9#include <linux/input.h>
 10#include <linux/interrupt.h>
 11#include <linux/i2c.h>
 12#include <linux/slab.h>
 13#include <linux/workqueue.h>
 14
 15#define DRV_NAME "pcf8574_keypad"
 16
 17static const unsigned char pcf8574_kp_btncode[] = {
 18	[0] = KEY_RESERVED,
 19	[1] = KEY_ENTER,
 20	[2] = KEY_BACKSLASH,
 21	[3] = KEY_0,
 22	[4] = KEY_RIGHTBRACE,
 23	[5] = KEY_C,
 24	[6] = KEY_9,
 25	[7] = KEY_8,
 26	[8] = KEY_7,
 27	[9] = KEY_B,
 28	[10] = KEY_6,
 29	[11] = KEY_5,
 30	[12] = KEY_4,
 31	[13] = KEY_A,
 32	[14] = KEY_3,
 33	[15] = KEY_2,
 34	[16] = KEY_1
 35};
 36
 37struct kp_data {
 38	unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
 39	struct input_dev *idev;
 40	struct i2c_client *client;
 41	char name[64];
 42	char phys[32];
 43	unsigned char laststate;
 44};
 45
 46static short read_state(struct kp_data *lp)
 47{
 48	unsigned char x, y, a, b;
 49
 50	i2c_smbus_write_byte(lp->client, 240);
 51	x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));
 52
 53	i2c_smbus_write_byte(lp->client, 15);
 54	y = 0xF & (~i2c_smbus_read_byte(lp->client));
 55
 56	for (a = 0; x > 0; a++)
 57		x = x >> 1;
 58	for (b = 0; y > 0; b++)
 59		y = y >> 1;
 60
 61	return ((a - 1) * 4) + b;
 62}
 63
 64static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
 65{
 66	struct kp_data *lp = dev_id;
 67	unsigned char nextstate = read_state(lp);
 68
 69	if (lp->laststate != nextstate) {
 70		int key_down = nextstate < ARRAY_SIZE(lp->btncode);
 71		unsigned short keycode = key_down ?
 72			lp->btncode[nextstate] : lp->btncode[lp->laststate];
 73
 74		input_report_key(lp->idev, keycode, key_down);
 75		input_sync(lp->idev);
 76
 77		lp->laststate = nextstate;
 78	}
 79
 80	return IRQ_HANDLED;
 81}
 82
 83static int pcf8574_kp_probe(struct i2c_client *client)
 84{
 85	int i, ret;
 86	struct input_dev *idev;
 87	struct kp_data *lp;
 88
 89	if (i2c_smbus_write_byte(client, 240) < 0) {
 90		dev_err(&client->dev, "probe: write fail\n");
 91		return -ENODEV;
 92	}
 93
 94	lp = kzalloc(sizeof(*lp), GFP_KERNEL);
 95	if (!lp)
 96		return -ENOMEM;
 97
 98	idev = input_allocate_device();
 99	if (!idev) {
100		dev_err(&client->dev, "Can't allocate input device\n");
101		ret = -ENOMEM;
102		goto fail_allocate;
103	}
104
105	lp->idev = idev;
106	lp->client = client;
107
108	idev->evbit[0] = BIT_MASK(EV_KEY);
109	idev->keycode = lp->btncode;
110	idev->keycodesize = sizeof(lp->btncode[0]);
111	idev->keycodemax = ARRAY_SIZE(lp->btncode);
112
113	for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
114		if (lp->btncode[i] <= KEY_MAX) {
115			lp->btncode[i] = pcf8574_kp_btncode[i];
116			__set_bit(lp->btncode[i], idev->keybit);
117		}
118	}
119	__clear_bit(KEY_RESERVED, idev->keybit);
120
121	sprintf(lp->name, DRV_NAME);
122	sprintf(lp->phys, "kp_data/input0");
123
124	idev->name = lp->name;
125	idev->phys = lp->phys;
126	idev->id.bustype = BUS_I2C;
127	idev->id.vendor = 0x0001;
128	idev->id.product = 0x0001;
129	idev->id.version = 0x0100;
130
131	lp->laststate = read_state(lp);
132
133	ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,
134				   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
135				   DRV_NAME, lp);
136	if (ret) {
137		dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
138		goto fail_free_device;
139	}
140
141	ret = input_register_device(idev);
142	if (ret) {
143		dev_err(&client->dev, "input_register_device() failed\n");
144		goto fail_free_irq;
145	}
146
147	i2c_set_clientdata(client, lp);
148	return 0;
149
150 fail_free_irq:
151	free_irq(client->irq, lp);
152 fail_free_device:
153	input_free_device(idev);
154 fail_allocate:
155	kfree(lp);
156
157	return ret;
158}
159
160static void pcf8574_kp_remove(struct i2c_client *client)
161{
162	struct kp_data *lp = i2c_get_clientdata(client);
163
164	free_irq(client->irq, lp);
165
166	input_unregister_device(lp->idev);
167	kfree(lp);
168}
169
170#ifdef CONFIG_PM
171static int pcf8574_kp_resume(struct device *dev)
172{
173	struct i2c_client *client = to_i2c_client(dev);
174
175	enable_irq(client->irq);
176
177	return 0;
178}
179
180static int pcf8574_kp_suspend(struct device *dev)
181{
182	struct i2c_client *client = to_i2c_client(dev);
183
184	disable_irq(client->irq);
185
186	return 0;
187}
188
189static const struct dev_pm_ops pcf8574_kp_pm_ops = {
190	.suspend	= pcf8574_kp_suspend,
191	.resume		= pcf8574_kp_resume,
192};
193
194#else
195# define pcf8574_kp_resume  NULL
196# define pcf8574_kp_suspend NULL
197#endif
198
199static const struct i2c_device_id pcf8574_kp_id[] = {
200	{ DRV_NAME, 0 },
201	{ }
202};
203MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
204
205static struct i2c_driver pcf8574_kp_driver = {
206	.driver = {
207		.name  = DRV_NAME,
208#ifdef CONFIG_PM
209		.pm = &pcf8574_kp_pm_ops,
210#endif
211	},
212	.probe_new = pcf8574_kp_probe,
213	.remove   = pcf8574_kp_remove,
214	.id_table = pcf8574_kp_id,
215};
216
217module_i2c_driver(pcf8574_kp_driver);
218
219MODULE_AUTHOR("Michael Hennerich");
220MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
221MODULE_LICENSE("GPL");