mtk-keypad


一.keypad基本原理

col作為輸出,row作為輸入檢測,低電平有效
col A~D輪流輸出低電平,通過rol 1~4上的低電平可以檢測是哪個按鍵按下了
但是存在這樣的問題,A1,A2,B1同時按下,會造成B2按下的假象,稱為鬼影(這3個鍵導通,colB打開,row2處也會檢測到低電平)

可以通過增加二極管的方式防止鬼影問題

二.keypad porting

  • 1.dws中GPIO設置,mtk將ROW作為輸出,COL作為輸入檢測,preloader的keypad.c文件中對keypad進行了設置
    •   KCOL:input + pull enable + pull up
    •        KROW:output + pull disable + pulldown

  • 2.dws keypad設置,mtk6750最多支持2*2按鍵矩陣,通過下拉框選擇相應的按鍵,按鍵name、對應鍵值[Key_code_linux]在Keypad_YuSu.cmp這個文件中有定義

  • 3.添加新的按鍵參考FAQ13931

三.keypad代碼分析

生成的keypad設備樹節點信息如下,kpd-hw-init-map將鍵盤矩陣以一維數組(鍵值)的格式表示,一共72個

keypad@10010000 {
            compatible = "mediatek,mt6755-keypad", "mediatek,kp";
            reg = <0x10010000 0x1000>;
            interrupts = <0x0 0xa4 0x2>;
            mediatek,kpd-key-debounce = <0x400>;
            mediatek,kpd-sw-pwrkey = <0x74>;
            mediatek,kpd-hw-pwrkey = <0x8>;
            mediatek,kpd-sw-rstkey = <0x73>;
            mediatek,kpd-hw-rstkey = <0x11>;
            mediatek,kpd-use-extend-type = <0x0>;
            mediatek,kpd-hw-map-num = <0x48>;
            mediatek,kpd-hw-init-map = <0x72 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>;
            mediatek,kpd-pwrkey-eint-gpio = <0x0>;
            mediatek,kpd-pwkey-gpio-din = <0x0>;
            mediatek,kpd-hw-dl-key0 = <0x11>;
            mediatek,kpd-hw-dl-key1 = <0x0>;
            mediatek,kpd-hw-dl-key2 = <0x8>;
            mediatek,kpd-hw-recovery-key = <0x11>;
            mediatek,kpd-hw-factory-key = <0x0>;
            status = "okay";
        };
static int kpd_pdrv_probe(struct platform_device *pdev)
{

    int i, r;
    int err = 0;
    struct clk *kpd_clk = NULL;
//獲取clk,這里kpd-clk是通過ccf設置的
    kpd_clk = devm_clk_get(&pdev->dev, "kpd-clk");
    if (!IS_ERR(kpd_clk)) {
        int ret_prepare, ret_enable;

        ret_prepare = clk_prepare(kpd_clk);
        if (ret_prepare)
            kpd_print("clk_prepare returned %d\n", ret_prepare);
        ret_enable = clk_enable(kpd_clk);
        if (ret_enable)
            kpd_print("clk_enable returned %d\n", ret_prepare);
    } else {
        kpd_print("get kpd-clk fail, but not return, maybe kpd-clk is set by ccf.\n");
    }
//reg重映射
    kp_base = of_iomap(pdev->dev.of_node, 0);
    if (!kp_base) {
        kpd_info("KP iomap failed\n");
        return -ENODEV;
    };
//irq映射
    kp_irqnr = irq_of_parse_and_map(pdev->dev.of_node, 0);
    if (!kp_irqnr) {
        kpd_info("KP get irqnr failed\n");
        return -ENODEV;
    }
//申請input設備
    kpd_input_dev = input_allocate_device();
    if (!kpd_input_dev) {
        kpd_print("input allocate device fail.\n");
        return -ENOMEM;
    }
//input設備初始化
    kpd_input_dev->name = KPD_NAME;
    kpd_input_dev->id.bustype = BUS_HOST;
    kpd_input_dev->id.vendor = 0x2454;
    kpd_input_dev->id.product = 0x6500;
    kpd_input_dev->id.version = 0x0010;
    kpd_input_dev->open = kpd_open;
//解析dts中keypad節點的信息,賦值給kpd_dts_data結構體
    kpd_get_dts_info(pdev->dev.of_node);
//分配內存空間,用於存放鍵值和按鍵狀態寄存器
    kpd_memory_setting();
//input設備支持EV_KEY事件
    __set_bit(EV_KEY, kpd_input_dev->evbit);
//powerkey連接PMIC,kpd_keymap[8]設置為空 #if defined(CONFIG_KPD_PWRKEY_USE_EINT) || defined(CONFIG_KPD_PWRKEY_USE_PMIC)
    __set_bit(kpd_dts_data.kpd_sw_pwrkey, kpd_input_dev->keybit);
    kpd_keymap[8] = 0;
#endif
//powerkey列除[8]外,其余都清空
if (!kpd_dts_data.kpd_use_extend_type) { for (i = 17; i < KPD_NUM_KEYS; i += 9) /* only [8] works for Power key */ kpd_keymap[i] = 0; }
//設置設備支持的鍵值
for (i = 0; i < KPD_NUM_KEYS; i++) { if (kpd_keymap[i] != 0) __set_bit(kpd_keymap[i], kpd_input_dev->keybit); } //reset按鍵 if (kpd_dts_data.kpd_sw_rstkey) __set_bit(kpd_dts_data.kpd_sw_rstkey, kpd_input_dev->keybit); kpd_input_dev->dev.parent = &pdev->dev;
//注冊input設備 r
= input_register_device(kpd_input_dev); if (r) { kpd_info("register input device failed (%d)\n", r); input_free_device(kpd_input_dev); return r; } /* register device (/dev/mt6575-kpd) */ kpd_dev.parent = &pdev->dev; r = misc_register(&kpd_dev); if (r) { kpd_info("register device failed (%d)\n", r); input_unregister_device(kpd_input_dev); return r; } //初始化wake_lock wake_lock_init(&kpd_suspend_lock, WAKE_LOCK_SUSPEND, "kpd wakelock"); //設置按鍵消抖並申請中斷處理 kpd_set_debounce(kpd_dts_data.kpd_key_debounce); r = request_irq(kp_irqnr, kpd_irq_handler, IRQF_TRIGGER_NONE, KPD_NAME, NULL); if (r) { kpd_info("register IRQ failed (%d)\n", r); misc_deregister(&kpd_dev); input_unregister_device(kpd_input_dev); return r; } #ifndef KPD_EARLY_PORTING /*add for avoid early porting build err the macro is defined in custom file */ long_press_reboot_function_setting(); /* /API 4 for kpd long press reboot function setting */ #endif hrtimer_init(&aee_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); aee_timer.function = aee_timer_func; //添加文件屬性 err = kpd_create_attr(&kpd_pdrv.driver); if (err) { kpd_info("create attr file fail\n"); kpd_delete_attr(&kpd_pdrv.driver); return err; } kpd_info("%s Done\n", __func__); return 0; }

看下中斷處理函數的內容:

static irqreturn_t kpd_irq_handler(int irq, void *dev_id)
{
//禁止中斷,無需進行同步,防止死鎖
    disable_irq_nosync(kp_irqnr);
//調度tasklet tasklet_schedule(
&kpd_keymap_tasklet); return IRQ_HANDLED; }
//定義tasklet,執行kpd_keymap_handler函數
static
DECLARE_TASKLET(kpd_keymap_tasklet, kpd_keymap_handler, 0);
static void kpd_keymap_handler(unsigned long data)
{
    int i, j;
    bool pressed;
    u16 new_state[KPD_NUM_MEMS], change, mask;
    u16 hw_keycode, linux_keycode;
//mtk通過5組寄存器來保存按鍵的狀態,這里回讀寄存器並保存為new_state
    kpd_get_keymap_state(new_state);
//激活鎖喚醒系統,500ms后就釋放掉
    wake_lock_timeout(&kpd_suspend_lock, HZ / 2);

    for (i = 0; i < KPD_NUM_MEMS; i++) {
//每組中按鍵狀態未改變則對比下一組,按位處理 change
= new_state[i] ^ kpd_keymap_state[i]; if (!change) continue; for (j = 0; j < 16; j++) {
//每組(16位)中對比按位查看是否狀態發生改變 mask
= 1U << j; if (!(change & mask)) continue; hw_keycode = (i << 4) + j; /* bit is 1: not pressed, 0: pressed */ //按鍵是否按下,寄存器中0表示按鍵處於按下狀態
pressed
= !(new_state[i] & mask); if (kpd_show_hw_keycode) kpd_print("(%s) HW keycode = %u\n", pressed ? "pressed" : "released", hw_keycode); BUG_ON(hw_keycode >= KPD_NUM_KEYS); linux_keycode = kpd_keymap[hw_keycode]; if (unlikely(linux_keycode == 0)) { kpd_print("Linux keycode = 0\n"); continue; } kpd_aee_handler(linux_keycode, pressed); //上報鍵值 input_report_key(kpd_input_dev, linux_keycode, pressed); input_sync(kpd_input_dev); kpd_print("report Linux keycode = %u\n", linux_keycode); } } //kpd_keymap_state保存new_state,用於下輪對比 memcpy(kpd_keymap_state, new_state, sizeof(new_state)); kpd_print("save new keymap state\n");
//按鍵處理完畢,打開中斷 enable_irq(kp_irqnr); }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM