// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2022 MediaTek Inc. * Author Fengping Yu */ #include #include #include #include #include #include #include #include #include #define MTK_KPD_NAME "mt6779-keypad" #define MTK_KPD_MEM 0x0004 #define MTK_KPD_DEBOUNCE 0x0018 #define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0) #define MTK_KPD_DEBOUNCE_MAX_MS 256 #define MTK_KPD_SEL 0x0020 #define MTK_KPD_SEL_DOUBLE_KP_MODE BIT(0) #define MTK_KPD_SEL_COL GENMASK(15, 10) #define MTK_KPD_SEL_ROW GENMASK(9, 4) #define MTK_KPD_SEL_COLMASK(c) GENMASK((c) + 9, 10) #define MTK_KPD_SEL_ROWMASK(r) GENMASK((r) + 3, 4) #define MTK_KPD_NUM_MEMS 5 #define MTK_KPD_NUM_BITS 136 /* 4*32+8 MEM5 only use 8 BITS */ struct mt6779_keypad { struct regmap *regmap; struct input_dev *input_dev; struct clk *clk; u32 n_rows; u32 n_cols; void (*calc_row_col)(unsigned int key, unsigned int *row, unsigned int *col); DECLARE_BITMAP(keymap_state, MTK_KPD_NUM_BITS); }; static const struct regmap_config mt6779_keypad_regmap_cfg = { .reg_bits = 32, .val_bits = 32, .reg_stride = sizeof(u32), .max_register = 36, }; static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id) { struct mt6779_keypad *keypad = dev_id; const unsigned short *keycode = keypad->input_dev->keycode; DECLARE_BITMAP(new_state, MTK_KPD_NUM_BITS); DECLARE_BITMAP(change, MTK_KPD_NUM_BITS); unsigned int bit_nr, key; unsigned int row, col; unsigned int scancode; unsigned int row_shift = get_count_order(keypad->n_cols); bool pressed; regmap_bulk_read(keypad->regmap, MTK_KPD_MEM, new_state, MTK_KPD_NUM_MEMS); bitmap_xor(change, new_state, keypad->keymap_state, MTK_KPD_NUM_BITS); for_each_set_bit(bit_nr, change, MTK_KPD_NUM_BITS) { /* * Registers are 32bits, but only bits [15:0] are used to * indicate key status. */ if (bit_nr % 32 >= 16) continue; key = bit_nr / 32 * 16 + bit_nr % 32; keypad->calc_row_col(key, &row, &col); scancode = MATRIX_SCAN_CODE(row, col, row_shift); /* 1: not pressed, 0: pressed */ pressed = !test_bit(bit_nr, new_state); dev_dbg(&keypad->input_dev->dev, "%s", pressed ? "pressed" : "released"); input_event(keypad->input_dev, EV_MSC, MSC_SCAN, scancode); input_report_key(keypad->input_dev, keycode[scancode], pressed); input_sync(keypad->input_dev); dev_dbg(&keypad->input_dev->dev, "report Linux keycode = %d\n", keycode[scancode]); } bitmap_copy(keypad->keymap_state, new_state, MTK_KPD_NUM_BITS); return IRQ_HANDLED; } static void mt6779_keypad_calc_row_col_single(unsigned int key, unsigned int *row, unsigned int *col) { *row = key / 9; *col = key % 9; } static void mt6779_keypad_calc_row_col_double(unsigned int key, unsigned int *row, unsigned int *col) { *row = key / 13; *col = (key % 13) / 2; } static int mt6779_keypad_pdrv_probe(struct platform_device *pdev) { struct mt6779_keypad *keypad; void __iomem *base; int irq; u32 debounce; u32 keys_per_group; bool wakeup; int error; keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); if (!keypad) return -ENOMEM; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); keypad->regmap = devm_regmap_init_mmio(&pdev->dev, base, &mt6779_keypad_regmap_cfg); if (IS_ERR(keypad->regmap)) { dev_err(&pdev->dev, "regmap init failed:%pe\n", keypad->regmap); return PTR_ERR(keypad->regmap); } bitmap_fill(keypad->keymap_state, MTK_KPD_NUM_BITS); keypad->input_dev = devm_input_allocate_device(&pdev->dev); if (!keypad->input_dev) { dev_err(&pdev->dev, "Failed to allocate input dev\n"); return -ENOMEM; } keypad->input_dev->name = MTK_KPD_NAME; keypad->input_dev->id.bustype = BUS_HOST; error = matrix_keypad_parse_properties(&pdev->dev, &keypad->n_rows, &keypad->n_cols); if (error) { dev_err(&pdev->dev, "Failed to parse keypad params\n"); return error; } if (device_property_read_u32(&pdev->dev, "debounce-delay-ms", &debounce)) debounce = 16; if (debounce > MTK_KPD_DEBOUNCE_MAX_MS) { dev_err(&pdev->dev, "Debounce time exceeds the maximum allowed time %dms\n", MTK_KPD_DEBOUNCE_MAX_MS); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "mediatek,keys-per-group", &keys_per_group)) keys_per_group = 1; switch (keys_per_group) { case 1: keypad->calc_row_col = mt6779_keypad_calc_row_col_single; break; case 2: keypad->calc_row_col = mt6779_keypad_calc_row_col_double; break; default: dev_err(&pdev->dev, "Invalid keys-per-group: %d\n", keys_per_group); return -EINVAL; } wakeup = device_property_read_bool(&pdev->dev, "wakeup-source"); dev_dbg(&pdev->dev, "n_row=%d n_col=%d debounce=%d\n", keypad->n_rows, keypad->n_cols, debounce); error = matrix_keypad_build_keymap(NULL, NULL, keypad->n_rows, keypad->n_cols, NULL, keypad->input_dev); if (error) { dev_err(&pdev->dev, "Failed to build keymap\n"); return error; } input_set_capability(keypad->input_dev, EV_MSC, MSC_SCAN); regmap_write(keypad->regmap, MTK_KPD_DEBOUNCE, (debounce * (1 << 5)) & MTK_KPD_DEBOUNCE_MASK); if (keys_per_group == 2) regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_DOUBLE_KP_MODE, MTK_KPD_SEL_DOUBLE_KP_MODE); regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_ROW, MTK_KPD_SEL_ROWMASK(keypad->n_rows)); regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_COL, MTK_KPD_SEL_COLMASK(keypad->n_cols)); keypad->clk = devm_clk_get_enabled(&pdev->dev, "kpd"); if (IS_ERR(keypad->clk)) return PTR_ERR(keypad->clk); irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mt6779_keypad_irq_handler, IRQF_ONESHOT, MTK_KPD_NAME, keypad); if (error) { dev_err(&pdev->dev, "Failed to request IRQ#%d: %d\n", irq, error); return error; } error = input_register_device(keypad->input_dev); if (error) { dev_err(&pdev->dev, "Failed to register device\n"); return error; } error = device_init_wakeup(&pdev->dev, wakeup); if (error) dev_warn(&pdev->dev, "device_init_wakeup() failed: %d\n", error); return 0; } static const struct of_device_id mt6779_keypad_of_match[] = { { .compatible = "mediatek,mt6779-keypad" }, { .compatible = "mediatek,mt6873-keypad" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mt6779_keypad_of_match); static struct platform_driver mt6779_keypad_pdrv = { .probe = mt6779_keypad_pdrv_probe, .driver = { .name = MTK_KPD_NAME, .of_match_table = mt6779_keypad_of_match, }, }; module_platform_driver(mt6779_keypad_pdrv); MODULE_AUTHOR("Mediatek Corporation"); MODULE_DESCRIPTION("MTK Keypad (KPD) Driver"); MODULE_LICENSE("GPL");