cregit-Linux how code gets into the kernel

Release 4.11 drivers/input/touchscreen/wm831x-ts.c

/*
 * Touchscreen driver for WM831x PMICs
 *
 * Copyright 2011 Wolfson Microelectronics plc.
 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/pm.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/irq.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>

/*
 * R16424 (0x4028) - Touch Control 1
 */

#define WM831X_TCH_ENA                          0x8000  
/* TCH_ENA */

#define WM831X_TCH_CVT_ENA                      0x4000  
/* TCH_CVT_ENA */

#define WM831X_TCH_SLPENA                       0x1000  
/* TCH_SLPENA */

#define WM831X_TCH_Z_ENA                        0x0400  
/* TCH_Z_ENA */

#define WM831X_TCH_Y_ENA                        0x0200  
/* TCH_Y_ENA */

#define WM831X_TCH_X_ENA                        0x0100  
/* TCH_X_ENA */

#define WM831X_TCH_DELAY_MASK                   0x00E0  
/* TCH_DELAY - [7:5] */

#define WM831X_TCH_DELAY_SHIFT                       5  
/* TCH_DELAY - [7:5] */

#define WM831X_TCH_DELAY_WIDTH                       3  
/* TCH_DELAY - [7:5] */

#define WM831X_TCH_RATE_MASK                    0x001F  
/* TCH_RATE - [4:0] */

#define WM831X_TCH_RATE_SHIFT                        0  
/* TCH_RATE - [4:0] */

#define WM831X_TCH_RATE_WIDTH                        5  
/* TCH_RATE - [4:0] */

/*
 * R16425 (0x4029) - Touch Control 2
 */

#define WM831X_TCH_PD_WK                        0x2000  
/* TCH_PD_WK */

#define WM831X_TCH_5WIRE                        0x1000  
/* TCH_5WIRE */

#define WM831X_TCH_PDONLY                       0x0800  
/* TCH_PDONLY */

#define WM831X_TCH_ISEL                         0x0100  
/* TCH_ISEL */

#define WM831X_TCH_RPU_MASK                     0x000F  
/* TCH_RPU - [3:0] */

#define WM831X_TCH_RPU_SHIFT                         0  
/* TCH_RPU - [3:0] */

#define WM831X_TCH_RPU_WIDTH                         4  
/* TCH_RPU - [3:0] */

/*
 * R16426-8 (0x402A-C) - Touch Data X/Y/X
 */

#define WM831X_TCH_PD                           0x8000  
/* TCH_PD1 */

#define WM831X_TCH_DATA_MASK                    0x0FFF  
/* TCH_DATA - [11:0] */

#define WM831X_TCH_DATA_SHIFT                        0  
/* TCH_DATA - [11:0] */

#define WM831X_TCH_DATA_WIDTH                       12  
/* TCH_DATA - [11:0] */


struct wm831x_ts {
	
struct input_dev *input_dev;
	
struct wm831x *wm831x;
	
unsigned int data_irq;
	
unsigned int pd_irq;
	
bool pressure;
	
bool pen_down;
	
struct work_struct pd_data_work;
};


static void wm831x_pd_data_work(struct work_struct *work) { struct wm831x_ts *wm831x_ts = container_of(work, struct wm831x_ts, pd_data_work); if (wm831x_ts->pen_down) { enable_irq(wm831x_ts->data_irq); dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n"); } else { enable_irq(wm831x_ts->pd_irq); dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n"); } }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown73100.00%1100.00%
Total73100.00%1100.00%


static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) { struct wm831x_ts *wm831x_ts = irq_data; struct wm831x *wm831x = wm831x_ts->wm831x; static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE }; u16 data[3]; int count; int i, ret; if (wm831x_ts->pressure) count = 3; else count = 2; wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data); if (ret != 0) { dev_err(wm831x->dev, "Failed to read touch data: %d\n", ret); return IRQ_NONE; } /* * We get a pen down reading on every reading, report pen up if any * individual reading does so. */ wm831x_ts->pen_down = true; for (i = 0; i < count; i++) { if (!(data[i] & WM831X_TCH_PD)) { wm831x_ts->pen_down = false; continue; } input_report_abs(wm831x_ts->input_dev, data_types[i], data[i] & WM831X_TCH_DATA_MASK); } if (!wm831x_ts->pen_down) { /* Switch from data to pen down */ dev_dbg(wm831x->dev, "IRQ DATA->PD\n"); disable_irq_nosync(wm831x_ts->data_irq); /* Don't need data any more */ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0); /* Flush any final samples that arrived while reading */ wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data); if (wm831x_ts->pressure) input_report_abs(wm831x_ts->input_dev, ABS_PRESSURE, 0); input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0); schedule_work(&wm831x_ts->pd_data_work); } else { input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1); } input_sync(wm831x_ts->input_dev); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown304100.00%4100.00%
Total304100.00%4100.00%


static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data) { struct wm831x_ts *wm831x_ts = irq_data; struct wm831x *wm831x = wm831x_ts->wm831x; int ena = 0; if (wm831x_ts->pen_down) return IRQ_HANDLED; disable_irq_nosync(wm831x_ts->pd_irq); /* Start collecting data */ if (wm831x_ts->pressure) ena |= WM831X_TCH_Z_ENA; wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena); wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, WM831X_TCHPD_EINT, WM831X_TCHPD_EINT); wm831x_ts->pen_down = true; /* Switch from pen down to data */ dev_dbg(wm831x->dev, "IRQ PD->DATA\n"); schedule_work(&wm831x_ts->pd_data_work); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown118100.00%3100.00%
Total118100.00%3100.00%


static int wm831x_ts_input_open(struct input_dev *idev) { struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); struct wm831x *wm831x = wm831x_ts->wm831x; wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, WM831X_TCH_ENA | WM831X_TCH_CVT_ENA | WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, WM831X_TCH_ENA); wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown63100.00%2100.00%
Total63100.00%2100.00%


static void wm831x_ts_input_close(struct input_dev *idev) { struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); struct wm831x *wm831x = wm831x_ts->wm831x; /* Shut the controller down, disabling all other functionality too */ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, WM831X_TCH_ENA | WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0); /* Make sure any pending IRQs are done, the above will prevent * new ones firing. */ synchronize_irq(wm831x_ts->data_irq); synchronize_irq(wm831x_ts->pd_irq); /* Make sure the IRQ completion work is quiesced */ flush_work(&wm831x_ts->pd_data_work); /* If we ended up with the pen down then make sure we revert back * to pen detection state for the next time we start up. */ if (wm831x_ts->pen_down) { disable_irq(wm831x_ts->data_irq); enable_irq(wm831x_ts->pd_irq); wm831x_ts->pen_down = false; } }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown10099.01%266.67%
Tejun Heo10.99%133.33%
Total101100.00%3100.00%


static int wm831x_ts_probe(struct platform_device *pdev) { struct wm831x_ts *wm831x_ts; struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent); struct wm831x_touch_pdata *pdata = NULL; struct input_dev *input_dev; int error, irqf; if (core_pdata) pdata = core_pdata->touch; wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts), GFP_KERNEL); input_dev = devm_input_allocate_device(&pdev->dev); if (!wm831x_ts || !input_dev) { error = -ENOMEM; goto err_alloc; } wm831x_ts->wm831x = wm831x; wm831x_ts->input_dev = input_dev; INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work); /* * If we have a direct IRQ use it, otherwise use the interrupt * from the WM831x IRQ controller. */ wm831x_ts->data_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "TCHDATA")); if (pdata && pdata->data_irq) wm831x_ts->data_irq = pdata->data_irq; wm831x_ts->pd_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "TCHPD")); if (pdata && pdata->pd_irq) wm831x_ts->pd_irq = pdata->pd_irq; if (pdata) wm831x_ts->pressure = pdata->pressure; else wm831x_ts->pressure = true; /* Five wire touchscreens can't report pressure */ if (pdata && pdata->fivewire) { wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, WM831X_TCH_5WIRE, WM831X_TCH_5WIRE); /* Pressure measurements are not possible for five wire mode */ WARN_ON(pdata->pressure && pdata->fivewire); wm831x_ts->pressure = false; } else { wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, WM831X_TCH_5WIRE, 0); } if (pdata) { switch (pdata->isel) { default: dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n", pdata->isel); /* Fall through */ case 200: case 0: wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, WM831X_TCH_ISEL, 0); break; case 400: wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, WM831X_TCH_ISEL, WM831X_TCH_ISEL); break; } } wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, WM831X_TCH_PDONLY, 0); /* Default to 96 samples/sec */ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, WM831X_TCH_RATE_MASK, 6); if (pdata && pdata->data_irqf) irqf = pdata->data_irqf; else irqf = IRQF_TRIGGER_HIGH; error = request_threaded_irq(wm831x_ts->data_irq, NULL, wm831x_ts_data_irq, irqf | IRQF_ONESHOT, "Touchscreen data", wm831x_ts); if (error) { dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n", wm831x_ts->data_irq, error); goto err_alloc; } disable_irq(wm831x_ts->data_irq); if (pdata && pdata->pd_irqf) irqf = pdata->pd_irqf; else irqf = IRQF_TRIGGER_HIGH; error = request_threaded_irq(wm831x_ts->pd_irq, NULL, wm831x_ts_pen_down_irq, irqf | IRQF_ONESHOT, "Touchscreen pen down", wm831x_ts); if (error) { dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n", wm831x_ts->pd_irq, error); goto err_data_irq; } /* set up touch configuration */ input_dev->name = "WM831x touchscreen"; input_dev->phys = "wm831x"; input_dev->open = wm831x_ts_input_open; input_dev->close = wm831x_ts_input_close; __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0); input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0); if (wm831x_ts->pressure) input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0); input_set_drvdata(input_dev, wm831x_ts); input_dev->dev.parent = &pdev->dev; error = input_register_device(input_dev); if (error) goto err_pd_irq; platform_set_drvdata(pdev, wm831x_ts); return 0; err_pd_irq: free_irq(wm831x_ts->pd_irq, wm831x_ts); err_data_irq: free_irq(wm831x_ts->data_irq, wm831x_ts); err_alloc: return error; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown672100.00%8100.00%
Total672100.00%8100.00%


static int wm831x_ts_remove(struct platform_device *pdev) { struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev); free_irq(wm831x_ts->pd_irq, wm831x_ts); free_irq(wm831x_ts->data_irq, wm831x_ts); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown42100.00%1100.00%
Total42100.00%1100.00%

static struct platform_driver wm831x_ts_driver = { .driver = { .name = "wm831x-touch", }, .probe = wm831x_ts_probe, .remove = wm831x_ts_remove, }; module_platform_driver(wm831x_ts_driver); /* Module information */ MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_DESCRIPTION("WM831x PMIC touchscreen driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:wm831x-touch");

Overall Contributors

PersonTokensPropCommitsCommitProp
Mark Brown161899.88%1083.33%
JJ Ding10.06%18.33%
Tejun Heo10.06%18.33%
Total1620100.00%12100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.