blob: efddf613be99e4976be515b25e766e03c25db0b5 [file] [log] [blame]
Mohan Pallakaec6ca822012-08-08 11:25:52 +05301/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/init.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/of.h>
18#include <linux/input.h>
19#include <linux/input/gen_vkeys.h>
20
21#define MAX_BUF_SIZE 256
22#define VKEY_VER_CODE "0x01"
23
24#define HEIGHT_SCALE_NUM 8
25#define HEIGHT_SCALE_DENOM 10
26
Amy Malochefa68db62013-05-09 10:23:41 -070027#define VKEY_Y_OFFSET_DEFAULT 0
28
Mohan Pallakaec6ca822012-08-08 11:25:52 +053029/* numerator and denomenator for border equations */
30#define BORDER_ADJUST_NUM 3
31#define BORDER_ADJUST_DENOM 4
32
33static struct kobject *vkey_obj;
34static char *vkey_buf;
35
36static ssize_t vkey_show(struct kobject *obj,
37 struct kobj_attribute *attr, char *buf)
38{
39 strlcpy(buf, vkey_buf, MAX_BUF_SIZE);
40 return strnlen(buf, MAX_BUF_SIZE);
41}
42
43static struct kobj_attribute vkey_obj_attr = {
44 .attr = {
45 .mode = S_IRUGO,
46 },
47 .show = vkey_show,
48};
49
50static struct attribute *vkey_attr[] = {
51 &vkey_obj_attr.attr,
52 NULL,
53};
54
55static struct attribute_group vkey_grp = {
56 .attrs = vkey_attr,
57};
58
59static int __devinit vkey_parse_dt(struct device *dev,
60 struct vkeys_platform_data *pdata)
61{
62 struct device_node *np = dev->of_node;
63 struct property *prop;
Amy Malochefa68db62013-05-09 10:23:41 -070064 int rc, val;
Mohan Pallakaec6ca822012-08-08 11:25:52 +053065
66 rc = of_property_read_string(np, "label", &pdata->name);
67 if (rc) {
68 dev_err(dev, "Failed to read label\n");
69 return -EINVAL;
70 }
71
72 rc = of_property_read_u32(np, "qcom,disp-maxx", &pdata->disp_maxx);
73 if (rc) {
74 dev_err(dev, "Failed to read display max x\n");
75 return -EINVAL;
76 }
77
78 rc = of_property_read_u32(np, "qcom,disp-maxy", &pdata->disp_maxy);
79 if (rc) {
80 dev_err(dev, "Failed to read display max y\n");
81 return -EINVAL;
82 }
83
84 rc = of_property_read_u32(np, "qcom,panel-maxx", &pdata->panel_maxx);
85 if (rc) {
86 dev_err(dev, "Failed to read panel max x\n");
87 return -EINVAL;
88 }
89
90 rc = of_property_read_u32(np, "qcom,panel-maxy", &pdata->panel_maxy);
91 if (rc) {
92 dev_err(dev, "Failed to read panel max y\n");
93 return -EINVAL;
94 }
95
96 prop = of_find_property(np, "qcom,key-codes", NULL);
97 if (prop) {
98 pdata->num_keys = prop->length / sizeof(u32);
99 pdata->keycodes = devm_kzalloc(dev,
100 sizeof(u32) * pdata->num_keys, GFP_KERNEL);
101 if (!pdata->keycodes)
102 return -ENOMEM;
103 rc = of_property_read_u32_array(np, "qcom,key-codes",
104 pdata->keycodes, pdata->num_keys);
105 if (rc) {
106 dev_err(dev, "Failed to read key codes\n");
107 return -EINVAL;
108 }
109 }
Amy Maloche0240a152013-04-17 12:15:56 -0700110
Amy Malochefa68db62013-05-09 10:23:41 -0700111 pdata->y_offset = VKEY_Y_OFFSET_DEFAULT;
112 rc = of_property_read_u32(np, "qcom,y-offset", &val);
113 if (!rc)
114 pdata->y_offset = val;
115 else if (rc != -EINVAL) {
Amy Maloche0240a152013-04-17 12:15:56 -0700116 dev_err(dev, "Failed to read y position offset\n");
Amy Malochefa68db62013-05-09 10:23:41 -0700117 return rc;
Amy Maloche0240a152013-04-17 12:15:56 -0700118 }
Mohan Pallakaec6ca822012-08-08 11:25:52 +0530119 return 0;
120}
121
122static int __devinit vkeys_probe(struct platform_device *pdev)
123{
124 struct vkeys_platform_data *pdata;
125 int width, height, center_x, center_y;
126 int x1 = 0, x2 = 0, i, c = 0, ret, border;
127 char *name;
128
129 vkey_buf = devm_kzalloc(&pdev->dev, MAX_BUF_SIZE, GFP_KERNEL);
130 if (!vkey_buf) {
131 dev_err(&pdev->dev, "Failed to allocate memory\n");
132 return -ENOMEM;
133 }
134
135 if (pdev->dev.of_node) {
136 pdata = devm_kzalloc(&pdev->dev,
137 sizeof(struct vkeys_platform_data), GFP_KERNEL);
138 if (!pdata) {
139 dev_err(&pdev->dev, "Failed to allocate memory\n");
140 return -ENOMEM;
141 }
142
143 ret = vkey_parse_dt(&pdev->dev, pdata);
144 if (ret) {
145 dev_err(&pdev->dev, "Parsing DT failed(%d)", ret);
146 return ret;
147 }
148 } else
149 pdata = pdev->dev.platform_data;
150
151 if (!pdata || !pdata->name || !pdata->keycodes || !pdata->num_keys ||
152 !pdata->disp_maxx || !pdata->disp_maxy || !pdata->panel_maxy) {
153 dev_err(&pdev->dev, "pdata is invalid\n");
154 return -EINVAL;
155 }
156
157 border = (pdata->panel_maxx - pdata->disp_maxx) * 2;
158 width = ((pdata->disp_maxx - (border * (pdata->num_keys - 1)))
159 / pdata->num_keys);
160 height = (pdata->panel_maxy - pdata->disp_maxy);
Amy Maloche0240a152013-04-17 12:15:56 -0700161 center_y = pdata->disp_maxy + (height / 2) + pdata->y_offset;
Mohan Pallakaec6ca822012-08-08 11:25:52 +0530162 height = height * HEIGHT_SCALE_NUM / HEIGHT_SCALE_DENOM;
163
164 x2 -= border * BORDER_ADJUST_NUM / BORDER_ADJUST_DENOM;
165
166 for (i = 0; i < pdata->num_keys; i++) {
167 x1 = x2 + border;
168 x2 = x2 + border + width;
169 center_x = x1 + (x2 - x1) / 2;
170 c += snprintf(vkey_buf + c, MAX_BUF_SIZE - c,
171 "%s:%d:%d:%d:%d:%d\n",
172 VKEY_VER_CODE, pdata->keycodes[i],
173 center_x, center_y, width, height);
174 }
175
176 vkey_buf[c] = '\0';
177
178 name = devm_kzalloc(&pdev->dev, sizeof(*name) * MAX_BUF_SIZE,
179 GFP_KERNEL);
180 if (!name)
181 return -ENOMEM;
182
183 snprintf(name, MAX_BUF_SIZE,
184 "virtualkeys.%s", pdata->name);
185 vkey_obj_attr.attr.name = name;
186
187 vkey_obj = kobject_create_and_add("board_properties", NULL);
188 if (!vkey_obj) {
189 dev_err(&pdev->dev, "unable to create kobject\n");
190 return -ENOMEM;
191 }
192
193 ret = sysfs_create_group(vkey_obj, &vkey_grp);
194 if (ret) {
195 dev_err(&pdev->dev, "failed to create attributes\n");
196 goto destroy_kobj;
197 }
198 return 0;
199
200destroy_kobj:
201 kobject_put(vkey_obj);
202
203 return ret;
204}
205
206static int __devexit vkeys_remove(struct platform_device *pdev)
207{
208 sysfs_remove_group(vkey_obj, &vkey_grp);
209 kobject_put(vkey_obj);
210
211 return 0;
212}
213
214static struct of_device_id vkey_match_table[] = {
215 { .compatible = "qcom,gen-vkeys",},
216 { },
217};
218
219static struct platform_driver vkeys_driver = {
220 .probe = vkeys_probe,
221 .remove = __devexit_p(vkeys_remove),
222 .driver = {
223 .owner = THIS_MODULE,
224 .name = "gen_vkeys",
225 .of_match_table = vkey_match_table,
226 },
227};
228
229module_platform_driver(vkeys_driver);
230MODULE_LICENSE("GPL v2");