RE: [PATCH] input/tc3589x: add support for tc3589x driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Sundar,
Some minor comments.

> -----Original Message-----
> From: linux-input-owner@xxxxxxxxxxxxxxx [mailto:linux-input-
> owner@xxxxxxxxxxxxxxx] On Behalf Of Sundar Iyer
> Sent: Friday, November 26, 2010 8:35 PM
> To: ken.lierman@xxxxxxxxxxxxx
> Cc: dmitry.torokhov@xxxxxxxxx; alan@xxxxxxxxxxxxxxx;
> linus.walleij@xxxxxxxxxxxxxx; rabin.vincent@xxxxxxxxxxxxxx; linux-
> input@xxxxxxxxxxxxxxx; sundar.iyer@xxxxxxxxxxxxxx
> Subject: [PATCH] input/tc3589x: add support for tc3589x driver
>
> This adds support for the TC35893 keypad controller
> as a MFD client to the TC3589x driver
>
> Signed-off-by: Sundar Iyer <sundar.iyer@xxxxxxxxxxxxxx>
> ---
>  drivers/input/keyboard/Kconfig          |   19 ++
>  drivers/input/keyboard/Makefile         |    1 +
>  drivers/input/keyboard/tc3589x-keypad.c |  498
> +++++++++++++++++++++++++++++++
>  3 files changed, 518 insertions(+), 0 deletions(-)
>  create mode 100755 drivers/input/keyboard/tc3589x-keypad.c
Be careful about the permissions.

>
> diff --git a/drivers/input/keyboard/Kconfig
> b/drivers/input/keyboard/Kconfig
> index b8c51b9..01326fd 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -443,6 +443,25 @@ config KEYBOARD_OMAP4
>         To compile this driver as a module, choose M here: the
>         module will be called omap4-keypad.
>
>         To compile this driver as a module, choose M here: the
>         module will be called spear-keboard.
> +
> +config KEYBOARD_TC3589X
> +        tristate "TC3589X Keypad support"
> +        depends on MFD_TC3589X
> +        help
> +          Say Y here if you want to use the keypad controller on
> +          TC35892/3 I/O expander
> +
> +          To compile this driver as a module, choose M here: the
> +          module will be called tc3589x-keypad
> +
>  config KEYBOARD_TNETV107X
>       tristate "TI TNETV107X keypad support"
>       depends on ARCH_DAVINCI_TNETV107X
> diff --git a/drivers/input/keyboard/Makefile
> b/drivers/input/keyboard/Makefile
> index a34452e..4411c70 100644
> --- a/drivers/input/keyboard/Makefile
> +++ b/drivers/input/keyboard/Makefile
> @@ -40,6 +40,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC)             += sh_keysc.o
>  obj-$(CONFIG_KEYBOARD_STMPE)         += stmpe-keypad.o
>  obj-$(CONFIG_KEYBOARD_STOWAWAY)              += stowaway.o
>  obj-$(CONFIG_KEYBOARD_SUNKBD)                += sunkbd.o
> +obj-$(CONFIG_KEYBOARD_TC3589X)               += tc3589x-keypad.o
>  obj-$(CONFIG_KEYBOARD_TNETV107X)     += tnetv107x-keypad.o
>  obj-$(CONFIG_KEYBOARD_TWL4030)               += twl4030_keypad.o
>  obj-$(CONFIG_KEYBOARD_XTKBD)         += xtkbd.o
> diff --git a/drivers/input/keyboard/tc3589x-keypad.c
> b/drivers/input/keyboard/tc3589x-keypad.c
> new file mode 100755
> index 0000000..fb1a2f2
> --- /dev/null
> +++ b/drivers/input/keyboard/tc3589x-keypad.c
> @@ -0,0 +1,498 @@
> +/*
> + * Copyright (C) ST-Ericsson SA 2010
> + *
> + * Author: Jayeeta Banerjee <jayeeta.banerjee@xxxxxxxxxxxxxx>
> + * Author: Sundar Iyer <sundar.iyer@xxxxxxxxxxxxxx>
> + *
> + * License Terms: GNU General Public License, version 2
> + *
> + * TC35893 MFD Keypad Controller driver
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/spinlock.h>
> +#include <linux/input.h>
> +#include <linux/platform_device.h>
> +#include <linux/input/matrix_keypad.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/mfd/tc3589x.h>
> +
> +/* Maximum supported keypad matrix row/columns size */
> +#define TC3589x_MAX_KPROW               8
> +#define TC3589x_MAX_KPCOL               12
> +
> +/* keypad related Constants */
> +#define TC3589x_MAX_DEBOUNCE_SETTLE     0xFF
> +#define DEDICATED_KEY_VAL            0xFF
> +
> +/* Pull up/down masks */
> +#define TC3589x_NO_PULL_MASK 0x0
> +#define TC3589x_PULL_DOWN_MASK       0x1
> +#define TC3589x_PULL_UP_MASK 0x2
> +#define TC3589x_PULLUP_ALL_MASK      0xAA
> +#define TC3589x_IO_PULL_VAL(index, mask)     ((mask)<<((index)%4)*2))
> +
> +/* Bit masks for IOCFG register */
> +#define IOCFG_BALLCFG                0x01
> +#define IOCFG_IG             0x08
> +
> +#define KP_EVCODE_COL_MASK   0x0F
> +#define KP_EVCODE_ROW_MASK   0x70
> +#define KP_RELEASE_EVT_MASK  0x80
> +
> +#define KP_ROW_SHIFT         4
> +
> +#define KP_NO_VALID_KEY_MASK 0x7F
> +
> +/* bit masks for RESTCTRL register */
> +#define TC3589x_KBDRST               0x2
> +#define TC3589x_IRQRST               0x10
> +#define TC3589x_RESET_ALL    0x1B
> +
> +/* KBDMFS register bit mask */
> +#define TC3589x_KBDMFS_EN    0x1
> +
> +/* CLKEN register bitmask */
> +#define KPD_CLK_EN           0x1
> +
> +/* RSTINTCLR register bit mask */
> +#define IRQ_CLEAR            0x1
> +
> +/* bit masks for keyboard interrupts*/
> +#define TC3589x_EVT_LOSS_INT 0x8
> +#define TC3589x_EVT_INT              0x4
> +#define TC3589x_KBD_LOSS_INT 0x2
> +#define TC3589x_KBD_INT              0x1
> +
> +/* bit masks for keyboard interrupt clear*/
> +#define TC3589x_EVT_INT_CLR  0x2
> +#define TC3589x_KBD_INT_CLR  0x1
> +
> +#define TC3589x_KBD_KEYMAP_SIZE     64
> +
> +/**
> + * struct tc_keypad - data structure used by keypad driver
> + * @input:      pointer to input device object
> + * @board:      keypad platform device
> + * @krow:    number of rows
> + * @kcol:    number of coloumns
> + * @keymap:     matrix scan code table for keycodes
> + * @enable:  bool to enable/disable keypad operation
> + */
> +struct tc_keypad {
> +     struct tc3589x *tc3589x;
> +     struct input_dev *input;
> +     const struct tc3589x_keypad_platform_data *board;
> +     unsigned int krow;
> +     unsigned int kcol;
> +     unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE];
> +     bool enable;
> +};
> +
> +static int __devinit tc3589x_keypad_init_key_hardware(struct tc_keypad
> *keypad)
> +{
> +     int ret;
> +     struct tc3589x *tc3589x = keypad->tc3589x;
> +     u8 settle_time = keypad->board->settle_time;
> +     u8 dbounce_period = keypad->board->debounce_period;
> +     u8 rows = keypad->board->krow & 0xf;    /* mask out the nibble */
> +     u8 column = keypad->board->kcol & 0xf;  /* mask out the nibble */
> +
> +     /* validate platform configurations */
> +     if ((keypad->board->kcol > TC3589x_MAX_KPCOL) ||
> +         (keypad->board->krow > TC3589x_MAX_KPROW) ||
> +         (keypad->board->debounce_period > TC3589x_MAX_DEBOUNCE_SETTLE)
> ||
> +         (keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE))
> +             return -EINVAL;
> +
> +     /* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_KBDSIZE, 0x0,
> +                     (rows << KP_ROW_SHIFT) | column);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* configure dedicated key config, no dedicated key selected */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_KBCFG_LSB,
> +                                             0x0, DEDICATED_KEY_VAL);
> +     if (ret < 0)
> +             return ret;
> +
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_KBCFG_MSB,
> +                                             0x0, DEDICATED_KEY_VAL);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Configure settle time */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_KBDSETTLE_REG,
> +                                             0x0, settle_time);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Configure debounce time */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_KBDBOUNCE, 0x0,
> dbounce_period);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Start of initialise keypad GPIOs */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Configure pull-up resistors for all row GPIOs */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_IOPULLCFG0_LSB, 0x0,
> +                     TC3589x_PULLUP_ALL_MASK);
> +     if (ret < 0)
> +             return ret;
> +
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_IOPULLCFG0_MSB, 0x0,
> +                     TC3589x_PULLUP_ALL_MASK);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Configure pull-up resistors for all column GPIOs */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_IOPULLCFG1_LSB, 0x0,
> +                     TC3589x_PULLUP_ALL_MASK);
> +     if (ret < 0)
> +             return ret;
> +
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_IOPULLCFG1_MSB, 0x0,
> +                     TC3589x_PULLUP_ALL_MASK);
> +     if (ret < 0)
> +             return ret;
> +
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_IOPULLCFG2_LSB, 0x0,
> +                     TC3589x_PULLUP_ALL_MASK);
> +
> +     return ret;
> +}
> +
> +#define TC35893_DATA_REGS              4
> +#define TC35893_KEYCODE_FIFO_EMPTY     0x7f
> +#define TC35893_KEYCODE_FIFO_CLEAR     0xff
> +
> +static irqreturn_t tc3589x_keypad_irq(int irq, void *dev)
> +{
> +     struct tc_keypad *keypad = (struct tc_keypad *)dev;
> +     struct tc3589x *tc3589x = keypad->tc3589x;
> +     u8 i, row_index, col_index, kbd_code, up;
> +     u8 code;
> +
> +     for (i = 0; i < (TC35893_DATA_REGS * 2); i++) {
> +             kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO);
> +
> +             /* loop till fifo is empty and no more keys are pressed */
> +             if ((kbd_code == TC35893_KEYCODE_FIFO_EMPTY) ||
> +                             (kbd_code == TC35893_KEYCODE_FIFO_CLEAR))
> +                     continue;
> +
> +             /* valid key is found */
> +             col_index = kbd_code & KP_EVCODE_COL_MASK;
> +             row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT;
> +             code = MATRIX_SCAN_CODE(row_index, col_index, 0x3);
> +             up = kbd_code & KP_RELEASE_EVT_MASK;
> +
> +             input_event(keypad->input, EV_MSC, MSC_SCAN, code);
> +             input_report_key(keypad->input, keypad->keymap[code], !up);
> +             input_sync(keypad->input);
> +     }
> +
> +     /* clear IRQ */
> +     tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
> +                     0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
> +     /* enable IRQ */
> +     tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
> +                     0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static int __devinit tc3589x_keypad_enable(struct tc3589x *tc3589x)
This need not be __devinit as the sysfs hooks may need it.

> +{
> +     int ret;
> +
> +     /* pull the keypad module out of reset */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST,
> 0x0);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* configure KBDMFS */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0,
> TC3589x_KBDMFS_EN);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* enable the keypad clock */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* clear pending IRQs */
> +     ret =  tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* enable the IRQs */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0,
> +                                     TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
> +
> +     return ret;
> +}
> +
> +static int __devexit tc3589x_keypad_disable(struct tc3589x *tc3589x)
> +{
> +     int ret;
> +
> +     /* clear IRQ */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
> +                     0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* disable all interrupts */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
> +                     ~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* disable the keypad module */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* put the keypad module into reset */
> +     ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST,
> 0x1);
> +
> +     return ret;
> +}
> +
> +static ssize_t keypad_show_attr_enable(struct device *dev,
> +             struct device_attribute *attr, char *buf)
> +{
> +        struct platform_device *pdev = to_platform_device(dev);
> +        struct tc_keypad *keypad = platform_get_drvdata(pdev);
> +
> +     return sprintf(buf, "%u\n", keypad->enable);
> +}
> +
> +static ssize_t keypad_store_attr_enable(struct device *dev,
> +             struct device_attribute *attr, const char *buf, size_t count)
> +{
> +        struct platform_device *pdev = to_platform_device(dev);
> +        struct tc_keypad *keypad = platform_get_drvdata(pdev);
> +        struct tc3589x *tc3589x = keypad->tc3589x;
> +     unsigned long state;
> +
> +     if (sscanf(buf, "%lu", &state) != 1)
> +             return -EINVAL;
> +
> +     if ((state != 1) && (state != 0))
> +             return -EINVAL;
> +
> +     if (state != keypad->enable) {
> +             if (state)
> +                     tc3589x_keypad_enable(tc3589x);
> +             else
> +                     tc3589x_keypad_disable(tc3589x);
> +             keypad->enable = state;
> +     }
> +
> +     return strnlen(buf, count);
> +}
> +
> +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
> +             keypad_show_attr_enable, keypad_store_attr_enable);
> +
> +static struct attribute *tc3589x_keypad_attrs[] = {
> +     &dev_attr_enable.attr,
> +     NULL,
> +};
> +
> +static struct attribute_group tc3589x_attr_group = {
> +     .attrs = tc3589x_keypad_attrs,
> +};
> +
> +static int __devinit tc3589x_keypad_probe(struct platform_device *pdev)
> +{
> +     struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
> +     struct tc_keypad *keypad;
> +     struct input_dev *input;
> +     const struct tc3589x_keypad_platform_data *plat;
> +     int error, irq;
> +
> +     plat  = tc3589x->pdata->keypad;
> +     if (!plat) {
> +             dev_err(&pdev->dev, "invalid keypad platform data\n");
> +             return -EINVAL;
> +     }
> +
> +        irq = platform_get_irq(pdev, 0);
> +        if (irq < 0)
> +                return irq;
Could you use tabs instead of spaces here.
> +
> +     keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL);
> +     input = input_allocate_device();
> +     if (!keypad || !input) {
> +             dev_err(&pdev->dev, "failed to allocate keypad memory\n");
> +             error = -ENOMEM;
> +             goto err_free_mem;
> +     }
> +
> +     keypad->board = plat;
> +     keypad->input = input;
> +     keypad->tc3589x = tc3589x;
> +
> +     /* enable the keypad module */
> +     error = tc3589x_keypad_enable(tc3589x);
> +     if (error < 0) {
> +             dev_err(&pdev->dev, "failed to enable keypad module\n");
> +             goto err_free_mem;
> +     }
> +
> +     error = tc3589x_keypad_init_key_hardware(keypad);
> +     if (error < 0) {
> +             dev_err(&pdev->dev, "failed to configure keypad module\n");
> +             goto err_free_mem;
> +     }
> +
> +     input->id.bustype = BUS_HOST;
> +     input->name = pdev->name;
> +     input->dev.parent = &pdev->dev;
> +
> +     input->keycode = keypad->keymap;
> +     input->keycodesize = sizeof(keypad->keymap[0]);
> +     input->keycodemax = ARRAY_SIZE(keypad->keymap);
> +
> +     input_set_capability(input, EV_MSC, MSC_SCAN);
> +
> +     __set_bit(EV_KEY, input->evbit);
> +     if (!plat->no_autorepeat)
> +             __set_bit(EV_REP, input->evbit);
> +
> +     matrix_keypad_build_keymap(plat->keymap_data, 0x3,
> +                     input->keycode, input->keybit);
> +
> +     error = request_threaded_irq(irq, NULL,
> +                     tc3589x_keypad_irq, plat->irqtype,
> +                     "tc3589x-keypad", keypad);
> +     if (error < 0) {
> +             dev_err(&pdev->dev,
> +                             "Could not allocate irq %d,error %d\n",
> +                             plat->irq, error);
> +             goto err_free_mem;
> +     }
> +
> +     error = input_register_device(input);
> +     if (error) {
> +             dev_err(&pdev->dev, "Could not register input device\n");
> +             goto err_free_irq;
> +     }
> +
> +     device_init_wakeup(&pdev->dev, plat->enable_wakeup);
> +     device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup);
> +
> +     /* sysfs implementation for dynamic enable/disable the input event
> */
> +     error = sysfs_create_group(&pdev->dev.kobj, &tc3589x_attr_group);
> +     if (error) {
> +             dev_err(&pdev->dev, "Could not register sysfs entries\n");
> +             goto err_free_irq;
> +     }
> +
> +     keypad->enable = true;
> +     platform_set_drvdata(pdev, keypad);
> +
> +     return 0;
> +
> +err_free_irq:
> +     free_irq(plat->irq, keypad);
> +err_free_mem:
> +     input_free_device(input);
> +     kfree(keypad);
> +     return error;
> +}
> +
> +static int __devexit tc3589x_keypad_remove(struct platform_device *pdev)
> +{
> +        struct tc_keypad *keypad = platform_get_drvdata(pdev);
> +        struct tc3589x *tc3589x = keypad->tc3589x;
> +
> +     free_irq(keypad->board->irq, keypad);
> +
> +     sysfs_remove_group(&pdev->dev.kobj, &tc3589x_attr_group);
> +
> +     input_unregister_device(keypad->input);
> +
> +     tc3589x_keypad_disable(tc3589x);
> +
> +     kfree(keypad);
> +
> +     return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int tc3589x_keypad_suspend(struct device *dev)
> +{
> +     struct platform_device *pdev = to_platform_device(dev);
> +     struct tc_keypad *keypad = platform_get_drvdata(pdev);
> +     struct tc3589x *tc3589x = keypad->tc3589x;
> +     int irq = keypad->board->irq;
> +
> +     /* disable the IRQ */
> +     if (!device_may_wakeup(&pdev->dev))
> +             tc3589x_keypad_disable(tc3589x);
> +     else
> +             enable_irq_wake(irq);
> +
> +     return 0;
> +}
> +
> +static int tc3589x_keypad_resume(struct device *dev)
> +{
> +     struct platform_device *pdev = to_platform_device(dev);
> +     struct tc_keypad *keypad = platform_get_drvdata(pdev);
> +     struct tc3589x *tc3589x = keypad->tc3589x;
> +     int irq = keypad->board->irq;
> +
> +     /* enable the IRQ */
> +     if (!device_may_wakeup(&pdev->dev))
> +             tc3589x_keypad_enable(tc3589x);
> +     else
> +             disable_irq_wake(irq);
> +
> +     return 0;
> +}
> +
> +static const struct dev_pm_ops tc3589x_keypad_dev_pm_ops = {
> +     .suspend = tc3589x_keypad_suspend,
> +     .resume  = tc3589x_keypad_resume,
> +};
> +#endif
> +
> +static struct platform_driver tc3589x_keypad_driver = {
> +     .driver.name  = "tc3589x-keypad",
> +     .driver.owner = THIS_MODULE,
> +#ifdef CONFIG_PM
> +     .driver.pm = &tc3589x_keypad_dev_pm_ops,
> +#endif
> +     .probe = tc3589x_keypad_probe,
> +     .remove = __devexit_p(tc3589x_keypad_remove),
> +};
> +
> +static int __init tc3589x_keypad_init(void)
> +{
> +     return platform_driver_register(&tc3589x_keypad_driver);
> +}
> +
> +static void __exit tc3589x_keypad_exit(void)
> +{
> +     return platform_driver_unregister(&tc3589x_keypad_driver);
> +}
> +
> +module_init(tc3589x_keypad_init);
> +module_exit(tc3589x_keypad_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer");
> +MODULE_DESCRIPTION("TC35893 Keypad Driver");
> +
> --
> 1.7.2.dirty
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux