Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- From 9e2c0e5d074347b86d6cfdc7888d145c0c9c00ea Mon Sep 17 00:00:00 2001
- From: Roberto Nibali <rnibali@gmail.com>
- Date: Tue, 13 Mar 2012 16:14:39 +0100
- Subject: [PATCH 14/24] This mainly adds the ADC/TS driver developed by Juergen Beisert
- from Pengutronix.
- ---
- arch/arm/mach-imx/Kconfig | 4 +
- arch/arm/mach-imx/clock-imx25.c | 39 ++-
- arch/arm/mach-imx/devices-imx25.h | 4 +
- arch/arm/plat-mxc/Makefile | 1 +
- arch/arm/plat-mxc/adc-v1.c | 448 ++++++++++++++
- arch/arm/plat-mxc/devices/Makefile | 1 +
- arch/arm/plat-mxc/devices/platform-imx-adc.c | 61 ++
- arch/arm/plat-mxc/imx-hwmon.c | 146 +++++
- arch/arm/plat-mxc/imx-ts.c | 749 +++++++++++++++++++++++
- arch/arm/plat-mxc/include/mach/adc.h | 47 ++
- arch/arm/plat-mxc/include/mach/devices-common.h | 14 +
- arch/arm/plat-mxc/include/mach/imxfb.h | 2 +
- arch/arm/plat-mxc/include/mach/iomux-mx25.h | 9 +-
- arch/arm/plat-mxc/include/mach/mx25.h | 2 +
- drivers/video/backlight/pwm_bl.c | 10 +-
- drivers/video/imxfb.c | 265 +++++++--
- include/linux/platform_data/imx-adc.h | 54 ++
- 17 files changed, 1789 insertions(+), 67 deletions(-)
- create mode 100644 arch/arm/plat-mxc/adc-v1.c
- create mode 100644 arch/arm/plat-mxc/devices/platform-imx-adc.c
- create mode 100644 arch/arm/plat-mxc/imx-hwmon.c
- create mode 100644 arch/arm/plat-mxc/imx-ts.c
- create mode 100644 arch/arm/plat-mxc/include/mach/adc.h
- create mode 100644 include/linux/platform_data/imx-adc.h
- diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
- index 4defb97..7c71286 100644
- --- a/arch/arm/mach-imx/Kconfig
- +++ b/arch/arm/mach-imx/Kconfig
- @@ -1,6 +1,9 @@
- config IMX_HAVE_DMA_V1
- bool
- +config IMX_HAVE_ADC_V1
- + bool
- +
- config HAVE_IMX_GPC
- bool
- @@ -56,6 +59,7 @@ config SOC_IMX25
- select ARCH_MX25
- select CPU_ARM926T
- select ARCH_MXC_AUDMUX_V2
- + select IMX_HAVE_ADC_V1
- select ARCH_MXC_IOMUX_V3
- select MXC_AVIC
- diff --git a/arch/arm/mach-imx/clock-imx25.c b/arch/arm/mach-imx/clock-imx25.c
- index b0fec74c..e42b0a5 100644
- --- a/arch/arm/mach-imx/clock-imx25.c
- +++ b/arch/arm/mach-imx/clock-imx25.c
- @@ -33,6 +33,9 @@
- #define CCM_MPCTL 0x00
- #define CCM_UPCTL 0x04
- #define CCM_CCTL 0x08
- +# define CCM_CCTL_UPLL_RST (1 << 26)
- +# define CCM_CCTL_UPLL_DIS (1 << 23)
- +# define CCM_CCTL_MPLL_BYPASS (1 << 22)
- #define CCM_CGCR0 0x0C
- #define CCM_CGCR1 0x10
- #define CCM_CGCR2 0x14
- @@ -54,15 +57,23 @@
- static unsigned long get_rate_mpll(void)
- {
- ulong mpctl = __raw_readl(CRM_BASE + CCM_MPCTL);
- + u32 ctrl = __raw_readl(CRM_BASE + CCM_CCTL);
- +
- + if (ctrl & CCM_CCTL_MPLL_BYPASS)
- + return 24000000;
- return mxc_decode_pll(mpctl, 24000000);
- }
- static unsigned long get_rate_upll(void)
- {
- - ulong mpctl = __raw_readl(CRM_BASE + CCM_UPCTL);
- + ulong upctl = __raw_readl(CRM_BASE + CCM_UPCTL);
- + u32 ctrl = __raw_readl(CRM_BASE + CCM_CCTL);
- - return mxc_decode_pll(mpctl, 24000000);
- + if (ctrl & CCM_CCTL_UPLL_DIS)
- + return 0;
- +
- + return mxc_decode_pll(upctl, 24000000);
- }
- unsigned long get_rate_arm(struct clk *clk)
- @@ -293,7 +304,7 @@ static struct clk_lookup lookups[] = {
- _REGISTER_CLOCK("mxc_pwm.2", NULL, pwm3_clk)
- _REGISTER_CLOCK("mxc_pwm.3", NULL, pwm4_clk)
- _REGISTER_CLOCK("imx-keypad", NULL, kpp_clk)
- - _REGISTER_CLOCK("mx25-adc", NULL, tsc_clk)
- + _REGISTER_CLOCK("mx25-adc.0", NULL, tsc_clk)
- _REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk)
- _REGISTER_CLOCK("imx-i2c.1", NULL, i2c_clk)
- _REGISTER_CLOCK("imx-i2c.2", NULL, i2c_clk)
- @@ -314,6 +325,27 @@ static struct clk_lookup lookups[] = {
- _REGISTER_CLOCK(NULL, "iim", iim_clk)
- };
- +/*
- + * check, if the UPLL is up and running. Some bootloaders forget to
- + * enable it, but some of our peripherals rely on it.
- + */
- +static void __init mx25_init_upll(void)
- +{
- + u32 reg, ctrl = __raw_readl(CRM_BASE + CCM_CCTL);
- +
- + if (!(ctrl & CCM_CCTL_UPLL_DIS)) /* already enabled? */
- + return;
- +
- + pr_warn("%s: UPLL disabled. Enabling it\n", __func__);
- + /* re-set the default value of 240 MHz */
- + __raw_writel(0x00001400, CRM_BASE + CCM_UPCTL);
- +
- + reg = __raw_readl(CRM_BASE + CCM_CCTL);
- + reg &= ~(CCM_CCTL_UPLL_DIS | (0x3f << 16)); /* enable the UPLL */
- + reg |= CCM_CCTL_UPLL_RST | (0x03 << 16);
- + __raw_writel(reg, CRM_BASE + CCM_CCTL);
- +}
- +
- int __init mx25_clocks_init(void)
- {
- clkdev_add_table(lookups, ARRAY_SIZE(lookups));
- @@ -328,6 +360,7 @@ int __init mx25_clocks_init(void)
- #if defined(CONFIG_DEBUG_LL) && !defined(CONFIG_DEBUG_ICEDCC)
- clk_enable(&uart1_clk);
- #endif
- + mx25_init_upll();
- /* Clock source for lcdc and csi is upll */
- __raw_writel(__raw_readl(CRM_BASE+0x64) | (1 << 7) | (1 << 0),
- diff --git a/arch/arm/mach-imx/devices-imx25.h b/arch/arm/mach-imx/devices-imx25.h
- index efa0761..70d047c 100644
- --- a/arch/arm/mach-imx/devices-imx25.h
- +++ b/arch/arm/mach-imx/devices-imx25.h
- @@ -35,6 +35,10 @@ extern const struct imx_imx_fb_data imx25_imx_fb_data;
- #define imx25_add_imx_fb(pdata) \
- imx_add_imx_fb(&imx25_imx_fb_data, pdata)
- +extern const struct imx_imx_adc_data imx25_imx_adc_data;
- +#define imx25_add_imx_adc(pdata1, pdata2, pdata3) \
- + imx_add_imx_adc(&imx25_imx_adc_data, pdata1, pdata2, pdata3)
- +
- extern const struct imx_imx_i2c_data imx25_imx_i2c_data[];
- #define imx25_add_imx_i2c(id, pdata) \
- imx_add_imx_i2c(&imx25_imx_i2c_data[id], pdata)
- diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
- index 076db84f..a5c012b 100644
- --- a/arch/arm/plat-mxc/Makefile
- +++ b/arch/arm/plat-mxc/Makefile
- @@ -11,6 +11,7 @@ obj-$(CONFIG_MXC_AVIC) += avic.o
- obj-$(CONFIG_IMX_HAVE_IOMUX_V1) += iomux-v1.o
- obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o
- obj-$(CONFIG_IRAM_ALLOC) += iram_alloc.o
- +obj-$(CONFIG_IMX_HAVE_ADC_V1) += adc-v1.o imx-ts.o imx-hwmon.o
- obj-$(CONFIG_MXC_PWM) += pwm.o
- obj-$(CONFIG_MXC_ULPI) += ulpi.o
- obj-$(CONFIG_MXC_USE_EPIT) += epit.o
- diff --git a/arch/arm/plat-mxc/adc-v1.c b/arch/arm/plat-mxc/adc-v1.c
- new file mode 100644
- index 0000000..58917a0
- --- /dev/null
- +++ b/arch/arm/plat-mxc/adc-v1.c
- @@ -0,0 +1,448 @@
- +/*
- + * Copyright (C) 2011 Juergen Beisert, Pengutronix <kernel@pengutronix.de>
- + *
- + * This software is licensed under the terms of the GNU General Public
- + * License version 2, as published by the Free Software Foundation, and
- + * may be copied, distributed, and modified under those terms.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + */
- +#undef DEBUG
- +
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/platform_device.h>
- +#include <linux/clk.h>
- +#include <linux/slab.h>
- +#include <linux/io.h>
- +#include <linux/err.h>
- +#include <linux/interrupt.h>
- +#include <linux/platform_data/imx-adc.h>
- +
- +#include <mach/adc.h>
- +
- +#define DRIVER_NAME "mx25-adc"
- +
- +#define TGCR 0
- +# define TGCR_SLPC (1 << 4)
- +# define TGCR_IPG_CLK_EN (1 << 0)
- +# define TGCR_TSC_RST (1 << 1)
- +# define TGCR_POWER_MASK (0x3 << 8)
- +# define TGCR_POWER_OFF (0 << 8)
- +# define TGCR_POWER_SAVE (1 << 8)
- +# define TGCR_POWER_ON (2 << 8)
- +# define TGCR_INTREFEN (1 << 10)
- +# define TGCR_CLKCFG_MASK (0x1f << 16)
- +# define TGCR_SET_CLKCFG(x) ((x) << 16)
- +
- +#define TGSR 4
- +
- +#define TICR 8
- +#define TCQCR
- +#define TCQCR
- +# define CQCR_PD_MSK
- +#define TCQMR
- +# define TCQMR_PD_IRQ_MSK
- +
- +struct imx_adc_device {
- + struct platform_device *pdev;
- + struct clk *clk;
- + void __iomem *regs;
- + void *ts; /* only one instance allowed */
- + irqreturn_t (*ts_irq)(void*);
- + void *hwmon; /* only one instance allowed */
- + irqreturn_t (*hwmon_irq)(void*);
- + int irq;
- + unsigned adc_period; /* in [ns] */
- + unsigned adc_freq; /* in [Hz] */
- +};
- +
- +/* there is only one instance per SoC */
- +static struct imx_adc_device *adc;
- +
- +/* --------- API to outer space ------------------ */
- +
- +void __iomem *imx_adc_get_iomem(void *imx_adc)
- +{
- + struct imx_adc_device *adc = (struct imx_adc_device *)imx_adc;
- + return adc->regs;
- +}
- +EXPORT_SYMBOL_GPL(imx_adc_get_iomem);
- +
- +void imx_adc_put_iomem(void __iomem *p, void *imx_adc)
- +{
- +// struct imx_adc_device *adc = (struct imx_adc_device *)imx_adc;
- +}
- +EXPORT_SYMBOL_GPL(imx_adc_put_iomem);
- +
- +static void imx_wake_adc(struct imx_adc_device *adc)
- +{
- + u32 reg;
- +
- + reg = readl(adc->regs + TGCR) & ~TGCR_POWER_MASK;
- + reg |= TGCR_POWER_SAVE | TGCR_INTREFEN;
- + writel(reg, adc->regs + TGCR);
- +}
- +
- +static void imx_sleep_adc(struct imx_adc_device *adc)
- +{
- + u32 reg;
- +
- + reg = readl(adc->regs + TGCR) & ~(TGCR_POWER_MASK | TGCR_INTREFEN);
- + reg |= TGCR_POWER_OFF;
- + writel(reg, adc->regs + TGCR);
- +}
- +
- +void *imx_adc_register_ts(void *ts, irqreturn_t (*f)(void*))
- +{
- + if (adc->ts != NULL) /* FIXME locking */
- + return ERR_PTR(-EBUSY);
- + adc->ts = ts;
- + adc->ts_irq = f;
- +
- + /* now as a user is present, its time to wake the ADC */
- + imx_wake_adc(adc);
- + return (void *)adc;
- +}
- +EXPORT_SYMBOL_GPL(imx_adc_register_ts);
- +
- +int imx_adc_unregister_ts(void *imx_adc, void *ts)
- +{
- + struct imx_adc_device *adc = (struct imx_adc_device *)imx_adc;
- + if (adc->ts == NULL || adc->ts != ts)
- + return -EINVAL;
- + adc->ts = NULL;
- + adc->ts_irq = NULL;
- +
- + if (adc->hwmon == NULL) /* FIXME locking */
- + imx_sleep_adc(adc);
- +
- + return 0;
- +}
- +EXPORT_SYMBOL_GPL(imx_adc_unregister_ts);
- +
- +void *imx_adc_register_hwmon(void *hwmon, irqreturn_t (*f)(void*))
- +{
- + if (adc->hwmon != NULL) /* FIXME locking */
- + return ERR_PTR(-EBUSY);
- + adc->hwmon = hwmon;
- + adc->hwmon_irq = f;
- +
- + /* now as a user is present, its time to wake the ADC */
- + imx_wake_adc(adc);
- + return (void *)adc;
- +}
- +EXPORT_SYMBOL_GPL(imx_adc_register_hwmon);
- +
- +int imx_adc_unregister_hwmon(void *imx_adc, void *hwmon)
- +{
- + struct imx_adc_device *adc = (struct imx_adc_device *)imx_adc;
- + if (adc->hwmon == NULL || adc->hwmon != hwmon)
- + return -EINVAL;
- + adc->hwmon = NULL;
- + adc->hwmon_irq = NULL;
- +
- + if (adc->ts == NULL) /* FIXME locking */
- + imx_sleep_adc(adc);
- +
- + return 0;
- +}
- +EXPORT_SYMBOL_GPL(imx_adc_unregister_hwmon);
- +
- +unsigned imx_adc_get_period(void *imx_adc)
- +{
- + struct imx_adc_device *adc = (struct imx_adc_device *)imx_adc;
- + return adc->adc_period;
- +}
- +EXPORT_SYMBOL_GPL(imx_adc_get_period);
- +
- +void imx_adc_ts_set_tgcr(void *imx_adc, u32 val)
- +{
- + struct imx_adc_device *adc = (struct imx_adc_device *)imx_adc;
- + u32 reg;
- +
- + /* restrict access to touchscreen relevant bits */
- + val &= TGCR_PEN_DEBT_MASK | TGCR_PDBEN | TGCR_PDEN | TGCR_HSYNCEN | TGCR_HSYNCPOL;
- + reg = readl(adc->regs + TGCR) &
- + ~(TGCR_PEN_DEBT_MASK | TGCR_PDBEN | TGCR_PDEN | TGCR_HSYNCEN | TGCR_HSYNCPOL);
- + writel(reg | val, adc->regs + TGCR);
- + pr_info("Write TGCR with %08X\n", reg | val);
- +}
- +EXPORT_SYMBOL_GPL(imx_adc_ts_set_tgcr);
- +
- +/* --------- end of API to outer space ------------------ */
- +
- +static void mxc_adc_set_reg_bit(void __iomem *ofs, u32 bit)
- +{
- + writel(readl(ofs) | bit, ofs);
- +}
- +
- +static void mxc_adc_clear_reg_bit(void __iomem *ofs, u32 bit)
- +{
- + writel(readl(ofs) & ~bit, ofs);
- +}
- +
- +static void mxc_adc_enable_clk(struct imx_adc_device *adc)
- +{
- + clk_enable(adc->clk);
- + mxc_adc_set_reg_bit(adc->regs + TGCR, TGCR_IPG_CLK_EN);
- +}
- +
- +static void mxc_adc_disable_clk(struct imx_adc_device *adc)
- +{
- + mxc_adc_clear_reg_bit(adc->regs + TGCR, TGCR_IPG_CLK_EN);
- + clk_disable(adc->clk);
- +}
- +
- +static void mxc_adc_reset(struct imx_adc_device *adc)
- +{
- + mxc_adc_set_reg_bit(adc->regs + TGCR, TGCR_TSC_RST);
- +
- + /* TODO timeout */
- + while (readl(adc->regs + TGCR) & TGCR_TSC_RST)
- + ;
- +}
- +
- +static irqreturn_t mxc_adc_irq(int irq, void *pw)
- +{
- + struct imx_adc_device *adc =(struct imx_adc_device *)pw;
- + irqreturn_t ts_ret = IRQ_NONE, hwmon_ret = IRQ_NONE;
- + u32 irq_status = readl(adc->regs + TGSR);
- +
- + pr_debug("%s %X\n", __func__, irq_status);
- + /* what source calls us? */
- + if (irq_status & TGSR_TCQINT) {
- + /* touch screen unit */
- + if (adc->ts == NULL) {
- + /* no consumer, bogus event */
- + /* TODO */
- + } else
- + ts_ret = adc->ts_irq(adc->ts);
- + }
- +
- + if (irq_status & TGSR_GCQINT) {
- + /* hwmon unit */
- + if (adc->hwmon == NULL) {
- + /* no consumer, bogus event */
- + /* TODO */
- + } else
- + hwmon_ret = adc->hwmon_irq(adc->hwmon);
- + }
- +
- + if ((ts_ret == IRQ_NONE) && (hwmon_ret == IRQ_NONE)) {
- + pr_warn("Event happens without a source\n");
- + return IRQ_NONE;
- + }
- +
- + return IRQ_HANDLED;
- +}
- +
- +static void __devinit mxc_adc_setup_clock(struct imx_adc_device *adc)
- +{
- + struct device *dev = &adc->pdev->dev;
- + unsigned long inp_freq, adc_freq;
- + unsigned div;
- + u32 reg;
- +
- + /* enable the unit's clock first */
- + mxc_adc_enable_clk(adc);
- +
- + inp_freq = clk_get_rate(adc->clk);
- + div = DIV_ROUND_UP(inp_freq, adc->adc_freq);
- + if (div < 10) {
- + dev_warn(dev, "Clock @%lu Hz exceeds possible value."
- + " Adjusted to %lu Hz\n", inp_freq / div, inp_freq / 10);
- + div = 10;
- + }
- +
- + div -= 2;
- + div = DIV_ROUND_UP(div, 2);
- + adc_freq = inp_freq / (2 * div + 2);
- +
- + reg = readl(adc->regs + TGCR) & ~TGCR_CLKCFG_MASK;
- + writel(reg | TGCR_SET_CLKCFG(div), adc->regs + TGCR);
- +
- + adc->adc_period = 1000000000L / adc_freq;
- + dev_dbg(dev, "Clock setup to %lu Hz\n", adc_freq);
- +}
- +
- +static int __devinit mxc_adc_probe_dt(struct imx_adc_device *adc,
- + struct platform_device *pdev)
- +{
- + /* TODO */
- + return -ENODEV;
- +}
- +
- +static int __devinit mxc_adc_probe_pdata(struct imx_adc_device *adc,
- + struct platform_device *pdev)
- +{
- + struct imx_adc_platform_data *pdata = pdev->dev.platform_data;
- + struct device *dev = &pdev->dev;
- +
- + if (pdata == NULL) {
- + dev_err(dev, "No platform data given. Cannot continue\n");
- + return -EINVAL;
- + }
- +
- + adc->adc_freq = pdata->adc_freq;
- +
- + if (adc->adc_freq > 1750000) {
- + dev_warn(dev, "Clock @%lu Hz exceeds spec. Limited to 1.75 MHz\n",
- + pdata->adc_freq);
- + adc->adc_freq = 1750000;
- + }
- +
- + if (adc->adc_freq == 0) {
- + dev_warn(dev, "No run time clock given. Assuming 1.75 MHz\n");
- + adc->adc_freq = 1750000;
- + }
- +
- + return 0;
- +}
- +
- +static int __devinit mxc_adc_probe(struct platform_device *pdev)
- +{
- + struct device *dev = &pdev->dev;
- + struct resource *res;
- + int ret;
- +
- + adc = kzalloc(sizeof(struct imx_adc_device), GFP_KERNEL);
- + if (adc == NULL) {
- + dev_err(dev, "failed to allocate imx_adc_device\n");
- + return -ENOMEM;
- + }
- +
- + ret = mxc_adc_probe_dt(adc, pdev);
- + if (ret == -ENODEV)
- + mxc_adc_probe_pdata(adc, pdev);
- +
- + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- + if (res == NULL) {
- + dev_err(dev, "No ADC base address given\n");
- + ret = -ENXIO;
- + goto err_out0;
- + }
- +
- + adc->irq = platform_get_irq(pdev, 0);
- + if (adc->irq <= 0) {
- + dev_err(dev, "failed to get ADC's irq\n");
- + ret = -ENOENT;
- + goto err_out0;
- + }
- +
- + if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
- + dev_err(dev, "ADC seems already in use\n");
- + ret = -EBUSY;
- + goto err_out0;
- + }
- +
- + adc->regs = ioremap(res->start, resource_size(res));
- + if (adc->regs == NULL) {
- + dev_err(dev, "failed to map ADC registers\n");
- + ret = -ENXIO;
- + goto err_region;
- + }
- +
- + ret = request_irq(adc->irq, mxc_adc_irq, 0, dev_name(dev), adc);
- + if (ret < 0) {
- + dev_err(dev, "failed to claim ADC's irq\n");
- + goto err_ioremap;
- + }
- +
- + adc->clk = clk_get(dev, NULL);
- + if (IS_ERR(adc->clk)) {
- + dev_err(dev, "failed to get ADC's clock\n");
- + ret = PTR_ERR(adc->clk);
- + goto err_irq;
- + }
- +
- + adc->pdev = pdev;
- +
- + mxc_adc_setup_clock(adc);
- +
- + dev_info(dev, "Up and running\n");
- + return 0;
- +
- +err_irq:
- + free_irq(adc->irq, adc);
- +err_ioremap:
- + iounmap(adc->regs);
- +err_region:
- + release_mem_region(res->start, resource_size(res));
- +err_out0:
- + kfree(adc);
- + return ret;
- +}
- +
- +static int __devexit mxc_adc_remove(struct platform_device *pdev)
- +{
- + struct imx_adc_device *adc = platform_get_drvdata(pdev);
- + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- +
- + mxc_adc_reset(adc);
- + free_irq(adc->irq, adc);
- + mxc_adc_disable_clk(adc);
- + iounmap(adc->regs);
- + release_mem_region(res->start, resource_size(res));
- + clk_put(adc->clk);
- + kfree(adc);
- +
- + return 0;
- +}
- +
- +static int __maybe_unused mxc_adc_suspend(struct device *dev)
- +{
- + struct platform_device *pdev =
- + container_of(dev, struct platform_device, dev);
- + struct imx_adc_device *adc = platform_get_drvdata(pdev);
- + u32 reg;
- +
- + /* TODO */
- + return 0;
- +}
- +
- +static int __maybe_unused mxc_adc_resume(struct device *dev)
- +{
- + struct platform_device *pdev =
- + container_of(dev, struct platform_device, dev);
- + struct imx_adc_device *adc = platform_get_drvdata(pdev);
- + u32 reg;
- +
- + /* TODO */
- +
- + return 0;
- +}
- +
- +static const struct dev_pm_ops adc_pm_ops = {
- +#ifdef CONFIG_PM
- + .suspend = mxc_adc_suspend,
- + .resume = mxc_adc_resume,
- +#endif
- +};
- +
- +static struct platform_driver mxc_adc_driver = {
- + .driver = {
- + .name = DRIVER_NAME,
- + .owner = THIS_MODULE,
- + .pm = &adc_pm_ops,
- + },
- + .probe = mxc_adc_probe,
- + .remove = __devexit_p(mxc_adc_remove),
- +};
- +
- +static int __init mxc_adc_init(void)
- +{
- + int ret;
- +
- + ret = platform_driver_register(&mxc_adc_driver);
- + if (ret)
- + pr_err("%s: failed to add adc driver\n", __func__);
- +
- + return ret;
- +}
- +
- +module_init(mxc_adc_init);
- diff --git a/arch/arm/plat-mxc/devices/Makefile b/arch/arm/plat-mxc/devices/Makefile
- index c11ac84..ee7cdaf 100644
- --- a/arch/arm/plat-mxc/devices/Makefile
- +++ b/arch/arm/plat-mxc/devices/Makefile
- @@ -24,6 +24,7 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_PWM) += platform-mxc_pwm.o
- obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_RNGA) += platform-mxc_rnga.o
- obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_RTC) += platform-mxc_rtc.o
- obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_W1) += platform-mxc_w1.o
- +obj-$(CONFIG_IMX_HAVE_ADC_V1) += platform-imx-adc.o
- obj-$(CONFIG_IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX) += platform-sdhci-esdhc-imx.o
- obj-$(CONFIG_IMX_HAVE_PLATFORM_SPI_IMX) += platform-spi_imx.o
- obj-$(CONFIG_IMX_HAVE_PLATFORM_AHCI) += platform-ahci-imx.o
- diff --git a/arch/arm/plat-mxc/devices/platform-imx-adc.c b/arch/arm/plat-mxc/devices/platform-imx-adc.c
- new file mode 100644
- index 0000000..a57a7ba
- --- /dev/null
- +++ b/arch/arm/plat-mxc/devices/platform-imx-adc.c
- @@ -0,0 +1,61 @@
- +/*
- + * Copyright (C) 2011 Pengutronix
- + * Juergen Beisert <kernel@pengutronix.de>
- + *
- + * This program is free software; you can redistribute it and/or modify it under
- + * the terms of the GNU General Public License version 2 as published by the
- + * Free Software Foundation.
- + */
- +#include <mach/hardware.h>
- +#include <mach/devices-common.h>
- +#include <linux/platform_data/imx-adc.h>
- +
- +#define imx_imx_adc_data_entry_single(soc, _size) \
- + .iobase = soc ## _ADC ## _BASE_ADDR, \
- + .iosize = _size, \
- + .irq = soc ## _INT_ADC
- +
- +#ifdef CONFIG_SOC_IMX25
- +const struct imx_imx_adc_data imx25_imx_adc_data __initconst = {
- + imx_imx_adc_data_entry_single(MX25, SZ_16K),
- +};
- +#endif /* ifdef CONFIG_SOC_IMX25 */
- +
- +
- +struct platform_device *__init imx_add_imx_adc(
- + const struct imx_imx_adc_data *data,
- + const struct imx_adc_platform_data *adc_pdata,
- + const struct imx_ts_platform_data *ts_pdata,
- + const struct imx_hwmon_platform_data *hwmon_pdata)
- +{
- + struct platform_device *adc_pdev, *ts_dev = NULL, *hwmon_dev = NULL;
- + struct resource res[] = {
- + {
- + .start = data->iobase,
- + .end = data->iobase + data->iosize - 1,
- + .flags = IORESOURCE_MEM,
- + }, {
- + .start = data->irq,
- + .end = data->irq,
- + .flags = IORESOURCE_IRQ,
- + },
- + };
- +
- + adc_pdev = imx_add_platform_device("mx25-adc", 0,
- + res, ARRAY_SIZE(res),
- + adc_pdata, sizeof(*adc_pdata));
- +
- + if (ts_pdata != NULL) {
- + ts_dev = imx_add_platform_device("mx25-ts", 0, NULL, 0,
- + ts_pdata, sizeof(*ts_pdata));
- + ts_dev->dev.parent = &adc_pdev->dev;
- + }
- +
- + if (hwmon_pdata != NULL) {
- + hwmon_dev = imx_add_platform_device("mx25-hwmon", 0, NULL, 0,
- + hwmon_pdata, sizeof(*hwmon_pdata));
- + hwmon_dev->dev.parent = &adc_pdev->dev;
- + }
- +
- + return adc_pdev;
- +}
- diff --git a/arch/arm/plat-mxc/imx-hwmon.c b/arch/arm/plat-mxc/imx-hwmon.c
- new file mode 100644
- index 0000000..9427021
- --- /dev/null
- +++ b/arch/arm/plat-mxc/imx-hwmon.c
- @@ -0,0 +1,146 @@
- +/*
- + * Copyright (C) 2011 Juergen Beisert, Pengutronix <kernel@pengutronix.de>
- + *
- + * This software is licensed under the terms of the GNU General Public
- + * License version 2, as published by the Free Software Foundation, and
- + * may be copied, distributed, and modified under those terms.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + */
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/platform_device.h>
- +#include <linux/slab.h>
- +#include <linux/io.h>
- +#include <linux/err.h>
- +#include <linux/platform_data/imx-adc.h>
- +
- +#include <mach/adc.h>
- +
- +#define GCQFIFO 0x800
- +#define GCQCR 0x804
- +#define GCQSR 0x808
- +#define GCQMR 0x80c
- +#define GCQITEM_L 0x820
- +#define GCQITEM_H 0x824
- +#define GCC0 0x840
- +#define GCC1 0x844
- +#define GCC2 0x848
- +#define GCC3 0x84c
- +#define GCC4 0x850
- +#define GCC5 0x854
- +#define GCC6 0x858
- +#define GCC7 0x85c
- +
- +struct imx_hwmon_device {
- + struct platform_device *pdev;
- + void *imx_adc;
- + void __iomem *regs;
- +};
- +
- +static irqreturn_t mx25_hwmon_irq(void *p)
- +{
- + struct imx_hwmon_device *hwmon = (struct imx_hwmon_device *)p;
- +
- + return IRQ_NONE;
- +}
- +
- +static int __devinit imx_hwmon_probe(struct platform_device *pdev)
- +{
- + struct imx_hwmon_platform_data *pdata = pdev->dev.platform_data;
- + struct device *dev = &pdev->dev;
- + struct imx_hwmon_device *hwmon;
- + int rc;
- +
- + if (pdata == NULL) {
- + dev_err(dev, "No platform data given. Cannot continue\n");
- + return -EINVAL;
- + }
- +
- + hwmon = kzalloc(sizeof(struct imx_hwmon_device), GFP_KERNEL);
- + if (hwmon == NULL) {
- + dev_err(dev, "failed to allocate imx_hwmon_device\n");
- + return -ENOMEM;
- + }
- +
- + hwmon->imx_adc = imx_adc_register_hwmon(hwmon, mx25_hwmon_irq);
- + if (IS_ERR(hwmon->imx_adc)) {
- + dev_err(dev, "Cannot conntect to ADC main driver\n");
- + rc = PTR_ERR(hwmon->imx_adc);
- + goto on_register_error;
- + }
- +
- + hwmon->regs = imx_adc_get_iomem(hwmon->imx_adc);
- +
- + platform_set_drvdata(pdev, hwmon);
- +
- + return 0;
- +
- +on_input_dev_error:
- + imx_adc_put_iomem(hwmon->regs, hwmon->imx_adc);
- + imx_adc_unregister_hwmon(hwmon->imx_adc, hwmon);
- +on_register_error:
- + kfree(hwmon);
- + return rc;
- +}
- +
- +static int __devexit imx_hwmon_remove(struct platform_device *pdev)
- +{
- + struct imx_hwmon_device *hwmon = platform_get_drvdata(pdev);
- +
- + imx_adc_put_iomem(hwmon->regs, hwmon->imx_adc);
- + imx_adc_unregister_hwmon(hwmon->imx_adc, hwmon);
- + kfree(hwmon);
- + platform_set_drvdata(pdev, NULL);
- +
- + return 0;
- +}
- +
- +static int __maybe_unused imx_hwmon_suspend(struct device *dev)
- +{
- + struct platform_device *pdev = container_of(dev, struct platform_device, dev);
- + struct imx_hwmon_device *hwmon = platform_get_drvdata(pdev);
- +
- + return 0;
- +}
- +
- +static int __maybe_unused imx_hwmon_resume(struct device *dev)
- +{
- + struct platform_device *pdev = container_of(dev, struct platform_device, dev);
- + struct imx_hwmon_device *hwmon = platform_get_drvdata(pdev);
- +
- + return 0;
- +}
- +
- +static const struct dev_pm_ops hwmon_pm_ops = {
- +#ifdef CONFIG_PM
- + .suspend = imx_hwmon_suspend,
- + .resume = imx_hwmon_resume,
- +#endif
- +};
- +
- +static struct platform_driver imx_hwmon_driver = {
- + .driver = {
- + .name = "imx-hwmon",
- + .owner = THIS_MODULE,
- + .pm = &hwmon_pm_ops,
- + },
- + .probe = imx_hwmon_probe,
- + .remove = __devexit_p(imx_hwmon_remove),
- +};
- +
- +static int __init imx_hwmon_init(void)
- +{
- + int ret;
- +
- + ret = platform_driver_register(&imx_hwmon_driver);
- + if (ret)
- + pr_err("%s: failed to add hwmon driver\n", __func__);
- + return ret;
- +}
- +
- +module_init(imx_hwmon_init);
- diff --git a/arch/arm/plat-mxc/imx-ts.c b/arch/arm/plat-mxc/imx-ts.c
- new file mode 100644
- index 0000000..4dcad9b
- --- /dev/null
- +++ b/arch/arm/plat-mxc/imx-ts.c
- @@ -0,0 +1,749 @@
- +/*
- + * Copyright (C) 2011 Juergen Beisert, Pengutronix <kernel@pengutronix.de>
- + *
- + * This software is licensed under the terms of the GNU General Public
- + * License version 2, as published by the Free Software Foundation, and
- + * may be copied, distributed, and modified under those terms.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + */
- +#undef DEBUG
- +
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/platform_device.h>
- +#include <linux/input.h>
- +#include <linux/slab.h>
- +#include <linux/io.h>
- +#include <linux/workqueue.h>
- +#include <linux/platform_data/imx-adc.h>
- +
- +#include <mach/adc.h>
- +
- +#define TCQFIFO 0x400
- +# define TCQFIFO_TO_INDEX(x) ((x) & 0x0f)
- +# define TCQFIFO_TO_VAL(x) ((x) >> 4)
- +
- +#define TCQCR 0x404
- +# define TCQCR_LAST_ITEM_MASK 0xf0
- +# define TCQCR_LAST_ITEM(x) ((x) << 4)
- +# define TCQCR_QSM_MASK 0x3
- +# define TCQCR_QSM_STOP 0x0
- +# define TCQCR_QSM_PD (1 << 0)
- +# define TCQCR_QSM_FQS (1 << 1)
- +# define TCQCR_FQS (1 << 2)
- +# define TCQCR_RPT (1 << 3)
- +# define TCQCR_WATERMARK(x) ((x) << 8)
- +# define TCQCR_REPEATWAIT(x) ((x) << 12)
- +# define TCQCR_QRST (1 << 16)
- +# define TCQCR_FRST (1 << 17)
- +# define TCQCR_PD_MSK (1 << 18)
- +# define TCQCR_PD_CFG (1 << 19)
- +
- +#define TCQSR 0x408
- +# define TCQSR_PD (1 << 0)
- +# define TCQSR_EOQ (1 << 1)
- +# define TCQSR_FOR (1 << 4)
- +# define TCQSR_FUR (1 << 5)
- +# define TCQSR_FRR (1 << 6)
- +# define TCQSR_EMPT (1 << 13)
- +# define TCQSR_FDRY (1 << 15)
- +
- +#define TCQMR 0x40c
- +# define TCQMR_PD_IRQ_MSK (1 << 0)
- +# define TCQMR_EOQ_IRQ_MSK (1 << 1)
- +# define TCQMR_FDRY_MSK (1 << 15)
- +
- +#define TCQITEM_L 0x420
- +# define TCQ_ITEM0(x) (x)
- +# define TCQ_ITEM1(x) ((x) << 4)
- +# define TCQ_ITEM2(x) ((x) << 8)
- +# define TCQ_ITEM3(x) ((x) << 12)
- +# define TCQ_ITEM4(x) ((x) << 16)
- +# define TCQ_ITEM5(x) ((x) << 20)
- +# define TCQ_ITEM6(x) ((x) << 24)
- +# define TCQ_ITEM7(x) ((x) << 28)
- +# define ITEM_TCC0 0
- +# define ITEM_TCC1 1
- +# define ITEM_TCC2 2
- +# define ITEM_TCC3 3
- +# define ITEM_TCC4 4
- +# define ITEM_TCC5 5
- +
- +#define TCQITEM_U 0x424
- +#define TCC0 0x440
- +#define TCC1 0x444
- +#define TCC2 0x448
- +#define TCC3 0x44c
- +#define TCC4 0x450
- +#define TCC5 0x454
- +#define TCC6 0x458
- +#define TCC7 0x45c
- +
- +#define TCC_SETTLING_TIME(x) ((x) << 24)
- +#define TCC_IGS (1 << 20)
- +#define TCC_NOS(x) ((x) << 16)
- +
- +#define TCC_WIPERSW_ON (1 << 15)
- +#define TCC_WIPERSW_OFF (0 << 15)
- +
- +#define TCC_YNLRSW_L (1 << 14)
- +#define TCC_YNLRSW_OFF (0 << 14)
- +
- +#define TCC_YPLLSW_H (0 << 12)
- +#define TCC_YPLLSW_OFF (1 << 12)
- +#define TCC_YPLLSW_L (3 << 12)
- +
- +#define TCC_XNURSW_H (0 << 10)
- +#define TCC_XNURSW_OFF (1 << 10)
- +#define TCC_XNURSW_L (3 << 10)
- +
- +#define TCC_XPULSW_H (0 << 9)
- +#define TCC_XPULSW_OFF (1 << 9)
- +
- +#define TCC_SELREFP_YPLL (0 << 7)
- +#define TCC_SELREFP_XPUL (1 << 7)
- +#define TCC_SELREFP_EXT_REF (2 << 7)
- +#define TCC_SELREFP_INT_REF (3 << 7)
- +
- +#define TCC_SELIN_XPUL (0 << 4)
- +#define TCC_SELIN_YPLL (1 << 4)
- +#define TCC_SELIN_XNUR (2 << 4)
- +#define TCC_SELIN_YNLR (3 << 4)
- +#define TCC_SELIN_WIPER (4 << 4)
- +#define TCC_SELIN_INAUX0 (5 << 4)
- +#define TCC_SELIN_INAUX1 (6 << 4)
- +#define TCC_SELIN_INAUX2 (7 << 4)
- +
- +#define TCC_SELREFN_XNUR (0 << 2)
- +#define TCC_SELREFN_YNLR (1 << 2)
- +#define TCC_SELREFN_AGND1 (2 << 2)
- +#define TCC_SELREFN_AGND2 (3 << 2)
- +
- +#define TCC_PENIACK (1 << 1)
- +
- +/*
- + * Y+ off
- + * Y- off
- + * X+ high (with low impedance) <-- ADC in
- + * X- off
- + * ADC in single-ended measurement topology
- + */
- +#define TSC_4WIRE_PRECHARGE (TCC_WIPERSW_OFF | TCC_YNLRSW_OFF | \
- + TCC_YPLLSW_OFF | TCC_XNURSW_OFF | TCC_XPULSW_H | \
- + TCC_SELREFP_INT_REF | TCC_SELIN_XPUL | TCC_SELREFN_AGND2)
- +
- +/*
- + * Y+ low
- + * Y- low
- + * X+ off <-- ADC in
- + * X- off
- + * ADC in single-ended measurement topology
- + *
- + * 'TCC_PENIACK' enables a weak pull up at the X+ signal
- + */
- +#define TSC_4WIRE_TOUCH_DETECT (TCC_WIPERSW_OFF | TCC_YNLRSW_L | \
- + TCC_YPLLSW_L | TCC_XNURSW_OFF | TCC_XPULSW_OFF | \
- + TCC_SELREFP_INT_REF | TCC_SELIN_XPUL | TCC_SELREFN_AGND2 | \
- + TCC_PENIACK)
- +
- +/*
- + * Y+ off <-- ADC in
- + * Y- off
- + * X+ high <- high ADC ref
- + * X- low <- low ADC ref
- + * ADC in differential measurement topology
- + */
- +#define TSC_4WIRE_X_MEASUMENT (TCC_WIPERSW_OFF | TCC_YNLRSW_OFF | \
- + TCC_YPLLSW_OFF | TCC_XNURSW_L | TCC_XPULSW_H | \
- + TCC_SELREFP_XPUL | TCC_SELIN_YPLL | TCC_SELREFN_XNUR)
- +
- +/*
- + * Y+ high <- high ADC ref
- + * Y- low <- low ADC ref
- + * X+ off <-- ADC in
- + * X- off
- + * ADC in differential measurement topology
- + */
- +#define TSC_4WIRE_Y_MEASUMENT (TCC_WIPERSW_OFF | TCC_YNLRSW_L | \
- + TCC_YPLLSW_H | TCC_XNURSW_OFF | TCC_XPULSW_OFF | \
- + TCC_SELREFP_YPLL | TCC_SELIN_XPUL | TCC_SELREFN_YNLR)
- +
- +/*
- + * Pressure measurement (1/2)
- + * Y+ high
- + * Y- off
- + * X+ off <-- ADC in
- + * X- low
- + * ADC in single-ended measurement topology
- + */
- +#define TSC_4WIRE_XP_MEASUMENT (TCC_WIPERSW_OFF | TCC_YNLRSW_OFF | \
- + TCC_YPLLSW_H | TCC_XNURSW_L | TCC_XPULSW_OFF | \
- + TCC_SELREFP_INT_REF | TCC_SELIN_XPUL | TCC_SELREFN_AGND2)
- +
- +/*
- + * Pressure measurement (2/2)
- + * Y+ high
- + * Y- off <-- ADC in
- + * X+ off
- + * X- low
- + * ADC in single-ended measurement topology
- + */
- +#define TSC_4WIRE_YN_MEASUMENT (TCC_WIPERSW_OFF | TCC_YNLRSW_OFF | \
- + TCC_YPLLSW_H | TCC_XNURSW_L | TCC_XPULSW_OFF | \
- + TCC_SELREFP_INT_REF | TCC_SELIN_YNLR | TCC_SELREFN_AGND2)
- +
- +struct imx_ts_device {
- + struct platform_device *pdev;
- + struct input_dev *input;
- + void *imx_adc;
- + void __iomem *regs;
- +
- + struct work_struct work;
- +
- + /* accquisition settings */
- + unsigned pen_debounce; /* [us] */
- + unsigned pen_treshold;
- + unsigned repeat_wait; /* [us] */
- + unsigned settling_time; /* [us] */
- + unsigned sample_cnt;
- + unsigned in_sync; /* noise reduction settings */
- + /* touchscreen settings */
- + unsigned last_queue_item;
- + unsigned expected_samples;
- + unsigned avail_samples;
- + u32 sample_buf[16];
- +};
- +
- +static void imx_tsc_fill_state(struct imx_ts_device *ts, int state, u32 val)
- +{
- + writel(val, ts->regs + TCC0 + state * 4);
- + pr_debug("%s: filling state %d with %08X\n", __func__, state, val);
- +}
- +
- +/* four wire measure protocol */
- +#define TSC_4WIRE_PRE_INDEX ITEM_TCC0
- +#define TSC_4WIRE_X_INDEX ITEM_TCC1
- +#define TSC_4WIRE_Y_INDEX ITEM_TCC2
- +#define TSC_4WIRE_POST_INDEX ITEM_TCC3
- +#define TSC_4WIRE_LEAVE ITEM_TCC4
- +
- +static int imx_prepare_for_4wire(struct imx_ts_device *ts,
- + unsigned settling_time)
- +{
- + /*
- + * What should be done in the queue?
- + * - touch meas
- + * - x meas
- + * - y meas
- + * - touch detect
- + */
- +
- + /* for a propper touch detection, preload the X plane */
- + writel(TSC_4WIRE_PRECHARGE | TCC_IGS, ts->regs + TICR);
- +
- + /* we need always a sample to check if this event is valid */
- + imx_tsc_fill_state(ts, TSC_4WIRE_PRE_INDEX, TSC_4WIRE_TOUCH_DETECT |
- + TCC_NOS(0) | /* one sample */
- + TCC_SETTLING_TIME(settling_time));
- + /* measure X coordinate */
- + imx_tsc_fill_state(ts, TSC_4WIRE_X_INDEX, TSC_4WIRE_X_MEASUMENT |
- + TCC_NOS(ts->sample_cnt - 1) |
- + TCC_SETTLING_TIME(settling_time));
- + /* measure Y corrdinate */
- + imx_tsc_fill_state(ts, TSC_4WIRE_Y_INDEX, TSC_4WIRE_Y_MEASUMENT |
- + TCC_NOS(ts->sample_cnt - 1) |
- + TCC_SETTLING_TIME(settling_time));
- + /* we need always a sample to check if this event is valid */
- + imx_tsc_fill_state(ts, TSC_4WIRE_POST_INDEX, TSC_4WIRE_TOUCH_DETECT |
- + TCC_NOS(0) | /* one sample */
- + TCC_SETTLING_TIME(settling_time));
- + /* leave the touchscreen in detection configuration */
- + imx_tsc_fill_state(ts, TSC_4WIRE_LEAVE, TSC_4WIRE_TOUCH_DETECT |
- + TCC_IGS | TCC_SETTLING_TIME(settling_time));
- +
- + /*
- + * In which order these items have to be done
- + * touch meas -> x meas -> y meas -> touch meas -> touch detect
- + */
- + writel(TCQ_ITEM0(TSC_4WIRE_PRE_INDEX) |
- + TCQ_ITEM1(TSC_4WIRE_X_INDEX) |
- + TCQ_ITEM2(TSC_4WIRE_Y_INDEX) |
- + TCQ_ITEM3(TSC_4WIRE_POST_INDEX) |
- + TCQ_ITEM4(TSC_4WIRE_LEAVE),
- + ts->regs + TCQITEM_L);
- +
- + ts->expected_samples = ts->sample_cnt + ts->sample_cnt + 1 + 1;
- + ts->last_queue_item = 4;
- +
- + /* Enable the touch detection right now */
- + writel(TSC_4WIRE_TOUCH_DETECT | TCC_IGS, ts->regs + TICR);
- +
- + return 0;
- +}
- +
- +static void imx_disable_touch_irq(struct imx_ts_device *ts)
- +{
- + u32 tcqcr;
- +
- + tcqcr = readl(ts->regs + TCQCR);
- + writel(tcqcr | TCQCR_PD_MSK, ts->regs + TCQCR);
- +}
- +
- +static void imx_enable_touch_irq(struct imx_ts_device *ts)
- +{
- + u32 tcqcr;
- +
- + tcqcr = readl(ts->regs + TCQCR);
- + writel(tcqcr & ~TCQCR_PD_MSK, ts->regs + TCQCR);
- +}
- +
- +static void imx_disable_fifo_irq(struct imx_ts_device *ts)
- +{
- + u32 tcqmr;
- +
- + tcqmr = readl(ts->regs + TCQMR);
- + writel(tcqmr | TCQMR_FDRY_MSK, ts->regs + TCQMR);
- +}
- +
- +static void imx_enable_fifo_irq(struct imx_ts_device *ts)
- +{
- + u32 tcqmr;
- +
- + tcqmr = readl(ts->regs + TCQMR);
- + writel(tcqmr & ~TCQMR_FDRY_MSK, ts->regs + TCQMR);
- +}
- +
- +/* fire the queue once */
- +static void imx_force_queue_start(struct imx_ts_device *ts)
- +{
- + u32 tcqcr;
- +
- + tcqcr = readl(ts->regs + TCQCR);
- + writel(tcqcr | TCQCR_FQS, ts->regs + TCQCR);
- + /* clear the flag immediately, else the queue runs forever */
- + writel(tcqcr, ts->regs + TCQCR);
- +}
- +
- +static void imx_force_queue_stop(struct imx_ts_device *ts)
- +{
- + u32 tcqcr;
- +
- + tcqcr = readl(ts->regs + TCQCR);
- + writel(tcqcr & ~TCQCR_FQS, ts->regs + TCQCR);
- +}
- +
- +static void imx_fifo_reset(struct imx_ts_device *ts)
- +{
- + u32 tcqcr;
- +
- + tcqcr = readl(ts->regs + TCQCR);
- + writel(tcqcr | TCQCR_FRST, ts->regs + TCQCR);
- + writel(tcqcr, ts->regs + TCQCR);
- +}
- +
- +static void imx_re_enable_touch_detection(struct imx_ts_device *ts)
- +{
- + u32 tcqmr;
- +
- + /* stop the queue from looping */
- + imx_force_queue_stop(ts);
- +
- + /* for a clean touch detection, preload the X plane */
- + writel(TSC_4WIRE_PRECHARGE | TCC_IGS, ts->regs + TICR);
- +
- + /* waste some time now to pre-load the X plate to high voltage */
- + imx_fifo_reset(ts);
- +
- + /* re-enable the detection right now */
- + writel(TSC_4WIRE_TOUCH_DETECT | TCC_IGS, ts->regs + TICR);
- +
- + writel(TCQSR_PD, ts->regs + TCQSR);
- +
- + /* enable the pen down event to be a source for the interrupt */
- + tcqmr = readl(ts->regs + TCQMR);
- + writel(tcqmr & ~TCQMR_PD_IRQ_MSK, ts->regs + TCQMR);
- +
- + /* lets fire the next IRQ if someone touches the touchscreen */
- + imx_enable_touch_irq(ts);
- +}
- +
- +static int imx_create_event_for_4wire(struct imx_ts_device *ts)
- +{
- + unsigned x_pos = 0, y_pos = 0, touch_pre = 0, touch_post = 0;
- + unsigned u, index, val;
- + int rc = 0;
- +
- + for (u = 0; u < ts->avail_samples; u++) {
- + index = TCQFIFO_TO_INDEX(ts->sample_buf[u]);
- + val = TCQFIFO_TO_VAL(ts->sample_buf[u]);
- + switch (index) {
- + case TSC_4WIRE_PRE_INDEX:
- + touch_pre += val;
- + break;
- + case TSC_4WIRE_X_INDEX:
- + x_pos += val;
- + break;
- + case TSC_4WIRE_Y_INDEX:
- + y_pos += val;
- + break;
- + case TSC_4WIRE_POST_INDEX:
- + touch_post += val;
- + break;
- + default:
- + pr_err("Unknown index %u\n", index);
- + rc = -EINVAL; /* TODO deadlock! */
- + }
- + }
- +
- + if (rc == 0 && ts->avail_samples != 0) {
- + ts->avail_samples = 0;
- + /*
- + * only if both touch measures are below a treshold,
- + * the position is valid
- + */
- + if (touch_pre < ts->pen_treshold &&
- + touch_post < ts->pen_treshold) {
- + /* valid samples, generate a report */
- + x_pos /= ts->sample_cnt;
- + y_pos /= ts->sample_cnt;
- + input_report_abs(ts->input, ABS_X, x_pos);
- + input_report_abs(ts->input, ABS_Y, y_pos);
- + input_report_key(ts->input, BTN_TOUCH,
- + 0xfff - ((touch_pre + touch_post) / 2));
- + input_sync(ts->input);
- +
- + /* get next sample */
- + imx_force_queue_start(ts);
- + imx_enable_fifo_irq(ts);
- + } else {
- + if (touch_pre >= ts->pen_treshold &&
- + touch_post >= ts->pen_treshold) {
- + /*
- + * if both samples are invalid,
- + * generate a release report
- + */
- + input_report_key(ts->input, BTN_TOUCH, 0);
- + input_sync(ts->input);
- + imx_re_enable_touch_detection(ts);
- + } else {
- + /*
- + * if only one of both touch measurements are
- + * below the threshold, still some bouncing
- + * happens. Take additional samples in this
- + * case to be sure
- + */
- + imx_force_queue_start(ts);
- + imx_enable_fifo_irq(ts);
- + }
- + }
- + }
- +
- + return rc;
- +}
- +
- +static void mx25_ts_workqueue(struct work_struct *work)
- +{
- + struct imx_ts_device *ts = container_of(work, struct imx_ts_device, work);
- +
- + /* read all samples */
- + while (!(readl(ts->regs + TCQSR) & TCQSR_EMPT)) {
- + if (ts->avail_samples < 16) {
- + ts->sample_buf[ts->avail_samples++] = readl(ts->regs + TCQFIFO);
- + pr_debug("- %04X", ts->sample_buf[ts->avail_samples - 1]);
- + } else {
- + pr_debug("!");
- + /* discard samples */
- + readl(ts->regs + TCQFIFO);
- + }
- + }
- + imx_create_event_for_4wire(ts);
- +}
- +
- +/* ------------------------------------------------------------------------- */
- +
- +static irqreturn_t mx25_tsc_irq(void *p)
- +{
- + struct imx_ts_device *ts = (struct imx_ts_device *)p;
- + u32 stat = readl(ts->regs + TCQSR);
- +
- + pr_debug("%s: %08X\n", __func__, stat);
- +
- + if (stat & (TCQSR_FRR | TCQSR_FUR | TCQSR_FOR)) {
- + pr_debug("%s: Resetting FIFO\n", __func__);
- + imx_fifo_reset(ts);
- + writel(stat & (TCQSR_FRR | TCQSR_FUR | TCQSR_FOR), ts->regs + TCQSR);
- + }
- +
- + if (stat & TCQSR_PD) {
- + imx_disable_touch_irq(ts);
- + imx_force_queue_start(ts);
- + imx_enable_fifo_irq(ts);
- + writel(TCQSR_PD, ts->regs + TCQSR);
- + }
- +
- + if (stat & TCQSR_EOQ) {
- + writel(TCQSR_EOQ, ts->regs + TCQSR);
- + }
- +
- + if (stat & TCQSR_FDRY) {
- + imx_disable_fifo_irq(ts);
- + ts->avail_samples = 0;
- + schedule_work(&ts->work);
- + }
- +
- + return IRQ_HANDLED;
- +}
- +
- +/* configure the statemachine for a 4-wire touchscreen */
- +static void __init imx_ts_init(struct imx_ts_device *ts)
- +{
- + u32 tcqcr, in_sync = 0;
- + unsigned adc_period = imx_adc_get_period(ts->imx_adc);
- + unsigned repeat_wait = fls(DIV_ROUND_UP(ts->repeat_wait, adc_period));
- + unsigned debounce_cnt =
- + DIV_ROUND_UP(ts->pen_debounce, adc_period * 8) - 1;
- + unsigned settling_time = DIV_ROUND_UP(ts->settling_time, adc_period);
- +
- + /* reset the logic first */
- + writel(TCQCR_QRST | TCQCR_FRST, ts->regs + TCQCR);
- +
- + /* up to 128 * 8 ADC clocks are possible */
- + if (debounce_cnt > 127)
- + debounce_cnt = 127;
- +
- + if (repeat_wait > 15)
- + repeat_wait = 15;
- +
- + tcqcr = TCQCR_QSM_STOP |
- + TCQCR_REPEATWAIT(repeat_wait);
- + writel(tcqcr, ts->regs + TCQCR);
- +
- + imx_prepare_for_4wire(ts, settling_time);
- +
- + tcqcr |= TCQCR_LAST_ITEM(ts->last_queue_item - 1) |
- + TCQCR_WATERMARK(ts->expected_samples - 1);
- + writel(tcqcr, ts->regs + TCQCR);
- +
- + pr_debug("%s: adc_period: %u ns, debounce_cnt: %u\n", __func__,
- + adc_period, debounce_cnt);
- + pr_debug("%s: sample_count: %u, repeat_wait: %u\n", __func__,
- + ts->expected_samples, repeat_wait);
- + pr_debug("%s: value for TCQITEM_L is: %X\n", __func__,
- + readl(ts->regs + TCQITEM_L));
- +
- + /* ensure for now all interrupt sources are disabled */
- + writel(0xffffffff, ts->regs + TCQMR);
- +
- + switch (ts->in_sync) {
- + case MEAS_IF_LOW_HSYNC:
- + in_sync |= TGCR_HSYNCPOL; /* run the ADC while HYNC is low */
- + case MEAS_IF_HIG_HSYNC:
- + in_sync |= TGCR_HSYNCEN; /* enable this feature */
- + break;
- + }
- +
- + /* setup ... */
- + imx_adc_ts_set_tgcr(ts->imx_adc, in_sync |
- + TGCR_SET_PEN_DEBT(debounce_cnt));
- + /* ... and enable */
- + imx_adc_ts_set_tgcr(ts->imx_adc, in_sync |
- + TGCR_SET_PEN_DEBT(debounce_cnt) |
- + TGCR_PDBEN);
- + /* ... */
- + imx_adc_ts_set_tgcr(ts->imx_adc, in_sync |
- + TGCR_SET_PEN_DEBT(debounce_cnt) |
- + TGCR_PDBEN | TGCR_PDEN);
- +
- + /* enable the engine on demand */
- + tcqcr |= TCQCR_QSM_FQS;
- + writel(tcqcr, ts->regs + TCQCR);
- +
- + imx_re_enable_touch_detection(ts);
- +}
- +
- +static int __devinit imx_ts_register_input_dev(struct imx_ts_device *ts)
- +{
- + struct device *dev = &ts->pdev->dev;
- +
- + ts->input = input_allocate_device();
- + if (ts->input == NULL) {
- + dev_err(dev, "Unable to allocate the input device !!\n");
- + return -ENOMEM;
- + }
- +
- + ts->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- + ts->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- + input_set_abs_params(ts->input, ABS_X, 0, 0xfff, 0, 0);
- + input_set_abs_params(ts->input, ABS_Y, 0, 0xfff, 0, 0);
- +
- + ts->input->name = "mx25 ts";
- + ts->input->id.bustype = BUS_HOST;
- + ts->input->id.vendor = 0xDEAD;
- + ts->input->id.product = 0xBEEF;
- + ts->input->id.version = 0x0101;
- +
- + if (input_register_device(ts->input) < 0) {
- + dev_err(dev, "failed to register input device\n");
- + input_free_device(ts->input);
- + return -EIO;
- + }
- +
- + return 0;
- +}
- +
- +/* TODO add device tree support */
- +static int __devinit mx25_ts_probe_dt(struct imx_ts_device *ts,
- + struct platform_device *pdev)
- +{
- + return -ENODEV;
- +}
- +
- +static int __devinit mx25_ts_probe_pdata(struct imx_ts_device *ts,
- + struct platform_device *pdev)
- +{
- + struct imx_ts_platform_data *pdata = pdev->dev.platform_data;
- + struct device *dev = &pdev->dev;
- +
- + if (pdata == NULL) {
- + dev_err(dev, "No platform data given. Cannot continue\n");
- + return -EINVAL;
- + }
- +
- + ts->pen_debounce = pdata->pen_debounce;
- + ts->repeat_wait = pdata->repeat_wait;
- + ts->settling_time = pdata->settling_time;
- + ts->sample_cnt = pdata->sample_cnt;
- + ts->pen_treshold = pdata->pen_treshold;
- + ts->in_sync = pdata->in_sync;
- +
- + if (ts->pen_treshold == 0)
- + ts->pen_treshold = 256;
- +
- + if (ts->sample_cnt == 0)
- + ts->sample_cnt = 1;
- +
- + return 0;
- +}
- +
- +static int __devinit imx_ts_probe(struct platform_device *pdev)
- +{
- + struct device *dev = &pdev->dev;
- + struct imx_ts_device *ts;
- + int rc;
- +
- + ts = kzalloc(sizeof(struct imx_ts_device), GFP_KERNEL);
- + if (ts == NULL) {
- + dev_err(dev, "failed to allocate imx_ts_device\n");
- + return -ENOMEM;
- + }
- +
- + ts->pdev = pdev;
- +
- + rc = mx25_ts_probe_dt(ts, pdev);
- + if (rc == -ENODEV)
- + mx25_ts_probe_pdata(ts, pdev);
- +
- + ts->imx_adc = imx_adc_register_ts(ts, mx25_tsc_irq);
- + if (IS_ERR(ts->imx_adc)) {
- + dev_err(dev, "Cannot conntect to ADC main driver\n");
- + rc = PTR_ERR(ts->imx_adc);
- + goto on_register_error;
- + }
- +
- + ts->regs = imx_adc_get_iomem(ts->imx_adc);
- +
- + INIT_WORK(&ts->work, mx25_ts_workqueue);
- +
- + imx_ts_init(ts);
- +
- + rc = imx_ts_register_input_dev(ts);
- + if (rc < 0)
- + goto on_input_dev_error;
- +
- + platform_set_drvdata(pdev, ts);
- +
- + return 0;
- +
- +on_input_dev_error:
- + imx_adc_put_iomem(ts->regs, ts->imx_adc);
- + imx_adc_unregister_ts(ts->imx_adc, ts);
- +on_register_error:
- + kfree(ts);
- + return rc;
- +}
- +
- +static void __devexit imx_ts_remove_input_dev(struct imx_ts_device *ts)
- +{
- + input_unregister_device(ts->input);
- + input_free_device(ts->input);
- +}
- +
- +static int __devexit imx_ts_remove(struct platform_device *pdev)
- +{
- + struct imx_ts_device *ts = platform_get_drvdata(pdev);
- +
- + cancel_work_sync(&ts->work);
- + /* disable all interrupts sources */
- + writel(0xffffffff, ts->regs + TCQMR);
- + writel(TCQCR_QSM_STOP | TCQCR_PD_MSK, ts->regs + TCQCR);
- +
- + imx_ts_remove_input_dev(ts);
- + imx_adc_put_iomem(ts->regs, ts->imx_adc);
- + imx_adc_unregister_ts(ts->imx_adc, ts);
- + kfree(ts);
- + platform_set_drvdata(pdev, NULL);
- +
- + return 0;
- +}
- +
- +static int __maybe_unused imx_ts_suspend(struct device *dev)
- +{
- + struct platform_device *pdev =
- + container_of(dev, struct platform_device, dev);
- + struct imx_ts_device *ts = platform_get_drvdata(pdev);
- +
- + return 0;
- +}
- +
- +static int __maybe_unused imx_ts_resume(struct device *dev)
- +{
- + struct platform_device *pdev =
- + container_of(dev, struct platform_device, dev);
- + struct imx_ts_device *ts = platform_get_drvdata(pdev);
- +
- + return 0;
- +}
- +
- +static const struct dev_pm_ops ts_pm_ops = {
- +#ifdef CONFIG_PM
- + .suspend = imx_ts_suspend,
- + .resume = imx_ts_resume,
- +#endif
- +};
- +
- +static struct platform_driver imx_ts_driver = {
- + .driver = {
- + .name = "mx25-ts",
- + .owner = THIS_MODULE,
- + .pm = &ts_pm_ops,
- + },
- + .probe = imx_ts_probe,
- + .remove = __devexit_p(imx_ts_remove),
- +};
- +
- +static int __init imx_tsc_init(void)
- +{
- + int ret;
- +
- + ret = platform_driver_register(&imx_ts_driver);
- + if (ret)
- + pr_err("%s: failed to add ts driver\n", __func__);
- + return ret;
- +}
- +
- +module_init(imx_tsc_init);
- diff --git a/arch/arm/plat-mxc/include/mach/adc.h b/arch/arm/plat-mxc/include/mach/adc.h
- new file mode 100644
- index 0000000..42e4899
- --- /dev/null
- +++ b/arch/arm/plat-mxc/include/mach/adc.h
- @@ -0,0 +1,47 @@
- +/*
- + * Copyright (C) 2011 Juergen Beisert, Pengutronix <kernel@pengutronix.de>
- + *
- + * This software is licensed under the terms of the GNU General Public
- + * License version 2, as published by the Free Software Foundation, and
- + * may be copied, distributed, and modified under those terms.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + */
- +
- +#ifndef __PLAT_MXC_MACH_ADC_H
- +# define __PLAT_MXC_MACH_ADC_H
- +
- +#include <linux/irqreturn.h>
- +
- +/* List of shared registers */
- +
- +#define TGSR 4 /* read only */
- +# define TGSR_TCQINT (1 << 0)
- +# define TGSR_GCQINT (1 << 1)
- +# define TGSR_SLPINT (1 << 2)
- +
- +#define TICR 8 /* FIXME only TS relevant? */
- +
- +/* List of shared register bits */
- +# define TGCR_PDBEN (1 << 24)
- +# define TGCR_PDEN (1 << 23)
- +# define TGCR_PEN_DEBT_MASK (0x7f << 25)
- +# define TGCR_SET_PEN_DEBT(x) ((x) << 25)
- +# define TGCR_HSYNCEN (1 << 6)
- +# define TGCR_HSYNCPOL (1 << 7)
- +
- +/* functions from the main module */
- +extern void __iomem *imx_adc_get_iomem(void*);
- +extern void imx_adc_put_iomem(void __iomem *, void*);
- +extern void *imx_adc_register_ts(void*, irqreturn_t (*)(void*));
- +extern int imx_adc_unregister_ts(void*, void*);
- +extern void *imx_adc_register_hwmon(void*, irqreturn_t (*)(void*));
- +extern int imx_adc_unregister_hwmon(void*, void*);
- +extern void imx_adc_ts_set_tgcr(void*, u32);
- +extern unsigned imx_adc_get_period(void*);
- +
- +#endif /* __PLAT_MXC_MACH_ADC_H */
- diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h
- index def9ba5..112bdd2 100644
- --- a/arch/arm/plat-mxc/include/mach/devices-common.h
- +++ b/arch/arm/plat-mxc/include/mach/devices-common.h
- @@ -108,6 +108,20 @@ struct platform_device *__init imx_add_imx_fb(
- const struct imx_imx_fb_data *data,
- const struct imx_fb_platform_data *pdata);
- +struct imx_adc_platform_data;
- +struct imx_ts_platform_data;
- +struct imx_hwmon_platform_data;
- +struct imx_imx_adc_data {
- + resource_size_t iobase;
- + resource_size_t iosize;
- + resource_size_t irq;
- +};
- +struct platform_device *__init imx_add_imx_adc(
- + const struct imx_imx_adc_data *data,
- + const struct imx_adc_platform_data *adc_pdata,
- + const struct imx_ts_platform_data *ts_pdata,
- + const struct imx_hwmon_platform_data *hwmon_pdata);
- +
- #include <mach/i2c.h>
- struct imx_imx_i2c_data {
- int id;
- diff --git a/arch/arm/plat-mxc/include/mach/imxfb.h b/arch/arm/plat-mxc/include/mach/imxfb.h
- index 9de8f06..6ca4a8b 100644
- --- a/arch/arm/plat-mxc/include/mach/imxfb.h
- +++ b/arch/arm/plat-mxc/include/mach/imxfb.h
- @@ -19,6 +19,7 @@
- #define PCR_BPIX_12 (4 << 25)
- #define PCR_BPIX_16 (5 << 25)
- #define PCR_BPIX_18 (6 << 25)
- +#define PCR_BPIX_24 (7 << 25)
- #define PCR_PIXPOL (1 << 24)
- #define PCR_FLMPOL (1 << 23)
- #define PCR_LPPOL (1 << 22)
- @@ -55,6 +56,7 @@ struct imx_fb_videomode {
- struct fb_videomode mode;
- u32 pcr;
- unsigned char bpp;
- + unsigned height, width;
- };
- struct imx_fb_platform_data {
- diff --git a/arch/arm/plat-mxc/include/mach/iomux-mx25.h b/arch/arm/plat-mxc/include/mach/iomux-mx25.h
- index f0726d4..04f5788 100644
- --- a/arch/arm/plat-mxc/include/mach/iomux-mx25.h
- +++ b/arch/arm/plat-mxc/include/mach/iomux-mx25.h
- @@ -151,20 +151,25 @@
- #define MX25_PAD_D13__GPIO_4_7 IOMUX_PAD(0x288, 0x090, 0x05, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D12__D12 IOMUX_PAD(0x28c, 0x094, 0x00, 0, 0, NO_PAD_CTRL)
- +#define MX25_PAD_D12__LD19 IOMUX_PAD(0x28c, 0x094, 0x01, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D12__GPIO_4_8 IOMUX_PAD(0x28c, 0x094, 0x05, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D11__D11 IOMUX_PAD(0x290, 0x098, 0x00, 0, 0, NO_PAD_CTRL)
- +#define MX25_PAD_D11__LD20 IOMUX_PAD(0x290, 0x098, 0x01, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D11__GPIO_4_9 IOMUX_PAD(0x290, 0x098, 0x05, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D10__D10 IOMUX_PAD(0x294, 0x09c, 0x00, 0, 0, NO_PAD_CTRL)
- +#define MX25_PAD_D10__LD21 IOMUX_PAD(0x294, 0x09c, 0x01, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D10__GPIO_4_10 IOMUX_PAD(0x294, 0x09c, 0x05, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D10__USBOTG_OC IOMUX_PAD(0x294, 0x09c, 0x06, 0x57c, 0, PAD_CTL_PUS_100K_UP)
- #define MX25_PAD_D9__D9 IOMUX_PAD(0x298, 0x0a0, 0x00, 0, 0, NO_PAD_CTRL)
- +#define MX25_PAD_D9__LD22 IOMUX_PAD(0x298, 0x0a0, 0x01, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D9__GPIO_4_11 IOMUX_PAD(0x298, 0x0a0, 0x05, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D9__USBH2_PWR IOMUX_PAD(0x298, 0x0a0, 0x06, 0, 0, PAD_CTL_PKE)
- #define MX25_PAD_D8__D8 IOMUX_PAD(0x29c, 0x0a4, 0x00, 0, 0, NO_PAD_CTRL)
- +#define MX25_PAD_D8__LD23 IOMUX_PAD(0x29c, 0x0a4, 0x01, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D8__GPIO_4_12 IOMUX_PAD(0x29c, 0x0a4, 0x05, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_D8__USBH2_OC IOMUX_PAD(0x29c, 0x0a4, 0x06, 0x580, 0, PAD_CTL_PUS_100K_UP)
- @@ -468,14 +473,14 @@
- #define MX25_PAD_GPIO_C__CAN2_TX IOMUX_PAD(0x3f8, 0x1fc, 0x16, 0, 0, PAD_CTL_PUS_22K_UP)
- #define MX25_PAD_GPIO_D__GPIO_D IOMUX_PAD(0x3fc, 0x200, 0x10, 0, 0, NO_PAD_CTRL)
- -#define MX25_PAD_GPIO_E__LD16 IOMUX_PAD(0x400, 0x204, 0x02, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_GPIO_D__CAN2_RX IOMUX_PAD(0x3fc, 0x200, 0x16, 0x484, 1, PAD_CTL_PUS_22K_UP)
- #define MX25_PAD_GPIO_E__GPIO_E IOMUX_PAD(0x400, 0x204, 0x10, 0, 0, NO_PAD_CTRL)
- -#define MX25_PAD_GPIO_F__LD17 IOMUX_PAD(0x404, 0x208, 0x02, 0, 0, NO_PAD_CTRL)
- +#define MX25_PAD_GPIO_E__LD16 IOMUX_PAD(0x400, 0x204, 0x02, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_GPIO_E__AUD7_TXD IOMUX_PAD(0x400, 0x204, 0x14, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_GPIO_F__GPIO_F IOMUX_PAD(0x404, 0x208, 0x10, 0, 0, NO_PAD_CTRL)
- +#define MX25_PAD_GPIO_F__LD17 IOMUX_PAD(0x404, 0x208, 0x02, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_GPIO_F__AUD7_TXC IOMUX_PAD(0x404, 0x208, 0x14, 0, 0, NO_PAD_CTRL)
- #define MX25_PAD_EXT_ARMCLK__EXT_ARMCLK IOMUX_PAD(0x000, 0x20c, 0x10, 0, 0, NO_PAD_CTRL)
- diff --git a/arch/arm/plat-mxc/include/mach/mx25.h b/arch/arm/plat-mxc/include/mach/mx25.h
- index ccebf5b..d5f4ec8 100644
- --- a/arch/arm/plat-mxc/include/mach/mx25.h
- +++ b/arch/arm/plat-mxc/include/mach/mx25.h
- @@ -40,6 +40,7 @@
- #define MX25_FEC_BASE_ADDR 0x50038000
- #define MX25_SSI2_BASE_ADDR 0x50014000
- #define MX25_SSI1_BASE_ADDR 0x50034000
- +#define MX25_ADC_BASE_ADDR 0x50030000
- #define MX25_NFC_BASE_ADDR 0xbb000000
- #define MX25_IIM_BASE_ADDR 0x53ff0000
- #define MX25_DRYICE_BASE_ADDR 0x53ffc000
- @@ -92,6 +93,7 @@
- #define MX25_INT_CAN1 43
- #define MX25_INT_CAN2 44
- #define MX25_INT_UART1 45
- +#define MX25_INT_ADC 46
- #define MX25_INT_GPIO2 51
- #define MX25_INT_GPIO1 52
- #define MX25_INT_FEC 57
- diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
- index 7496d04..5685de9 100644
- --- a/drivers/video/backlight/pwm_bl.c
- +++ b/drivers/video/backlight/pwm_bl.c
- @@ -9,6 +9,7 @@
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- +#define DEBUG
- #include <linux/module.h>
- #include <linux/kernel.h>
- @@ -45,12 +46,13 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
- if (bl->props.fb_blank != FB_BLANK_UNBLANK)
- brightness = 0;
- + pr_debug("%s: backlight o%s\n", __func__, brightness != 0 ? "n" : "ff");
- +
- if (pb->notify)
- brightness = pb->notify(pb->dev, brightness);
- if (brightness == 0) {
- pwm_config(pb->pwm, 0, pb->period);
- - pwm_disable(pb->pwm);
- } else {
- brightness = pb->lth_brightness +
- (brightness * (pb->period - pb->lth_brightness) / max);
- @@ -89,6 +91,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
- struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
- struct backlight_device *bl;
- struct pwm_bl_data *pb;
- + struct device *parent;
- int ret;
- if (!data) {
- @@ -128,7 +131,8 @@ static int pwm_backlight_probe(struct platform_device *pdev)
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_RAW;
- props.max_brightness = data->max_brightness;
- - bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
- + parent = pdev->dev.parent ? pdev->dev.parent : &pdev->dev;
- + bl = backlight_device_register(dev_name(&pdev->dev), parent, pb,
- &pwm_backlight_ops, &props);
- if (IS_ERR(bl)) {
- dev_err(&pdev->dev, "failed to register backlight\n");
- @@ -137,7 +141,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
- }
- bl->props.brightness = data->dft_brightness;
- - backlight_update_status(bl);
- +// backlight_update_status(bl);
- platform_set_drvdata(pdev, bl);
- return 0;
- diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
- index f135dbe..2ca737f 100644
- --- a/drivers/video/imxfb.c
- +++ b/drivers/video/imxfb.c
- @@ -31,6 +31,8 @@
- #include <linux/dma-mapping.h>
- #include <linux/io.h>
- #include <linux/math64.h>
- +#include <linux/backlight.h>
- +#include <linux/lcd.h>
- #include <mach/imxfb.h>
- #include <mach/hardware.h>
- @@ -156,14 +158,20 @@ struct imxfb_info {
- cmap_static:1,
- unused:30;
- + unsigned line_length;
- +
- struct imx_fb_videomode *mode;
- int num_modes;
- + void (*lcd_power)(int);
- + void (*backlight_power)(int);
- +
- #ifdef PWMR_BACKLIGHT_AVAILABLE
- struct backlight_device *bl;
- #endif
- -
- - void (*lcd_power)(int);
- - void (*backlight_power)(int);
- +#ifdef CONFIG_LCD_CLASS_DEVICE
- + struct lcd_device *lcd;
- + int lcd_power_state;
- +#endif
- };
- #define IMX_NAME "IMX"
- @@ -174,6 +182,14 @@ struct imxfb_info {
- #define MIN_XRES 64
- #define MIN_YRES 64
- +/* memory to display mapping is 0123 -> 2301 */
- +static struct imxfb_rgb def_rgb_24 = {
- + .blue = {.offset = 0, .length = 8,},
- + .transp = {.offset = 8, .length = 0,},
- + .red = {.offset = 16, .length = 8,},
- + .green = {.offset = 24, .length = 8,},
- +};
- +
- /* Actually this really is 18bit support, the lowest 2 bits of each colour
- * are unused in hardware. We claim to have 24bit support to make software
- * like X work, which does not support 18bit.
- @@ -300,6 +316,47 @@ static const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi)
- return NULL;
- }
- +static u32 imxfb_calc_dclk(struct imxfb_info *fbi, struct fb_var_screeninfo *var)
- +{
- + unsigned input_clk = clk_get_rate(fbi->clk); /* in [Hz] */
- + unsigned dclk = PICOS2KHZ(var->pixclock) * 1000; /* from [psec] to [Hz] */
- + u32 pcr;
- +
- + pr_debug("%s: calculating pixel clock from %u Hz main clock\n",
- + __func__, input_clk);
- + pr_debug("%s: target pixel clock should be %u Hz\n",
- + __func__, dclk);
- +
- + pcr = DIV_ROUND_UP(input_clk, dclk);
- +
- + if (pcr > 1)
- + pcr--;
- + else {
- + pr_debug("%s: must limit the pixel clock to %u Hz\n",
- + __func__, input_clk / 2);
- + pcr = 1; /* '1' means '/2' */
- + }
- + /*
- + * TODO: output clock must be less than one third of HCLK for TFT mode
- + * and one fourth of HCLK for CSTN mode
- + */
- + pr_debug("%s: clock divider is now %u\n", __func__, pcr + 1);
- +
- + if (pcr > 0x3F) {
- + pcr = 0x3F;
- + pr_warn("Must extend the pixel clock to %u Hz\n",
- + input_clk / (pcr + 1));
- + }
- +
- + pr_debug("%s: resulting pixel clock frequency is: %u Hz\n",
- + __func__, input_clk / (pcr + 1));
- +
- + /* the real pixel clock frequency for this mode */
- + var->pixclock = KHZ2PICOS((input_clk / (pcr + 1)) / 1000);
- +
- + return pcr;
- +}
- +
- /*
- * imxfb_check_var():
- * Round up in the following order: bits_per_pixel, xres,
- @@ -311,9 +368,7 @@ static int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
- struct imxfb_info *fbi = info->par;
- struct imxfb_rgb *rgb;
- const struct imx_fb_videomode *imxfb_mode;
- - unsigned long lcd_clk;
- - unsigned long long tmp;
- - u32 pcr = 0;
- + u32 pcr;
- if (var->xres < MIN_XRES)
- var->xres = MIN_XRES;
- @@ -337,33 +392,30 @@ static int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
- var->sync = imxfb_mode->mode.sync;
- var->xres_virtual = max(var->xres_virtual, var->xres);
- var->yres_virtual = max(var->yres_virtual, var->yres);
- + var->width = imxfb_mode->width ? imxfb_mode->width : -1;
- + var->height = imxfb_mode->height ? imxfb_mode->height : -1;
- pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel);
- - lcd_clk = clk_get_rate(fbi->clk);
- -
- - tmp = var->pixclock * (unsigned long long)lcd_clk;
- -
- - do_div(tmp, 1000000);
- -
- - if (do_div(tmp, 1000000) > 500000)
- - tmp++;
- -
- - pcr = (unsigned int)tmp;
- -
- - if (--pcr > 0x3F) {
- - pcr = 0x3F;
- - printk(KERN_WARNING "Must limit pixel clock to %luHz\n",
- - lcd_clk / pcr);
- - }
- + pcr = imxfb_calc_dclk(fbi, var);
- switch (var->bits_per_pixel) {
- case 32:
- - pcr |= PCR_BPIX_18;
- + case 24:
- + pr_debug("%s establishing a %u bpp mode\n", __func__, var->bits_per_pixel);
- + pcr |= PCR_BPIX_24 | PCR_END_BYTE_SWAP;
- rgb = &def_rgb_18;
- + var->bits_per_pixel = 32;
- + break;
- + case 18:
- + pr_debug("%s establishing a 18 bpp mode\n", __func__);
- + pcr |= PCR_BPIX_18 | PCR_END_SEL; /* Microsoft BGR */
- + rgb = &def_rgb_18;
- + var->bits_per_pixel = 32;
- break;
- - case 16:
- default:
- + var->bits_per_pixel = 16;
- + case 16:
- if (cpu_is_mx1())
- pcr |= PCR_BPIX_12;
- else
- @@ -427,10 +479,9 @@ static int imxfb_set_par(struct fb_info *info)
- info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
- }
- - info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
- fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
- -
- imxfb_activate_var(var, info);
- + info->fix.line_length = fbi->line_length;
- return 0;
- }
- @@ -453,8 +504,17 @@ static int imxfb_bl_update_status(struct backlight_device *bl)
- if (bl->props.fb_blank != FB_BLANK_UNBLANK)
- brightness = 0;
- + pr_debug("%s called with '%d'\n", __func__, brightness);
- +
- fbi->pwmr = (fbi->pwmr & ~0xFF) | brightness;
- + if (fbi->backlight_power) {
- + if (brightness)
- + fbi->backlight_power(true);
- + else
- + fbi->backlight_power(false);
- + }
- +
- if (bl->props.fb_blank != FB_BLANK_UNBLANK)
- clk_enable(fbi->clk);
- writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
- @@ -503,10 +563,57 @@ static void imxfb_exit_backlight(struct imxfb_info *fbi)
- }
- #endif
- -static void imxfb_enable_controller(struct imxfb_info *fbi)
- +#ifdef CONFIG_LCD_CLASS_DEVICE
- +
- +static inline struct imxfb_info *to_our_lcd(struct lcd_device *lcd)
- +{
- + return lcd_get_data(lcd);
- +}
- +
- +static int imxfb_lcd_get_power(struct lcd_device *lcd)
- +{
- + struct imxfb_info *fbi = to_our_lcd(lcd);
- +
- + return fbi->lcd_power_state;
- +}
- +
- +static int imxfb_lcd_set_power(struct lcd_device *lcd, int power)
- +{
- + struct imxfb_info *fbi = to_our_lcd(lcd);
- + int lcd_power = 1;
- +
- + if (power == FB_BLANK_POWERDOWN)
- + lcd_power = 0;
- +
- + fbi->lcd_power(lcd_power);
- + fbi->lcd_power_state = power;
- +
- + return 0;
- +}
- +
- +static struct lcd_ops imxfb_lcd_ops = {
- + .get_power = imxfb_lcd_get_power,
- + .set_power = imxfb_lcd_set_power,
- +};
- +
- +static void imxfb_init_lcd(struct imxfb_info *fbi)
- {
- - pr_debug("Enabling LCD controller\n");
- + if (fbi->lcd)
- + return;
- +
- + fbi->lcd = lcd_device_register("imxfb-lcd", &fbi->pdev->dev, fbi,
- + &imxfb_lcd_ops);
- +}
- +
- +static void imxfb_exit_lcd(struct imxfb_info *fbi)
- +{
- + if (fbi->lcd)
- + lcd_device_unregister(fbi->lcd);
- +}
- +#endif
- +static void imxfb_enable_controller(struct imxfb_info *fbi)
- +{
- writel(fbi->screen_dma, fbi->regs + LCDC_SSA);
- /* panning offset 0 (0 pixel offset) */
- @@ -523,22 +630,10 @@ static void imxfb_enable_controller(struct imxfb_info *fbi)
- writel(RMCR_LCDC_EN_MX1, fbi->regs + LCDC_RMCR);
- clk_enable(fbi->clk);
- -
- - if (fbi->backlight_power)
- - fbi->backlight_power(1);
- - if (fbi->lcd_power)
- - fbi->lcd_power(1);
- }
- static void imxfb_disable_controller(struct imxfb_info *fbi)
- {
- - pr_debug("Disabling LCD controller\n");
- -
- - if (fbi->backlight_power)
- - fbi->backlight_power(0);
- - if (fbi->lcd_power)
- - fbi->lcd_power(0);
- -
- clk_disable(fbi->clk);
- writel(0, fbi->regs + LCDC_RMCR);
- @@ -548,17 +643,17 @@ static int imxfb_blank(int blank, struct fb_info *info)
- {
- struct imxfb_info *fbi = info->par;
- - pr_debug("imxfb_blank: blank=%d\n", blank);
- -
- switch (blank) {
- case FB_BLANK_POWERDOWN:
- case FB_BLANK_VSYNC_SUSPEND:
- case FB_BLANK_HSYNC_SUSPEND:
- case FB_BLANK_NORMAL:
- + pr_debug("%s: LCDC off\n", __func__);
- imxfb_disable_controller(fbi);
- break;
- case FB_BLANK_UNBLANK:
- + pr_debug("%s: LCDC on\n", __func__);
- imxfb_enable_controller(fbi);
- break;
- }
- @@ -619,9 +714,10 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
- info->fix.id, var->lower_margin);
- #endif
- - /* physical screen start address */
- - writel(VPW_VPW(var->xres * var->bits_per_pixel / 8 / 4),
- - fbi->regs + LCDC_VPW);
- + /* physical line length in bytes */
- + fbi->line_length = var->xres * var->bits_per_pixel / 8;
- + /* and the controller wants this value in 32 bit words */
- + writel(VPW_VPW(fbi->line_length >> 2), fbi->regs + LCDC_VPW);
- writel(HCR_H_WIDTH(var->hsync_len - 1) |
- HCR_H_WAIT_1(var->right_margin - 1) |
- @@ -682,8 +778,6 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
- struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
- struct fb_info *info = dev_get_drvdata(&pdev->dev);
- struct imxfb_info *fbi = info->par;
- - struct imx_fb_videomode *m;
- - int i;
- pr_debug("%s\n",__func__);
- @@ -704,8 +798,6 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
- info->var.nonstd = 0;
- info->var.activate = FB_ACTIVATE_NOW;
- - info->var.height = -1;
- - info->var.width = -1;
- info->var.accel_flags = 0;
- info->var.vmode = FB_VMODE_NONINTERLACED;
- @@ -721,13 +813,49 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
- fbi->lcd_power = pdata->lcd_power;
- fbi->backlight_power = pdata->backlight_power;
- - for (i = 0, m = &pdata->mode[0]; i < pdata->num_modes; i++, m++)
- - info->fix.smem_len = max_t(size_t, info->fix.smem_len,
- - m->mode.xres * m->mode.yres * m->bpp / 8);
- -
- return 0;
- }
- +/*
- + * calculate the minimal required framebuffer size and honor
- + * some physical contraints
- + */
- +static unsigned __init imxfb_calc_fbuffer_size(struct platform_device *pdev)
- +{
- + struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
- + struct imx_fb_videomode *m;
- + size_t max_size = 0;
- + int i;
- + unsigned bpp;
- +
- + for (i = 0, m = &pdata->mode[0]; i < pdata->num_modes; i++, m++) {
- + switch (m->bpp) {
- + case 2:
- + case 4:
- + case 8:
- + bpp = 8;
- + break;
- + case 12:
- + case 15:
- + case 16:
- + bpp = 16;
- + break;
- + case 18:
- + case 24:
- + case 32:
- + bpp = 32;
- + break;
- + default:
- + dev_err(&pdev->dev,"%u is an unsupported bit per pixel value\n", m->bpp);
- + return 0;
- + }
- + max_size = max_t(size_t, max_size, m->mode.xres * m->mode.yres * bpp / 8);
- + }
- +
- + dev_dbg(&pdev->dev,"need %u kiB of memory for the framebuffer\n", max_size);
- + return max_size;
- +}
- +
- static int __init imxfb_probe(struct platform_device *pdev)
- {
- struct imxfb_info *fbi;
- @@ -784,6 +912,7 @@ static int __init imxfb_probe(struct platform_device *pdev)
- }
- if (!pdata->fixed_screen_cpu) {
- + info->fix.smem_len = imxfb_calc_fbuffer_size(pdev);
- fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
- fbi->map_cpu = dma_alloc_writecombine(&pdev->dev,
- fbi->map_size, &fbi->map_dma, GFP_KERNEL);
- @@ -806,6 +935,8 @@ static int __init imxfb_probe(struct platform_device *pdev)
- fbi->screen_cpu = fbi->map_cpu;
- fbi->screen_dma = fbi->map_dma;
- info->fix.smem_start = fbi->screen_dma;
- + /* TODO this is a stupid solution! We need the available memory from the platform! */
- + info->fix.smem_len = imxfb_calc_fbuffer_size(pdev);
- }
- if (pdata->init) {
- @@ -827,23 +958,36 @@ static int __init imxfb_probe(struct platform_device *pdev)
- */
- imxfb_check_var(&info->var, info);
- - ret = fb_alloc_cmap(&info->cmap, 1 << info->var.bits_per_pixel, 0);
- - if (ret < 0)
- + imxfb_set_par(info);
- +
- + ret = fb_alloc_cmap(&info->cmap, fbi->palette_size, 0);
- + if (ret < 0) {
- + dev_err(&pdev->dev, "cannot allocate a cmap\n");
- goto failed_cmap;
- + }
- - imxfb_set_par(info);
- ret = register_framebuffer(info);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register framebuffer\n");
- goto failed_register;
- }
- - imxfb_enable_controller(fbi);
- fbi->pdev = pdev;
- +
- #ifdef PWMR_BACKLIGHT_AVAILABLE
- - imxfb_init_backlight(fbi);
- + if (fbi->backlight_power)
- + imxfb_init_backlight(fbi);
- #endif
- -
- +#ifdef CONFIG_LCD_CLASS_DEVICE
- + if (fbi->lcd_power)
- + imxfb_init_lcd(fbi);
- +#endif
- + /*
- + * enable the whole chain of LCD, Backlight and LCD controller
- + * Note: the order the members of the chain are called depends on their
- + * registration!
- + */
- + fb_blank(info, 0);
- return 0;
- failed_register:
- @@ -880,6 +1024,9 @@ static int __devexit imxfb_remove(struct platform_device *pdev)
- imxfb_disable_controller(fbi);
- +#ifdef CONFIG_LCD_CLASS_DEVICE
- + imxfb_exit_lcd(fbi);
- +#endif
- #ifdef PWMR_BACKLIGHT_AVAILABLE
- imxfb_exit_backlight(fbi);
- #endif
- diff --git a/include/linux/platform_data/imx-adc.h b/include/linux/platform_data/imx-adc.h
- new file mode 100644
- index 0000000..63aabbf
- --- /dev/null
- +++ b/include/linux/platform_data/imx-adc.h
- @@ -0,0 +1,54 @@
- +/*
- + * Copyright (C) 2011 Juergen Beisert, Pengutronix <kernel@pengutronix.de>
- + *
- + * This software is licensed under the terms of the GNU General Public
- + * License version 2, as published by the Free Software Foundation, and
- + * may be copied, distributed, and modified under those terms.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + */
- +
- +#ifndef __PLATFORM_DATA_IMX_ADC_H
- +# define __PLATFORM_DATA_IMX_ADC_H
- +
- +/**
- + * struct imx_adc_platform_data
- + * @adc_freq; frequency in [Hz] the ADC should run (max 1.75 MHz)
- + *
- + * Describes the physical requirements to run the ADC unit.
- + */
- +struct imx_adc_platform_data {
- + unsigned long adc_freq;
- +};
- +
- +/**
- + * struct imx_ts_platform_data
- + * @pen_debounce: time [ns] the pen signal needs to settle (touchscreen dependent)
- + * @pen_treshold: minimum ADC value to detect a pressure as valid
- + * @repeat_wait: time [ns] to wait between a repeated conversion
- + * @settling_time: time [ns] the electrical signals must settle prior measurement
- + * @sample_cnt: sample count per direction per measurement
- + * @in_sync: 0: disabled, 1 in sync with a high active HSYNC, -1 low active HSYNC
- + *
- + * Describes the physical requirements for a specific touchscreen.
- + */
- +struct imx_ts_platform_data {
- + unsigned pen_debounce;
- + unsigned pen_treshold;
- + unsigned repeat_wait;
- + unsigned settling_time;
- + unsigned sample_cnt;
- + int in_sync;
- +# define MEAS_IF_LOW_HSYNC -1
- +# define MEAS_IF_HIG_HSYNC 1
- +};
- +
- +struct imx_hwmon_platform_data {
- + unsigned dummy;
- +};
- +
- +#endif /* __ASM_ARCH_MSM_SERIAL_HS_H */
- --
- 1.7.4.1
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement