1.dtsi配置
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/pinctrl-zynqmp.h>
/include/ "system-conf.dtsi"
/ {
model = "Zynq MpSoc Development Board";
chosen {
bootargs = "earlycon console=ttyPS0,115200 clk_ignore_unused root=/dev/mmcblk1p2 rw rootwait";
stdout-path = "serial0:115200n8";
};
keypl {
compatible = "alinx-keypl";
key-gpios = <&gpio 38 GPIO_PULL_UP>;
};
};
&amba_pl {
axidmatest_0: axidmatest@0 {
compatible ="xlnx,axi-dma-test-1.00.a";
dmas = <&axi_dma_0 0 &axi_dma_0 1>;
dma-names = "axidma0","axidma1";
};
};
/* SD */
&sdhci1 {
disable-wp;
no-1-8-v;
};
/* USB */
&dwc3_0 {
status = "okay";
dr_mode = "host";
};
&amba {
zyxclmm_drm {
compatible = "xlnx,zocl";
status = "okay";
};
};
&i2c0 {
status = "okay";
clock-frequency = <100000>;
xilinx_an071@5D {
compatible = "goodix,gt9xx";
reg = <0x5D>;
interrupt-parent = <&gic>;
interrupts = <0 91 4>;
};
};
&v_tc_0 {
compatible = "xlnx,bridge-v-tc-6.1";
xlnx,pixels-per-clock = <1>;
};
&axi_dynclk_0 {
compatible = "digilent,axi-dynclk";
#clock-cells = <0x0>;
linux,phandle = <0xc>;
phandle = <0xc>;
};
&amba {
drm-pl-disp-drv {
compatible = "xlnx,pl-disp";
dmas = <&axi_vdma_0 0>;
dma-names = "dma0";
xlnx,vformat = "RG24";
xlnx,bridge = <&v_tc_0>;
port@0 {
reg = <0>;
pl_disp_crtc: endpoint {
remote-endpoint = <&lcd_port>;
};
};
};
ax_lcd_encoder {
compatible = "ax-drm-encoder";
ports {
#address-cells = <1>;
#size-cells = <0>;
encoder_lcd_port: port@0 {
reg = <0>;
lcd_port: endpoint {
remote-endpoint = <&pl_disp_crtc>;
};
};
};
};
};
&gpio {
status = "okay";
};
2.驱动层
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#define SCREEN_MAX_X 800
#define SCREEN_MAX_Y 480
//***************************PART1:ON/OFF define*******************************
#define GTP_DEBUG_ON 1
#define GTP_DEBUG_FUNC_ON 0
struct goodix_ts_data {
spinlock_t irq_lock;
struct i2c_client *client;
struct input_dev *input_dev;
struct hrtimer timer;
struct work_struct work;
s32 irq_is_disable;
s32 use_irq;
u16 abs_x_max;
u16 abs_y_max;
u8 int_trigger_type;
u8 gtp_rawdiff_mode;
};
//*************************** PART2:TODO define **********************************
#define GTP_MAX_HEIGHT 800
#define GTP_MAX_WIDTH 480
#define GTP_INT_TRIGGER 0 // 0: Rising 1: Falling
#define GTP_MAX_TOUCH 1
//***************************PART3:OTHER define*********************************
#define GTP_DRIVER_VERSION "V2.4<2014/11/28>"
#define GTP_I2C_NAME "goodix,gt9xx"
#define GTP_POLL_TIME 10
#define GTP_ADDR_LENGTH 2
#define GTP_CONFIG_MIN_LENGTH 186
#define GTP_CONFIG_MAX_LENGTH 240
#define FAIL 0
#define SUCCESS 1
#define SWITCH_OFF 0
#define SWITCH_ON 1
// Registers define
#define GTP_READ_COOR_ADDR 0x814E
#define GTP_REG_SLEEP 0x8040
#define GTP_REG_SENSOR_ID 0x814A
#define GTP_REG_CONFIG_DATA 0x8047
#define GTP_REG_VERSION 0x8140
#define RESOLUTION_LOC 3
#define TRIGGER_LOC 8
// Log define
#define GTP_INFO(fmt,arg...) printk("<<-GTP-INFO->> "fmt"\n",##arg)
#define GTP_ERROR(fmt,arg...) printk("<<-GTP-ERROR->> "fmt"\n",##arg)
#define GTP_DEBUG(fmt,arg...) do{\
if(GTP_DEBUG_ON)\
printk("<<-GTP-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
}while(0)
#define GTP_DEBUG_FUNC() do{\
if(GTP_DEBUG_FUNC_ON)\
printk("<<-GTP-FUNC->> Func:%s@Line:%d\n",__func__,__LINE__);\
}while(0)
static const char *goodix_ts_name = "goodix-ts";
//static const char *goodix_input_phys = "input/ts";
static struct workqueue_struct *goodix_wq;
struct i2c_client * i2c_connect_client = NULL;
u8 config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH]
= {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
static s8 gtp_i2c_test(struct i2c_client *client);
struct edt_i2c_chip_data
{
int max_support_points;
};
s32 gtp_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
{
struct i2c_msg msgs[2];
s32 ret=-1;
s32 retries = 0;
GTP_DEBUG_FUNC();
msgs[0].flags = !I2C_M_RD;
msgs[0].addr = client->addr;
msgs[0].len = GTP_ADDR_LENGTH;
msgs[0].buf = &buf[0];
msgs[1].flags = I2C_M_RD;
msgs[1].addr = client->addr;
msgs[1].len = len - GTP_ADDR_LENGTH;
msgs[1].buf = &buf[GTP_ADDR_LENGTH];
while(retries < 5)
{
ret = i2c_transfer(client->adapter, msgs, 2);
if(ret == 2)break;
retries++;
}
if((retries >= 5))
{
GTP_ERROR("I2C Read: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((u16)(buf[0] << 8)) | buf[1]), len-2, ret);
}
return ret;
}
s32 gtp_i2c_write(struct i2c_client *client,u8 *buf,s32 len)
{
struct i2c_msg msg;
s32 ret = -1;
s32 retries = 0;
GTP_DEBUG_FUNC();
msg.flags = !I2C_M_RD;
msg.addr = client->addr;
msg.len = len;
msg.buf = buf;
//msg.scl_rate = 300 * 1000; // for Rockchip, etc
while(retries < 5)
{
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret == 1)break;
retries++;
}
if((retries >= 5))
{
GTP_ERROR("I2C Write: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((u16)(buf[0] << 8)) | buf[1]), len-2, ret);
}
return ret;
}
void gtp_irq_disable(struct goodix_ts_data *ts)
{
unsigned long irqflags;
GTP_DEBUG_FUNC();
spin_lock_irqsave(&ts->irq_lock, irqflags);
if (!ts->irq_is_disable)
{
ts->irq_is_disable = 1;
disable_irq_nosync(ts->client->irq);
}
spin_unlock_irqrestore(&ts->irq_lock, irqflags);
}
void gtp_irq_enable(struct goodix_ts_data *ts)
{
unsigned long irqflags = 0;
GTP_DEBUG_FUNC();
spin_lock_irqsave(&ts->irq_lock, irqflags);
if (ts->irq_is_disable)
{
enable_irq(ts->client->irq);
ts->irq_is_disable = 0;
}
spin_unlock_irqrestore(&ts->irq_lock, irqflags);
}
static void gtp_touch_down(struct goodix_ts_data* ts,s32 id,s32 x,s32 y,s32 w)
{
input_report_abs(ts->input_dev, ABS_X, x);
input_report_abs(ts->input_dev, ABS_Y, y);
input_report_abs(ts->input_dev, ABS_PRESSURE, w);
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, w);
input_report_key(ts->input_dev, BTN_TOUCH, 1);
input_mt_slot(ts->input_dev, id);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1);
GTP_DEBUG("touch_down ID:%d, X:%d, Y:%d, P:%d", id, x, y, w);
}
static void gtp_touch_up(struct goodix_ts_data* ts, s32 id,s32 x,s32 y,s32 w)
{
input_report_abs(ts->input_dev, ABS_X, x);
input_report_abs(ts->input_dev, ABS_Y, y);
input_report_abs(ts->input_dev, ABS_PRESSURE, 0);
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
input_report_key(ts->input_dev, BTN_TOUCH, 0);
input_mt_slot(ts->input_dev, id);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
GTP_DEBUG("touch_up ID:%d, X:%d, Y:%d, P:%d", id, x, y, w);
}
static void goodix_ts_work_func(struct work_struct *work)
{
u8 end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};
u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]={GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};
u8 touch_num = 0;
u8 finger = 0;
static u16 pre_touch = 0;
u8* coor_data = NULL;
s32 input_x = 0;
s32 input_y = 0;
s32 input_w = 0;
s32 id = 0;
s32 i = 0;
s32 ret = -1;
struct goodix_ts_data *ts = NULL;
GTP_DEBUG_FUNC();
ts = container_of(work, struct goodix_ts_data, work);
ret = gtp_i2c_read(ts->client, point_data, 12);
if (ret < 0)
{
GTP_ERROR("I2C transfer error. errno:%d\n ", ret);
if (ts->use_irq)
{
gtp_irq_enable(ts);
}
return;
}
finger = point_data[GTP_ADDR_LENGTH];
if (finger == 0x00)
{
if (ts->use_irq)
{
gtp_irq_enable(ts);
}
return;
}
if((finger & 0x80) == 0)
{
goto exit_work_func;
}
touch_num = finger & 0x0f;
if (touch_num > GTP_MAX_TOUCH)
{
goto exit_work_func;
}
if (touch_num > 1)
{
u8 buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff};
ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));
memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
}
if (touch_num == pre_touch)
{
for (i = 0; i < touch_num; i++)
{
coor_data = &point_data[i * 8 + 3];
id = coor_data[0] & 0x0F;
input_x = coor_data[1] | (coor_data[2] << 8);
input_y = coor_data[3] | (coor_data[4] << 8);
input_w = coor_data[5] | (coor_data[6] << 8);
gtp_touch_down(ts, id, input_x, input_y, input_w);
}
}
else if (pre_touch)
{
for (i = 0; i < pre_touch; i++)
{
coor_data = &point_data[i * 8 + 3];
id = coor_data[0] & 0x0F;
input_x = coor_data[1] | (coor_data[2] << 8);
input_y = coor_data[3] | (coor_data[4] << 8);
input_w = coor_data[5] | (coor_data[6] << 8);
gtp_touch_up(ts, id, input_x, input_y, input_w);
}
}
pre_touch = touch_num;
input_mt_report_pointer_emulation(ts->input_dev, 1);
input_sync(ts->input_dev);
exit_work_func:
if(!ts->gtp_rawdiff_mode)
{
ret = gtp_i2c_write(ts->client, end_cmd, 3);
if (ret < 0)
{
GTP_INFO("I2C write end_cmd error!");
}
}
if (ts->use_irq)
{
gtp_irq_enable(ts);
}
}
static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer)
{
struct goodix_ts_data *ts = container_of(timer, struct goodix_ts_data, timer);
GTP_DEBUG_FUNC();
queue_work(goodix_wq, &ts->work);
hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME+6)*1000000), HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
{
struct goodix_ts_data *ts = dev_id;
GTP_DEBUG_FUNC();
gtp_irq_disable(ts);
queue_work(goodix_wq, &ts->work);
return IRQ_HANDLED;
}
static s32 gtp_init_panel(struct goodix_ts_data *ts)
{
if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0))
{
ts->abs_x_max = (config[RESOLUTION_LOC + 1] << 8) + config[RESOLUTION_LOC];
ts->abs_y_max = (config[RESOLUTION_LOC + 3] << 8) + config[RESOLUTION_LOC + 2];
ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
}
GTP_INFO("X_MAX: %d, Y_MAX: %d, TRIGGER: 0x%02x", ts->abs_x_max,ts->abs_y_max,ts->int_trigger_type);
msleep(10);
return 0;
}
s32 gtp_read_version(struct i2c_client *client, u16* version)
{
s32 ret = -1;
u8 buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff};
GTP_DEBUG_FUNC();
ret = gtp_i2c_read(client, buf, sizeof(buf));
if (ret < 0)
{
GTP_ERROR("GTP read version failed");
return ret;
}
if (version)
{
*version = (buf[7] << 8) | buf[6];
}
if (buf[5] == 0x00)
{
GTP_INFO("IC Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]);
}
else
{
GTP_INFO("IC Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
}
return ret;
}
static s8 gtp_i2c_test(struct i2c_client *client)
{
u8 test[3] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
u8 retry = 0;
s8 ret = -1;
GTP_DEBUG_FUNC();
while(retry++ < 5)
{
ret = gtp_i2c_read(client, test, 3);
if (ret > 0)
{
return ret;
}
GTP_ERROR("GTP i2c test failed time %d.",retry);
msleep(10);
}
return ret;
}
static s8 gtp_request_irq(struct goodix_ts_data *ts)
{
s32 ret = -1;
unsigned long irq_flags;
GTP_DEBUG_FUNC();
GTP_DEBUG("INT trigger type:%x", ts->int_trigger_type);
irq_flags = irq_get_trigger_type(ts->client->irq);
if (irq_flags == IRQF_TRIGGER_NONE) irq_flags = IRQF_TRIGGER_HIGH;
irq_flags |= IRQF_ONESHOT;
ret = devm_request_threaded_irq(&ts->client->dev, ts->client->irq, NULL, goodix_ts_irq_handler, irq_flags, ts->client->name, ts);
if (ret)
{
GTP_ERROR("Request IRQ failed!ERRNO:%d.", ret);
hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ts->timer.function = goodix_ts_timer_handler;
hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
return -1;
}
else
{
gtp_irq_disable(ts);
ts->use_irq = 1;
return 0;
}
}
static s8 gtp_request_input_dev(struct goodix_ts_data *ts)
{
s8 ret = -1;
s32 error = 0;
GTP_DEBUG_FUNC();
ts->input_dev = input_allocate_device();
if (ts->input_dev == NULL)
{
GTP_ERROR("Failed to allocate input device.");
return -ENOMEM;
}
__set_bit(EV_ABS, ts->input_dev->evbit);
__set_bit(EV_KEY, ts->input_dev->evbit);
__set_bit(EV_SYN, ts->input_dev->evbit);
__set_bit(BTN_TOUCH, ts->input_dev->keybit);
input_set_abs_params(ts->input_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);
input_set_abs_params(ts->input_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);
input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 32, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, 32, 0, 0);
ts->input_dev->name = goodix_ts_name;
ts->input_dev->id.bustype = BUS_I2C;
ts->input_dev->dev.parent = &ts->client->dev;
error = input_mt_init_slots(ts->input_dev, GTP_MAX_TOUCH, INPUT_MT_DIRECT);
if (error) {
GTP_ERROR("Failed to initialize MT slots: %d", error);
return error;
}
ret = input_register_device(ts->input_dev);
if (ret)
{
GTP_ERROR("Register %s input device failed", ts->input_dev->name);
return -ENODEV;
}
return 0;
}
static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
s32 ret = -1;
struct goodix_ts_data *ts;
u16 version_info;
GTP_DEBUG_FUNC();
GTP_INFO("GTP Driver Version: %s", GTP_DRIVER_VERSION);
GTP_INFO("GTP I2C Address: 0x%02x", client->addr);
i2c_connect_client = client;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
{
GTP_ERROR("I2C check functionality failed.");
return -ENODEV;
}
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
if (ts == NULL)
{
GTP_ERROR("Alloc GFP_KERNEL memory failed.");
return -ENOMEM;
}
INIT_WORK(&ts->work, goodix_ts_work_func);
ts->client = client;
spin_lock_init(&ts->irq_lock);
i2c_set_clientdata(client, ts);
ts->gtp_rawdiff_mode = 0;
ret = gtp_i2c_test(client);
if (ret < 0)
{
GTP_ERROR("I2C communication ERROR!");
}
ret = gtp_read_version(client, &version_info);
if (ret < 0)
{
GTP_ERROR("Read version failed.");
}
ret = gtp_init_panel(ts);
if (ret < 0)
{
GTP_ERROR("GTP init panel failed.");
ts->abs_x_max = GTP_MAX_WIDTH;
ts->abs_y_max = GTP_MAX_HEIGHT;
ts->int_trigger_type = GTP_INT_TRIGGER;
}
ret = gtp_request_input_dev(ts);
if (ret < 0)
{
GTP_ERROR("GTP request input dev failed");
}
ret = gtp_request_irq(ts);
if (ret < 0)
{
GTP_INFO("GTP works in polling mode.");
}
else
{
GTP_INFO("GTP works in interrupt mode.");
}
if (ts->use_irq)
{
gtp_irq_enable(ts);
}
return 0;
}
static int goodix_ts_remove(struct i2c_client *client)
{
struct goodix_ts_data *ts = i2c_get_clientdata(client);
GTP_DEBUG_FUNC();
if (ts)
{
if (ts->use_irq)
{
//free_irq(client->irq, ts);
}
else
{
hrtimer_cancel(&ts->timer);
}
}
GTP_INFO("GTP driver removing...");
i2c_set_clientdata(client, NULL);
input_unregister_device(ts->input_dev);
kfree(ts);
return 0;
}
static int __maybe_unused alinx_ts_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
if (device_may_wakeup(dev)) enable_irq_wake(client->irq);
return 0;
}
static int __maybe_unused alinx_ts_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
if (device_may_wakeup(dev)) disable_irq_wake(client->irq);
return 0;
}
static SIMPLE_DEV_PM_OPS(alinx_ts_pm_ops, alinx_ts_suspend, alinx_ts_resume);
static const struct edt_i2c_chip_data alinx_data =
{
.max_support_points = 5,
};
static const struct of_device_id goodix_match_table[] = {
{.compatible = GTP_I2C_NAME, .data = &alinx_data },
{ },
};
static const struct i2c_device_id goodix_ts_id[] = {
{ .name = GTP_I2C_NAME, .driver_data = (long)&alinx_data },
{ }
};
static struct i2c_driver goodix_ts_driver = {
.probe = goodix_ts_probe,
.remove = goodix_ts_remove,
.id_table = goodix_ts_id,
.driver = {
.name = GTP_I2C_NAME,
.owner = THIS_MODULE,
.of_match_table = goodix_match_table,
.pm = &alinx_ts_pm_ops,
},
};
static int goodix_ts_init(void)
{
s32 ret;
GTP_DEBUG_FUNC();
GTP_INFO("GTP driver installing...");
goodix_wq = create_singlethread_workqueue("goodix_wq");
if (!goodix_wq)
{
GTP_ERROR("Creat workqueue failed.");
return -ENOMEM;
}
ret = i2c_add_driver(&goodix_ts_driver);
return ret;
}
static void __exit goodix_ts_exit(void)
{
GTP_DEBUG_FUNC();
GTP_INFO("GTP driver exited.");
i2c_del_driver(&goodix_ts_driver);
if (goodix_wq)
{
destroy_workqueue(goodix_wq);
}
}
module_init(goodix_ts_init);
module_exit(goodix_ts_exit);
MODULE_DESCRIPTION("GTP Series Driver");
MODULE_LICENSE("GPL");
交叉编译Makefile
modname:=touch_screen
obj-m:=$(modname).o
PWD :=$(shell pwd)
MAKE :=make
KERNELDIR = /home/petalinux/build/tmp/work-shared/zynqmp-generic/kernel-build-artifacts
CROSS_COMPILE=aarch64-linux-gnu-
ARCH=arm64
all:
$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf $(modname).ko *.o *mod* \.*cmd *odule* .tmp_versions
.PHONY: all clean