1. 程式人生 > >I.MX6Q(TQIMX6Q/TQE9)學習筆記——新版BSP之觸控式螢幕驅動移植

I.MX6Q(TQIMX6Q/TQE9)學習筆記——新版BSP之觸控式螢幕驅動移植

之所以說是驅動移植是因為之前已經在TQ210、AM335x兩個平臺上移植過了,因此,僅需要少量修改就可以將驅動移植到imx6q。下面開始觸控驅動移植。

DTS編寫

參考其它DTS的i2c裝置寫法,我們可以新增如下內容:

&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1_2>;
	status = "okay";

	[email protected] {
		compatible = "gt811,gt811_ts";  
	    pinctrl-names = "default";  
		reg = <0x5d>;  
		interrupt-parent = <&gpio1>;  
		interrupts = <9 2>;  
		gpios = <&gpio1 5 0>;  
		  
		touchscreen-size-x = <800>;  
		touchscreen-size-y = <480>;  

		touchscreen-swap = <1>;  
		touchscreen-revert-x = <1>;  
		touchscreen-revert-y = <1>; 
	};
};

新增以上內容後重新編譯並燒寫DTB。

驅動編寫

其實移植AM335x的時候就是以DTB方式移植的,因此,除核心api變更的部分需要修改一下,其它程式碼基本是不需要修改的。驅動程式碼如下(不含event上報程式碼):

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/io.h>

struct gt811_ts_platdata
{
	u32 size_x;
	u32 size_y;
	u32 size_p;
	u32 swap;
	u32 revert_x;
	u32 revert_y;
	u32 reset_pin;
	u32 interrupt_pin;
	u32 ponits_max;
	struct i2c_client *client;
	struct input_dev *input;
	struct work_struct work;
};

static const struct of_device_id gt811_ts_of_match[] = {
	{ .compatible = "gt811,gt811_ts", .data = NULL },
	{ }
};

static int i2c_write_bytes(struct i2c_client *client, uint8_t *data, int len){
	struct i2c_msg msg;

	msg.flags=!I2C_M_RD;
	msg.addr=client->addr;
	msg.len=len;
	msg.buf=data;

	return i2c_transfer(client->adapter,&msg, 1);
}

static int i2c_read_bytes(struct i2c_client *client, uint8_t *buf, int len){
	struct i2c_msg msgs[2];

	msgs[0].flags=!I2C_M_RD;
	msgs[0].addr=client->addr;
	msgs[0].len=2;
	msgs[0].buf=&buf[0];

	msgs[1].flags=I2C_M_RD;
	msgs[1].addr=client->addr;
	msgs[1].len=len-2;
	msgs[1].buf=&buf[2];

	return i2c_transfer(client->adapter,msgs, 2);
}
static void gt811_ts_handler(struct work_struct *work)
{
	struct gt811_ts_platdata *pdata = container_of(work, struct gt811_ts_platdata, work);
	struct device *dev = &pdata->client->dev;
	uint8_t buffer[36] = {0x07, 0x21, 0};
	struct input_mt_pos pos[5];
	int slots[5], slot;
	uint8_t count, index, flags, position;

	buffer[0] = 0x0f;
	buffer[1] = 0xff;
	if (i2c_write_bytes(pdata->client,buffer,2) < 0) {
		dev_err(dev, "Failed to write wakeup message.\n");
		goto reenable_irq;
	}

	buffer[0] = 0x07;
	buffer[1] = 0x21;
	if (i2c_read_bytes(pdata->client, buffer, sizeof(buffer)) < 0) {
		dev_err(dev, "Failed to read touch message.\n");
		goto reenable_irq;
	}

	buffer[0] = 0x80;
	buffer[1] = 0x00;
	if (i2c_write_bytes(pdata->client, buffer, 2) < 0) {
		dev_err(dev, "Failed to write sleep message.\n");
		goto reenable_irq;
	}

	buffer[25] = buffer[19];
	buffer[19] = 0;

	flags = buffer[2]&0x1f;

	count = 0;
	for (index = 0; index < 5 && flags; ++index, flags >>= 1) {
		if (!(flags&0x1)) {
			continue;
		}

		if (index < 3) {
			position = 4 + index * 5;
		}
		else{
			position = 25 + (index - 3) * 5;
		}

		pos[count].x = (buffer[position] << 8) | buffer[position + 1];
		pos[count].y = (buffer[position + 2] << 8) | buffer[position + 3];
		slots[count] = index;

		if(pdata->swap) {
			swap(pos[count].x, pos[count].y);
		}
		if(pdata->revert_x){
			pos[count].x = pdata->size_x - pos[count].x;
		}
		if(pdata->revert_y){
			pos[count].y = pdata->size_y - pos[count].y;
		}

		++count;
	}

	// 在這個位置新增event上報程式碼
	...

reenable_irq:
	enable_irq(pdata->client->irq);
}

static irqreturn_t gt811_ts_isr(int irq, void *dev_id)
{
	struct gt811_ts_platdata* pdata = (struct gt811_ts_platdata*)dev_id;

	disable_irq_nosync(pdata->client->irq);
	schedule_work(&pdata->work);

	return IRQ_HANDLED;
}

static int gt811_ts_initilize(struct i2c_client *client)
{
	struct device *dev = &client->dev;
	struct gt811_ts_platdata *pdata = (struct gt811_ts_platdata*)i2c_get_clientdata(client);
	int status = 0, count = 0;
	uint8_t version[4] = {0x7, 0x17, 0};
	uint8_t config[] = {
		0x06,0xA2,
		0x12,0x10,0x0E,0x0C,0x0A,0x08,0x06,0x04,0x02,0x00,0xE2,0x53,0xD2,0x53,0xC2,0x53,
		0xB2,0x53,0xA2,0x53,0x92,0x53,0x82,0x53,0x72,0x53,0x62,0x53,0x52,0x53,0x42,0x53,
		0x32,0x53,0x22,0x53,0x12,0x53,0x02,0x53,0xF2,0x53,0x0F,0x13,0x40,0x40,0x40,0x10,
		0x10,0x10,0x0F,0x0F,0x0A,0x35,0x25,0x0C,0x03,0x00,0x05,0x20,0x03,0xE0,0x01,0x00,
		0x00,0x34,0x2C,0x36,0x2E,0x00,0x00,0x03,0x19,0x03,0x08,0x00,0x00,0x00,0x00,0x00,
		0x14,0x10,0xEC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0D,0x40,
		0x30,0x3C,0x28,0x00,0x00,0x00,0x00,0xC0,0x12,0x01
	};

	config[62] = 480 >> 8;
	config[61] = 480 & 0xff;
	config[64] = 800 >> 8;
	config[63] = 800 & 0xff;

	if (!gpio_is_valid(pdata->reset_pin)) {
		dev_err(dev, "The reset pin number is invalid.\n");
		return -EINVAL;
	}

	count = 3;
	while (count--) {
		gpio_direction_output(pdata->reset_pin, 0);
		msleep(10);
		gpio_direction_output(pdata->reset_pin, 1);
		msleep(100);

		if (i2c_read_bytes(client, version, sizeof(version)) < 0) {
			dev_err(dev, "Failed to get the version of GT811, try again...\n");
			status = -ENODEV;
		}
		else {
			dev_info(dev, "Gt811 detected, version(%04x)...\n", (version[2]<<8)|version[3]);
			status = 0;
			break;
		}
	}

	if (status) {
		return status;
	}

	count = 3;
	while (count--) {
		if (i2c_write_bytes(client, config, sizeof(config)) < 0) {
			dev_err(dev, "Failed to configure the GT811, try again...\n");
			status = -EINVAL;
		}
		else {
			dev_info(dev, "Gt811 configue succeed\n");
			status = 0;
			break;
		}
	}

	return status;
}

static struct gt811_ts_platdata *gt811_ts_parse_devtree(struct i2c_client *client)
{
	struct device *dev = &client->dev;
	struct device_node *node;
	struct gt811_ts_platdata *pdata;
	enum of_gpio_flags flags;

	node = dev->of_node;
	if (!node) {
		dev_err(dev, "The of_node is NULL.\n");
		return ERR_PTR(-ENODEV);
	}

	pdata = devm_kzalloc(dev, sizeof(struct device_node), GFP_KERNEL);
	if (!pdata) {
		dev_err(dev, "No enough memory left.\n");
		return ERR_PTR(-ENOMEM);
	}

	pdata->reset_pin = of_get_gpio_flags(node, 0, &flags);
	if (pdata->reset_pin < 0) {
		dev_err(dev, "Get RST pin failed!\n");
		return ERR_PTR(-EINVAL);
	}

	if (of_property_read_u32(node, "touchscreen-size-x", &pdata->size_x )) {
		dev_err(dev, "Failed to get the touch screen x size.\n");
		return ERR_PTR(-EINVAL);
	}

	if (of_property_read_u32(node, "touchscreen-size-y", &pdata->size_y)) {
		dev_err(dev, "Failed to get the touch screen y size.\n");
		return ERR_PTR(-EINVAL);
	}

	if (of_property_read_u32(node, "touchscreen-size-p", &pdata->size_p)) {
		pdata->size_p = 255;
	}

	if (of_property_read_u32(node, "touchscreen-swap", &pdata->swap)) {
		pdata->swap = 1;
	}

	if (of_property_read_u32(node, "touchscreen-revert-x", &pdata->revert_x)) {
		pdata->revert_x = 1;
	}

	if (of_property_read_u32(node, "touchscreen-revert-y", &pdata->revert_y)) {
		pdata->revert_y = 1;
	}

	return pdata;
}

static int gt811_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	struct device *dev = &client->dev;
	struct gt811_ts_platdata *pdata = dev_get_platdata(dev);
	struct input_dev *input;
	int error = 0;

	if (!of_match_device(of_match_ptr(gt811_ts_of_match), dev)) {
		dev_err(dev, "Failed to match.\n");
		return -EINVAL;
	}

	if (!pdata) {
		pdata = gt811_ts_parse_devtree(client);
		if (IS_ERR(pdata)) {
			dev_err(dev, "Get device data from device tree failed!\n");
			error = -EINVAL;
			goto failed_exit;
		}
	}

	pdata->client = client;

	i2c_set_clientdata(client, pdata);

	error = devm_gpio_request_one(&client->dev, pdata->reset_pin,
				GPIOF_OUT_INIT_HIGH, "gt811_rst_pin");
	if (error < 0) {
		dev_err(dev, "Failed to request gt811 reset pin\n");
		return error;
	}

	input = devm_input_allocate_device(dev);
	if (!input) {
		dev_err(dev, "Failed to allocate input device\n");
		error = -ENOMEM;
		goto pdata_free;
	}

	pdata->input = input;

	input->name = client->name;
	input->id.bustype = BUS_I2C;
	input->id.product = 0xBEEF;
	input->id.vendor  =0xDEAD;
	input->dev.parent = &client->dev;

	__set_bit(EV_KEY, input->evbit);
	__set_bit(EV_ABS, input->evbit);
	__set_bit(BTN_TOUCH, input->keybit);
	input_set_abs_params(input, ABS_X, 0, pdata->size_x, 0, 0);
	input_set_abs_params(input, ABS_Y, 0, pdata->size_y, 0, 0);
	input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->size_x, 0, 0);
	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->size_y, 0, 0);

	error = input_mt_init_slots(input, 5, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
	if (error) {
		dev_err(dev, "Failed to initialize the multi-touch slots.\n");
		goto input_free;
	}

	input_set_drvdata(input, pdata);

	error = input_register_device(input);
	if (error) {
		dev_err(dev, "Register input device failed!\n");
		goto input_free;
	}

	if (gt811_ts_initilize(client)) {
		dev_err(dev, "Failed to initialize GT811.\n");
	}

	INIT_WORK(&pdata->work, gt811_ts_handler);

	error = devm_request_irq(dev, client->irq, gt811_ts_isr,
					  IRQF_TRIGGER_FALLING, client->name, pdata);
	if (error) {
		dev_err(dev, "Failed to request irq(number:%d)\n", client->irq);
		goto input_free;
	}

	return 0;

input_free:
	devm_kfree(dev, input);
pdata_free:
	devm_kfree(dev, pdata);
failed_exit:
	return error;
}

static int gt811_ts_remove(struct i2c_client *client)
{
	struct gt811_ts_platdata *pdata = (struct gt811_ts_platdata*)i2c_get_clientdata(client);

	devm_free_irq(&client->dev, client->irq, i2c_get_clientdata(client));

	input_unregister_device(pdata->input);

	devm_kfree(&client->dev, pdata);
	return 0;
}

static const struct i2c_device_id gt811_ts_id[] = {
	{ "gt811_ts", 0 },
	{ }
};

static struct i2c_driver gt811_ts_driver = {
	.driver = {
		.owner	= THIS_MODULE,
		.name	= "gt811_ts",
		.of_match_table = of_match_ptr(gt811_ts_of_match),
	},
	.probe		= gt811_ts_probe,
	.remove		= gt811_ts_remove,
	.id_table   = gt811_ts_id,
};

module_i2c_driver(gt811_ts_driver);

MODULE_AUTHOR("girlkoo <
[email protected]
>"); MODULE_DESCRIPTION("GT811 I2C Touchscreen Driver"); MODULE_LICENSE("GPL");

編譯以上程式碼得到相應的ko檔案,然後將該ko拷貝SD卡的根檔案系統內。完整的程式碼下載連結如下(賺點下載積分^_^):

測試方法

我認為最實用的測試方法就是tslib,tslib的配置方法請參考TQ210的移植筆記或者自己網上搜索資料,我這裡就不重複了,安裝好tslib後先通過ts_calibrate校準觸控式螢幕(實際上電容屏是不需要校準的,僅僅是為了使用tslib),然後執行ts_test並點draw按鈕,即可隨意畫圖。下面是實測效果圖:

至此,觸控式螢幕驅動就移植完成了。有問題請幫忙指出,有任何疑問歡迎留言討論。

本文作者:girlkoo

相關推薦

I.MX6Q(TQIMX6Q/TQE9)學習筆記——新版BSP觸控式螢幕驅動移植

之所以說是驅動移植是因為之前已經在TQ210、AM335x兩個平臺上移植過了,因此,僅需要少量修改就可以將驅動移植到imx6q。下面開始觸控驅動移植。 DTS編寫 參考其它DTS的i2c裝置寫法,我們可以新增如下內容: &i2c1 { clock-frequen

I.MX6Q(TQIMX6Q/TQE9)學習筆記——新版BSPu-boot移植

前段時間就開始學習I.MX6Q了,但是最近工作實在是忙,間斷了一些時間了。為了提高移植效率,還是考慮移植Freescale維護的3.10版本的核心。 原始碼獲取 Freescale維護的3.10的核心是使用git管理的,但是直接使用git下載程式碼會比較慢,下面是我下載好的

I.MX6Q(TQIMX6Q/TQE9)學習筆記——新版BSPKEY、LED

經過前面的移植,核心已經可以在tqimx6q上正常掛載根檔案系統了,接下來開始移植各類裝置驅動,本文先來移植按鍵和LED。之所以從按鍵和LED開始是因為這兩種裝置的驅動是最簡單的。 按鍵驅動移植 檢視tqimx6q的原理圖可知,這塊開發板有5個按鍵,其中,除了reset鍵之

I.MX6Q(TQIMX6Q/TQE9)學習筆記——新版BSPUART移植

我們在核心啟動時候已經配置系統了UART,但是tqimx6q這塊開發板上引出了imx6q的5路TTL介面,其中,uart1和uart2轉成232介面,本文將配置這5路Uart介面。 DTS配置 由於核心中已經有imx6q的uart控制器驅動,因此,我們只需要配置各埠的pin

I.MX6Q(TQIMX6Q/TQE9)學習筆記——U-Boot移植

其實Freescale的BSP移植文件已經將u-boot的移植步驟講述的非常詳細了,但為了以後方便查閱,還是按照自己的理解記錄在這裡。 獲取原始碼 根據前一篇文章搭建好LTIB環境後就可以非常方便的匯出u-boot原始碼了。切換到ltib目錄,並執行如下指令: ./l

I.MX6Q(TQIMX6Q/TQE9)學習筆記——核心啟動與檔案系統掛載

經過前面的移植,u-boot已經有能力啟動核心了,本文主要來看下如何通過之前移植的u-boot來啟動核心。如果按照前面的文章完成了LTIB的編譯,那麼,Linux的核心應該就會出現rpm/BUILD/目錄下,接下來,我們就開始移植這個3.0.35版本的核心到TQIMX6Q。

I.MX6Q(TQIMX6Q/TQE9)學習筆記——開發板的選擇

其實入手這塊TQIMX6Q的時候手頭上已經有一塊E9開發板了,但還是選擇了TQIMX6Q開發板,主要原因是個人感覺開發板更適合學習或研發,E9卡片電腦更適合應用,而且這兩款單板使用的是相同的晶片(Freescale的IMX6Q),因此,學習過程中還可以在開發板上實驗,然後在

友善臂最新版mini2440學習筆記——u-boot 1.1.6移植(一)

從本文開始,將記錄博主在進行u-boot 1.1.6移植過程中遇到的問題。本文將涉及兩個問題: 1. u-boot中新增開發板 2. u-boot第一階段啟動程式碼 1. u-boot中新增開發板 1.1. Makefile更改 1.1.1. 增加反彙編檔案輸出 Makef

Java學習筆記——設計模式五.工廠方法

strong scanner multipl 石頭 simple 決定 定義 opera 下使用 水邊一只青蛙在笑       ——石頭和水 工廠方法模式(Factory Method),定義了一個用於創建對象的接口,讓實現類決定實例化哪一個類。工廠方法使一個類的實

Java學習筆記——設計模式六.原型模式(淺克隆和深克隆)

catch 新的 att over 引用變量 col logs implement pri That there‘s some good in this world, Mr. Frodo. And it‘s worth fighting for. 原型模式(prot

Java學習筆記——設計模式七.模板方法模式

tro mage emp java學習 java學習筆記 trac 子類 技術 primitive 模板方法模式(TemplateMethod),定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。

Redis學習筆記04Redis命令(3)服務器操作

毫秒 上下 set 文件描述符 mil 輸出鏈表 事件 客戶 moni 1.1.1. client list 列出所有客戶端連接信息。 每個連接使用一個id=xxx的行表示。 redis.coe2coe.me:6379> client list id=8 ad

Redis學習筆記06Redis命令(5)事務

mman 客戶 cau 連接 discard 順序 strong 存在 執行命令 1.1.1. multi 開始一個新事務。 redis.coe2coe.me:6379> multi OK 執行此命令後,後面執行的set等命令將被緩存,直到被discard

【知了堂學習筆記】mysql數據庫的增刪改查

into database uniq unique 大寫 spa 分享 incr int 一、DDL 1、創建數據庫 create database 數據庫名 *數據庫名不能中文, 不能數字正常英文 , 關鍵字會自動變大寫 2、刪除數據庫 drop datab

七、Hadoop學習筆記————調優Hadoop參數調優

node 參數 受限 .com 資源 mage 預留空間 嘗試 nod dfs.datanode.handler.count默認為3,大集群可以調整為10 傳統MapReduce和yarn對比 如果服務器物理內存128G,則容器內存建議為100比較合理 配置總

八、Hadoop學習筆記————調優Hive調優

需要 cnblogs log logs nbsp .cn 集中 bsp 9.png 表1表2的join和表3表4的join同時運行 此法需要關註是否有數據傾斜(大量數據集中在某一區間段) 八、Hadoop學習筆記————調優之Hive調優

學習筆記:設計模式抽象工廠(C#)

sta 實體 mys actor div ted rac int c# 1.創建抽象工廠類 public abstract class AbstractFactory { public abstract Connect CreatConne

kvm虛擬化學習筆記(十四)kvm虛擬機靜態遷移

虛擬主機 kvm 虛擬機遷移 kvm虛擬化 這裏提到的靜態遷移同是基於KVM虛擬主機之間的遷移,非異構虛擬化平臺的靜態遷移。1.靜態遷移就是虛擬機在關機狀態下,拷貝虛擬機虛擬磁盤文件與配置文件到目標虛擬主機中,實現的遷移。(1)虛擬主機各自使用本地存儲存放虛擬機磁盤文件本文實現基於本地磁盤存儲

kvm虛擬化學習筆記(十八)ESXi到KVMv2v遷移

虛擬化 kvm v2v kvm虛擬機遷移 1.ESXi到KVM之v2v情況說明(1).配置任務列表:1)VMwareESXi虛擬平臺下linux系統遷移到KVM虛擬平臺。2)VMwareESXi虛擬平臺下windows系統遷移到KVM虛擬平臺。提示:本文只介紹以上兩種遷移過程,KVM到ESXi

MYSQL 學習筆記記錄整理二:分組數據

where 分組 學習 供應商 order by 記錄 price 需要 sele 1、創建分組 GROUP BY SELECT ven_id,COUNT(*) AS num_prods FROM products GROUP BY ven_id; 註意: GROUP BY