Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Cypress StreetFighter Touchkey Driver
  4 *
  5 * Copyright (c) 2021 Yassine Oudjana <y.oudjana@protonmail.com>
  6 */
  7
  8#include <linux/bitmap.h>
  9#include <linux/bitops.h>
 10#include <linux/device.h>
 11#include <linux/i2c.h>
 12#include <linux/input.h>
 13#include <linux/interrupt.h>
 14#include <linux/module.h>
 15#include <linux/pm.h>
 16#include <linux/regulator/consumer.h>
 17
 18#define CYPRESS_SF_DEV_NAME "cypress-sf"
 19
 20#define CYPRESS_SF_REG_BUTTON_STATUS	0x4a
 21
 22struct cypress_sf_data {
 23	struct i2c_client *client;
 24	struct input_dev *input_dev;
 25	struct regulator_bulk_data regulators[2];
 26	u32 *keycodes;
 27	unsigned long keystates;
 28	int num_keys;
 29};
 30
 31static irqreturn_t cypress_sf_irq_handler(int irq, void *devid)
 32{
 33	struct cypress_sf_data *touchkey = devid;
 34	unsigned long keystates, changed;
 35	bool new_state;
 36	int val, key;
 37
 38	val = i2c_smbus_read_byte_data(touchkey->client,
 39				       CYPRESS_SF_REG_BUTTON_STATUS);
 40	if (val < 0) {
 41		dev_err(&touchkey->client->dev,
 42			"Failed to read button status: %d", val);
 43		return IRQ_NONE;
 44	}
 45	keystates = val;
 46
 47	bitmap_xor(&changed, &keystates, &touchkey->keystates,
 48		   touchkey->num_keys);
 49
 50	for_each_set_bit(key, &changed, touchkey->num_keys) {
 51		new_state = keystates & BIT(key);
 52		dev_dbg(&touchkey->client->dev,
 53			"Key %d changed to %d", key, new_state);
 54		input_report_key(touchkey->input_dev,
 55				 touchkey->keycodes[key], new_state);
 56	}
 57
 58	input_sync(touchkey->input_dev);
 59	touchkey->keystates = keystates;
 60
 61	return IRQ_HANDLED;
 62}
 63
 64static void cypress_sf_disable_regulators(void *arg)
 65{
 66	struct cypress_sf_data *touchkey = arg;
 67
 68	regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators),
 69			       touchkey->regulators);
 70}
 71
 72static int cypress_sf_probe(struct i2c_client *client)
 73{
 74	struct cypress_sf_data *touchkey;
 75	int key, error;
 76
 77	touchkey = devm_kzalloc(&client->dev, sizeof(*touchkey), GFP_KERNEL);
 78	if (!touchkey)
 79		return -ENOMEM;
 80
 81	touchkey->client = client;
 82	i2c_set_clientdata(client, touchkey);
 83
 84	touchkey->regulators[0].supply = "vdd";
 85	touchkey->regulators[1].supply = "avdd";
 86
 87	error = devm_regulator_bulk_get(&client->dev,
 88					ARRAY_SIZE(touchkey->regulators),
 89					touchkey->regulators);
 90	if (error) {
 91		dev_err(&client->dev, "Failed to get regulators: %d\n", error);
 92		return error;
 93	}
 94
 95	touchkey->num_keys = device_property_read_u32_array(&client->dev,
 96							    "linux,keycodes",
 97							    NULL, 0);
 98	if (touchkey->num_keys < 0) {
 99		/* Default key count */
100		touchkey->num_keys = 2;
101	}
102
103	touchkey->keycodes = devm_kcalloc(&client->dev,
104					  touchkey->num_keys,
105					  sizeof(*touchkey->keycodes),
106					  GFP_KERNEL);
107	if (!touchkey->keycodes)
108		return -ENOMEM;
109
110	error = device_property_read_u32_array(&client->dev, "linux,keycodes",
111					       touchkey->keycodes,
112					       touchkey->num_keys);
113
114	if (error) {
115		dev_warn(&client->dev,
116			 "Failed to read keycodes: %d, using defaults\n",
117			 error);
118
119		/* Default keycodes */
120		touchkey->keycodes[0] = KEY_BACK;
121		touchkey->keycodes[1] = KEY_MENU;
122	}
123
124	error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
125				      touchkey->regulators);
126	if (error) {
127		dev_err(&client->dev,
128			"Failed to enable regulators: %d\n", error);
129		return error;
130	}
131
132	error = devm_add_action_or_reset(&client->dev,
133					 cypress_sf_disable_regulators,
134					 touchkey);
135	if (error)
136		return error;
137
138	touchkey->input_dev = devm_input_allocate_device(&client->dev);
139	if (!touchkey->input_dev) {
140		dev_err(&client->dev, "Failed to allocate input device\n");
141		return -ENOMEM;
142	}
143
144	touchkey->input_dev->name = CYPRESS_SF_DEV_NAME;
145	touchkey->input_dev->id.bustype = BUS_I2C;
146
147	for (key = 0; key < touchkey->num_keys; ++key)
148		input_set_capability(touchkey->input_dev,
149				     EV_KEY, touchkey->keycodes[key]);
150
151	error = input_register_device(touchkey->input_dev);
152	if (error) {
153		dev_err(&client->dev,
154			"Failed to register input device: %d\n", error);
155		return error;
156	}
157
158	error = devm_request_threaded_irq(&client->dev, client->irq,
159					  NULL, cypress_sf_irq_handler,
160					  IRQF_ONESHOT,
161					  CYPRESS_SF_DEV_NAME, touchkey);
162	if (error) {
163		dev_err(&client->dev,
164			"Failed to register threaded irq: %d", error);
165		return error;
166	}
167
168	return 0;
169};
170
171static int cypress_sf_suspend(struct device *dev)
172{
173	struct i2c_client *client = to_i2c_client(dev);
174	struct cypress_sf_data *touchkey = i2c_get_clientdata(client);
175	int error;
176
177	disable_irq(client->irq);
178
179	error = regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators),
180				       touchkey->regulators);
181	if (error) {
182		dev_err(dev, "Failed to disable regulators: %d", error);
183		enable_irq(client->irq);
184		return error;
185	}
186
187	return 0;
188}
189
190static int cypress_sf_resume(struct device *dev)
191{
192	struct i2c_client *client = to_i2c_client(dev);
193	struct cypress_sf_data *touchkey = i2c_get_clientdata(client);
194	int error;
195
196	error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
197				      touchkey->regulators);
198	if (error) {
199		dev_err(dev, "Failed to enable regulators: %d", error);
200		return error;
201	}
202
203	enable_irq(client->irq);
204
205	return 0;
206}
207
208static DEFINE_SIMPLE_DEV_PM_OPS(cypress_sf_pm_ops,
209				cypress_sf_suspend, cypress_sf_resume);
210
211static struct i2c_device_id cypress_sf_id_table[] = {
212	{ CYPRESS_SF_DEV_NAME, 0 },
213	{ }
214};
215MODULE_DEVICE_TABLE(i2c, cypress_sf_id_table);
216
217#ifdef CONFIG_OF
218static const struct of_device_id cypress_sf_of_match[] = {
219	{ .compatible = "cypress,sf3155", },
220	{ },
221};
222MODULE_DEVICE_TABLE(of, cypress_sf_of_match);
223#endif
224
225static struct i2c_driver cypress_sf_driver = {
226	.driver = {
227		.name = CYPRESS_SF_DEV_NAME,
228		.pm = pm_sleep_ptr(&cypress_sf_pm_ops),
229		.of_match_table = of_match_ptr(cypress_sf_of_match),
230	},
231	.id_table = cypress_sf_id_table,
232	.probe_new = cypress_sf_probe,
233};
234module_i2c_driver(cypress_sf_driver);
235
236MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
237MODULE_DESCRIPTION("Cypress StreetFighter Touchkey Driver");
238MODULE_LICENSE("GPL v2");