Merge "input: atmel_mxt_ts: Add device tree support" into msm-3.4
diff --git a/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt
new file mode 100644
index 0000000..a6c83d0
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/atmel-mxt-ts.txt
@@ -0,0 +1,128 @@
+Atmel touch controller
+
+Required properties:
+
+ - compatible : should be "atmel,mxt-ts"
+ - reg : i2c slave address of the device
+ - interrupt-parent : parent of interrupt
+ - interrupts : touch sample interrupt to indicate presense or release
+ of fingers on the panel.
+ - atmel,panel-coords : touch panel minimum x, minimum y, maximum x and
+ maximum y resolution
+ - atmel,display-coords : LCD display minimum x, minimum y, maximum x and
+ maximum y resolution
+ - vdd_ana-supply : Analog power supply needed to power device
+ - atmel,irq-gpio : irq gpio
+ - atmel,reset-gpio : reset gpio
+ - atmel,family-id : family identification of the controller
+ - atmel,variant-id : variant identification of the controller
+ - atmel,version : firmware version of the controller
+ - atmel,build i : firmware build number of the controller
+ - atmel,bootldr-id : bootloader identification of the controller
+ - atmel,fw-name : firmware name to used for flashing firmware
+
+Optional property:
+ - atmel,config : configuration parameter for the controller
+ - atmel,i2c-pull-up : specify to indicate pull up is needed
+ - vcc_i2c-supply : Power source required to pull up i2c bus
+ - atmel,dig-reg-support : specify to indicate digital regulator is
+ needed
+
+Example:
+ i2c@f9966000 {
+ cell-index = <3>;
+ compatible = "qcom,i2c-qup";
+ reg = <0xf9966000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 104 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <24000000>;
+
+ atmel_mxt_ts@4a {
+ compatible = "atmel,mxt-ts";
+ reg = <0x4a>
+ interrupt-parent = <&msmgpio>
+ interrupts = <48 0x0>;
+ vdd_ana-supply = <&pm8941_l18>;
+ vcc_i2c-supply = <&pm8941_lvs1>;
+ atmel,panel-coords = <0 0 479 799>;
+ atmel,display-coords = <0 0 479 799>;
+ atmel,i2c-pull-up = <1>;
+ atmel,dig-reg-support;
+ atmel,key-codes = <
+ 102 139 0 0 0 0 0 0
+ 0 158 217 0 0 0 0 0
+ 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 >;
+ atmel,irq-gpio = <&msmgpio 48 0>;
+ atmel,reset-gpio = <&msmgpio 26 0>;
+ atmel,cfg_1 {
+ atmel,family-id = <0x81>;
+ atmel,variant-id = <0x01>;
+ atmel,version = <0x10>;
+ atmel,build = <0xaa>;
+ atmel,config = [
+ /* Object 6, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 38, Instance = 0 */
+ 15 00 02 10 08 0C 00 00
+ /* Object 7, Instance = 0 */
+ FF FF 32 03
+ /* Object 8, Instance = 0 */
+ 0F 00 0A 0A 00 00 0A 00 00 00
+ /* Object 9, Instance = 0 */
+ 83 00 00 18 0E 00 70 32 02 01
+ 00 03 01 01 05 0A 0A 0A 90 05
+ F8 02 00 00 0F 0F 00 00 48 2D
+ 07 0C 00 00 00 00
+ /* Object 15, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00
+ /* Object 18, Instance = 0 */
+ 00 00
+ /* Object 19, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 23, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00
+ /* Object 25, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00
+ /* Object 40, Instance = 0 */
+ 00 00 00 00 00
+ /* Object 42, Instance = 0 */
+ 00 00 00 00 00 00 00 00 00 00
+ /* Object 46, Instance = 0 */
+ 00 00 10 10 00 00 03 00 00 01
+ /* Object 47, Instance = 0 */
+ 08 0A 28 0A 02 0A 00 8C 00 20
+ 00 00 00
+ /* Object 55, Instance = 0 */
+ 00 00 00 00 00 00
+ /* Object 56, Instance = 0 */
+ 03 00 01 18 05 05 05 05 05 05
+ 05 05 05 05 05 05 05 05 05 05
+ 05 05 05 05 05 05 05 05 00 00
+ 00 00 00 00 00 00 00 00 00 00
+ 00 00
+ /* Object 57, Instance = 0 */
+ 00 00 00
+ /* Object 61, Instance = 0 */
+ 00 00 00 00 00
+ /* Object 61, Instance = 1 */
+ 00 00 00 00 00
+ /* Object 62, Instance = 0 */
+ 7F 03 00 16 00 00 00 00 00 00
+ 04 08 10 18 05 00 0A 05 05 50
+ 14 19 34 1A 64 00 00 04 40 00
+ 00 00 00 00 30 32 02 00 01 00
+ 05 00 00 00 00 00 00 00 00 00
+ 00 00 0C 00
+ ];
+ }
+
+ }
+ };
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 49a081e..e696973 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -26,7 +26,7 @@
#include <linux/seq_file.h>
#include <linux/regulator/consumer.h>
#include <linux/string.h>
-
+#include <linux/of_gpio.h>
#if defined(CONFIG_HAS_EARLYSUSPEND)
#include <linux/earlysuspend.h>
/* Early-suspend level */
@@ -290,6 +290,8 @@
#define MXT_CFG_VERSION_LESS 1
#define MXT_CFG_VERSION_GREATER 2
+#define MXT_COORDS_ARR_SIZE 4
+
#define MXT_DEBUGFS_DIR "atmel_mxt_ts"
#define MXT_DEBUGFS_FILE "object"
@@ -2324,14 +2326,228 @@
}
}
+#ifdef CONFIG_OF
+static int mxt_get_dt_coords(struct device *dev, char *name,
+ struct mxt_platform_data *pdata)
+{
+ u32 coords[MXT_COORDS_ARR_SIZE];
+ struct property *prop;
+ struct device_node *np = dev->of_node;
+ int coords_size, rc;
+
+ prop = of_find_property(np, name, NULL);
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ coords_size = prop->length / sizeof(u32);
+ if (coords_size != MXT_COORDS_ARR_SIZE) {
+ dev_err(dev, "invalid %s\n", name);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32_array(np, name, coords, coords_size);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read %s\n", name);
+ return rc;
+ }
+
+ if (strncmp(name, "atmel,panel-coords",
+ sizeof("atmel,panel-coords")) == 0) {
+ pdata->panel_minx = coords[0];
+ pdata->panel_miny = coords[1];
+ pdata->panel_maxx = coords[2];
+ pdata->panel_maxy = coords[3];
+ } else if (strncmp(name, "atmel,display-coords",
+ sizeof("atmel,display-coords")) == 0) {
+ pdata->disp_minx = coords[0];
+ pdata->disp_miny = coords[1];
+ pdata->disp_maxx = coords[2];
+ pdata->disp_maxy = coords[3];
+ } else {
+ dev_err(dev, "unsupported property %s\n", name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mxt_parse_config(struct device *dev, struct device_node *np,
+ struct mxt_config_info *info)
+{
+ struct property *prop;
+ u8 *temp_cfg;
+
+ prop = of_find_property(np, "atmel,config", &info->config_length);
+ if (!prop) {
+ dev_err(dev, "Looking up %s property in node %s failed",
+ "atmel,config", np->full_name);
+ return -ENODEV;
+ } else if (!info->config_length) {
+ dev_err(dev, "Invalid length of configuration data\n");
+ return -EINVAL;
+ }
+
+ temp_cfg = devm_kzalloc(dev,
+ info->config_length * sizeof(u8), GFP_KERNEL);
+ if (!temp_cfg) {
+ dev_err(dev, "Unable to allocate memory to store cfg\n");
+ return -ENOMEM;
+ }
+
+ memcpy(temp_cfg, prop->value, info->config_length);
+ info->config = temp_cfg;
+
+ return 0;
+}
+
+static int mxt_parse_dt(struct device *dev, struct mxt_platform_data *pdata)
+{
+ int rc;
+ struct mxt_config_info *info;
+ struct device_node *temp, *np = dev->of_node;
+ struct property *prop;
+ u32 temp_val;
+
+ rc = mxt_get_dt_coords(dev, "atmel,panel-coords", pdata);
+ if (rc)
+ return rc;
+
+ rc = mxt_get_dt_coords(dev, "atmel,display-coords", pdata);
+ if (rc)
+ return rc;
+
+ /* regulator info */
+ pdata->i2c_pull_up = of_property_read_bool(np, "atmel,i2c-pull-up");
+ pdata->digital_pwr_regulator = of_property_read_bool(np,
+ "atmel,dig-reg-support");
+ /* reset, irq gpio info */
+ pdata->reset_gpio = of_get_named_gpio_flags(np, "atmel,reset-gpio",
+ 0, &pdata->reset_gpio_flags);
+ pdata->irq_gpio = of_get_named_gpio_flags(np, "atmel,irq-gpio",
+ 0, &pdata->irq_gpio_flags);
+
+ /* keycodes for keyarray object*/
+ prop = of_find_property(np, "atmel,key-codes", NULL);
+ if (prop) {
+ pdata->key_codes = devm_kzalloc(dev,
+ sizeof(int) * MXT_KEYARRAY_MAX_KEYS,
+ GFP_KERNEL);
+ if (!pdata->key_codes)
+ return -ENOMEM;
+ if ((prop->length/sizeof(u32)) == MXT_KEYARRAY_MAX_KEYS) {
+ rc = of_property_read_u32_array(np, "atmel,key-codes",
+ pdata->key_codes, MXT_KEYARRAY_MAX_KEYS);
+ if (rc) {
+ dev_err(dev, "Unable to read key codes\n");
+ return rc;
+ }
+ } else
+ return -EINVAL;
+ }
+
+ /* config array size */
+ pdata->config_array_size = 0;
+ temp = NULL;
+ while ((temp = of_get_next_child(np, temp)))
+ pdata->config_array_size++;
+
+ if (!pdata->config_array_size)
+ return 0;
+
+ info = devm_kzalloc(dev, pdata->config_array_size *
+ sizeof(struct mxt_config_info), GFP_KERNEL);
+ if (!info) {
+ dev_err(dev, "Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ pdata->config_array = info;
+
+ for_each_child_of_node(np, temp) {
+ rc = of_property_read_string(temp, "atmel,fw-name",
+ &info->fw_name);
+ if (rc && (rc != -EINVAL)) {
+ dev_err(dev, "Unable to read fw name\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(temp, "atmel,family-id", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read family id\n");
+ return rc;
+ } else
+ info->family_id = (u8) temp_val;
+
+ rc = of_property_read_u32(temp, "atmel,variant-id", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read variant id\n");
+ return rc;
+ } else
+ info->variant_id = (u8) temp_val;
+
+ rc = of_property_read_u32(temp, "atmel,version", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read controller version\n");
+ return rc;
+ } else
+ info->version = (u8) temp_val;
+
+ rc = of_property_read_u32(temp, "atmel,build", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read build id\n");
+ return rc;
+ } else
+ info->build = (u8) temp_val;
+
+ info->bootldr_id = of_property_read_u32(temp,
+ "atmel,bootldr-id", &temp_val);
+ if (rc) {
+ dev_err(dev, "Unable to read bootldr-id\n");
+ return rc;
+ } else
+ info->bootldr_id = (u8) temp_val;
+
+ rc = mxt_parse_config(dev, temp, info);
+ if (rc) {
+ dev_err(dev, "Unable to parse config data\n");
+ return rc;
+ }
+ info++;
+ }
+
+ return 0;
+}
+#else
+static int mxt_parse_dt(struct device *dev, struct mxt_platform_data *pdata)
+{
+ return -ENODEV;
+}
+#endif
+
static int __devinit mxt_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const struct mxt_platform_data *pdata = client->dev.platform_data;
+ struct mxt_platform_data *pdata;
struct mxt_data *data;
struct input_dev *input_dev;
int error, i;
+ if (client->dev.of_node) {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct mxt_platform_data), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ error = mxt_parse_dt(&client->dev, pdata);
+ if (error)
+ return error;
+ } else
+ pdata = client->dev.platform_data;
+
if (!pdata)
return -EINVAL;
@@ -2353,7 +2569,6 @@
data->client = client;
data->input_dev = input_dev;
data->pdata = pdata;
- data->irq = client->irq;
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
@@ -2411,46 +2626,47 @@
if (gpio_is_valid(pdata->irq_gpio)) {
/* configure touchscreen irq gpio */
- error = gpio_request(pdata->irq_gpio,
- "mxt_irq_gpio");
+ error = gpio_request(pdata->irq_gpio, "mxt_irq_gpio");
if (error) {
- pr_err("%s: unable to request gpio [%d]\n", __func__,
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
pdata->irq_gpio);
goto err_power_on;
}
error = gpio_direction_input(pdata->irq_gpio);
if (error) {
- pr_err("%s: unable to set_direction for gpio [%d]\n",
- __func__, pdata->irq_gpio);
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ pdata->irq_gpio);
goto err_irq_gpio_req;
}
+ data->irq = client->irq = gpio_to_irq(pdata->irq_gpio);
+ } else {
+ dev_err(&client->dev, "irq gpio not provided\n");
+ goto err_power_on;
}
if (gpio_is_valid(pdata->reset_gpio)) {
/* configure touchscreen reset out gpio */
- error = gpio_request(pdata->reset_gpio,
- "mxt_reset_gpio");
+ error = gpio_request(pdata->reset_gpio, "mxt_reset_gpio");
if (error) {
- pr_err("%s: unable to request reset gpio %d\n",
- __func__, pdata->reset_gpio);
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ pdata->reset_gpio);
goto err_irq_gpio_req;
}
- error = gpio_direction_output(
- pdata->reset_gpio, 1);
+ error = gpio_direction_output(pdata->reset_gpio, 1);
if (error) {
- pr_err("%s: unable to set direction for gpio %d\n",
- __func__, pdata->reset_gpio);
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ pdata->reset_gpio);
goto err_reset_gpio_req;
}
}
mxt_reset_delay(data);
-
error = mxt_initialize(data);
if (error)
goto err_reset_gpio_req;
-
error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
pdata->irqflags, client->dev.driver->name, data);
if (error) {
@@ -2557,11 +2773,20 @@
{ }
};
MODULE_DEVICE_TABLE(i2c, mxt_id);
+#ifdef OF_CONFIG
+static struct of_device_id mxt_match_table[] = {
+ { .compatible = "atmel,mxt-ts",},
+ { },
+};
+#else
+#define mxt_match_table NULL
+#endif
static struct i2c_driver mxt_driver = {
.driver = {
.name = "atmel_mxt_ts",
.owner = THIS_MODULE,
+ .of_match_table = mxt_match_table,
#ifdef CONFIG_PM
.pm = &mxt_pm_ops,
#endif
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index b54fcb4..5c3c728 100644
--- a/include/linux/i2c/atmel_mxt_ts.h
+++ b/include/linux/i2c/atmel_mxt_ts.h
@@ -69,7 +69,9 @@
bool i2c_pull_up;
bool digital_pwr_regulator;
int reset_gpio;
+ u32 reset_gpio_flags;
int irq_gpio;
+ u32 irq_gpio_flags;
int *key_codes;
u8(*read_chg) (void);