/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* Touch scanning module */ #include "common.h" #include "console.h" #include "cpu.h" #include "debug.h" #include "dma.h" #include "encode.h" #include "gpio.h" #include "hooks.h" #include "master_slave.h" #include "registers.h" #include "spi_comm.h" #include "task.h" #include "timer.h" #include "touch_scan.h" #include "util.h" #define TS_PIN_TO_CR(p) ((((p).port_id + 1) << 16) | (1 << (p).pin)) #define TS_GPIO_TO_BASE(p) (0x40010800 + (p) * 0x400) static uint8_t buf[2][ROW_COUNT * 2]; #ifdef CONFIG_KEYBORG_FAST_SCAN #define SCAN_BUF_SIZE (DIV_ROUND_UP(COL_COUNT * 2, 32) + 2) #define GET_SCAN_NEEDED(x) (scan_needed[(x) / 32 + 1] & (1 << ((x) & 31))) static uint32_t scan_needed[SCAN_BUF_SIZE]; #else #define GET_SCAN_NEEDED(x) 1 #endif #define SPAN_LENGTH (2 * COL_SPAN + 1) #define SPAN_MASK ((1 << SPAN_LENGTH) - 1) static uint32_t mccr_list[COL_COUNT]; static uint32_t mrcr_list[ROW_COUNT]; static void set_gpio(const struct ts_pin pin, enum pin_type type) { uint32_t addr, mode, mask; uint32_t port = TS_GPIO_TO_BASE(pin.port_id); uint32_t pmask = 1 << pin.pin; if (pmask & 0xff) { addr = port; mode = pmask; } else { addr = port + 0x04; mode = pmask >> 8; } mode = mode * mode * mode * mode * 0xf; mask = REG32(addr) & ~mode; if (type == PIN_COL) { /* Alternate output open-drain */ mask |= 0xdddddddd & mode; } else if (type == PIN_PD) { mask |= 0x88888888 & mode; STM32_GPIO_BSRR(port) = pmask << 16; } else if (type == PIN_Z) { mask |= 0x44444444 & mode; } else if (type == PIN_ROW) { /* Nothing for PIN_ROW. Already analog input. */ } REG32(addr) = mask; } void touch_scan_init(void) { int i; for (i = 0; i < ROW_COUNT; ++i) { set_gpio(row_pins[i], PIN_ROW); STM32_PMSE_PxPMR(row_pins[i].port_id) |= 1 << row_pins[i].pin; } for (i = 0; i < COL_COUNT; ++i) set_gpio(col_pins[i], PIN_PD); for (i = 0; i < ROW_COUNT; ++i) mrcr_list[i] = TS_PIN_TO_CR(row_pins[i]); for (i = 0; i < COL_COUNT; ++i) mccr_list[i] = TS_PIN_TO_CR(col_pins[i]); } void touch_scan_enable_interrupt(void) { int i; /* Set ALLROW and ALLCOL */ for (i = 0; i < ROW_COUNT; ++i) set_gpio(row_pins[i], PIN_Z); for (i = 0; i < COL_COUNT; ++i) { set_gpio(col_pins[i], PIN_COL); STM32_PMSE_PxPMR(col_pins[i].port_id) |= 1 << col_pins[i].pin; } STM32_PMSE_MCCR = (1 << 31) | (0 << 20); STM32_PMSE_MRCR = 1 << 31; /* Enable external interrupt. EXTI3 on port E. Rising edge */ STM32_EXTI_RTSR |= 1 << 3; STM32_AFIO_EXTICR(0) = (STM32_AFIO_EXTICR(0) & ~0xf000) | (4 << 12); STM32_EXTI_IMR |= 1 << 3; task_clear_pending_irq(STM32_IRQ_EXTI3); task_enable_irq(STM32_IRQ_EXTI3); } void touch_scan_disable_interrupt(void) { int i; for (i = 0; i < ROW_COUNT; ++i) set_gpio(row_pins[i], PIN_ROW); for (i = 0; i < COL_COUNT; ++i) { set_gpio(col_pins[i], PIN_PD); STM32_PMSE_PxPMR(col_pins[i].port_id) &= ~(1 << col_pins[i].pin); } } void touch_scan_interrupt(void) { STM32_EXTI_PR = STM32_EXTI_PR; } DECLARE_IRQ(STM32_IRQ_EXTI3, touch_scan_interrupt, 1); static void discharge(void) { int i; /* * The value 20 is deducted from experiments. * Somehow this needs to be in reverse order */ for (i = 20; i >= 0; --i) STM32_PMSE_MRCR = mrcr_list[i]; } static void start_adc_sample(int id, int wait_cycle) { /* Clear EOC and STRT bit */ STM32_ADC_SR(id) &= ~((1 << 1) | (1 << 4)); /* Start conversion */ STM32_ADC_CR2(id) |= (1 << 0); /* Wait for conversion start */ while (!(STM32_ADC_SR(id) & (1 << 4))) ; /* Each iteration takes 3 CPU cycles */ asm("1: subs %0, #1\n" " bne 1b\n" :: "r"(wait_cycle / 3)); } static inline uint8_t flush_adc(int id) { uint16_t v; #if ADC_SMPL_CYCLE_2 < ADC_QUNTZ_CYCLE_2 while (!(STM32_ADC_SR(id) & (1 << 1))) ; #endif v = STM32_ADC_DR(id) >> ADC_WINDOW_POS; if (v > 255) v = 255; return v; } static void enable_col(int idx, int enabled) { if (enabled) { set_gpio(col_pins[idx], PIN_COL); STM32_PMSE_PxPMR(col_pins[idx].port_id) |= 1 << col_pins[idx].pin; } else { set_gpio(col_pins[idx], PIN_PD); STM32_PMSE_PxPMR(col_pins[idx].port_id) &= ~(1 << col_pins[idx].pin); } } #ifdef CONFIG_KEYBORG_FAST_SCAN static inline void set_scan_needed(int col) { uint8_t word = (col - COL_SPAN + 32) / 32; uint8_t bit = (col - COL_SPAN + 32) & 31; scan_needed[word] |= SPAN_MASK << bit; if (bit + SPAN_LENGTH > 32) scan_needed[word + 1] |= SPAN_MASK >> (32 - bit); } int fast_scan(uint32_t *data) { int col; int chip_col = 0; memset(data, 0, SCAN_BUF_SIZE * 4); STM32_PMSE_MRCR = 1 << 31; for (col = 0; col < COL_COUNT * 2; ++col) { if (master_slave_is_master()) chip_col = (col >= COL_COUNT) ? col - COL_COUNT : -1; else chip_col = (col < COL_COUNT) ? col : -1; if (chip_col >= 0) { enable_col(chip_col, 1); STM32_PMSE_MCCR = mccr_list[chip_col]; } if (master_slave_sync(5) != EC_SUCCESS) goto fast_scan_err; start_adc_sample(0, ADC_SMPL_CPU_CYCLE); while (!(STM32_ADC_SR(0) & (1 << 1))) ; if (flush_adc(0) >= COL_THRESHOLD) set_scan_needed(col); if (master_slave_sync(5) != EC_SUCCESS) goto fast_scan_err; if (chip_col >= 0) { enable_col(chip_col, 0); STM32_PMSE_MCCR = 0; } } STM32_PMSE_MRCR = 0; /* Discharge the panel */ discharge(); return EC_SUCCESS; fast_scan_err: enable_col(chip_col, 0); STM32_PMSE_MCCR = 0; STM32_PMSE_MRCR = 0; return EC_ERROR_UNKNOWN; } #else #define fast_scan(x) EC_SUCCESS #endif void scan_column(uint8_t *data) { int i; STM32_PMSE_MRCR = mrcr_list[0]; start_adc_sample(0, ADC_SMPL_CPU_CYCLE); STM32_PMSE_MRCR = mrcr_list[1]; start_adc_sample(1, ADC_SMPL_CPU_CYCLE); for (i = 2; i < ROW_COUNT; ++i) { data[i - 2] = flush_adc(i & 1); STM32_PMSE_MRCR = mrcr_list[i]; start_adc_sample(i & 1, ADC_SMPL_CPU_CYCLE); } while (!(STM32_ADC_SR(ROW_COUNT & 1) & (1 << 1))) ; data[ROW_COUNT - 2] = flush_adc(ROW_COUNT & 1); while (!(STM32_ADC_SR((ROW_COUNT & 1) ^ 1) & (1 << 1))) ; data[ROW_COUNT - 1] = flush_adc((ROW_COUNT & 1) ^ 1); } void touch_scan_slave_start(void) { int col = 0, i, v; struct spi_comm_packet *resp = (struct spi_comm_packet *)buf; if (fast_scan(scan_needed) != EC_SUCCESS) goto slave_err; for (col = 0; col < COL_COUNT * 2; ++col) { if (col < COL_COUNT) { enable_col(col, 1); STM32_PMSE_MCCR = mccr_list[col]; } if (master_slave_sync(20) != EC_SUCCESS) goto slave_err; if (GET_SCAN_NEEDED(col)) { scan_column(resp->data); /* Reverse the scanned data */ for (i = 0; ROW_COUNT - 1 - i > i; ++i) { v = resp->data[i]; resp->data[i] = resp->data[ROW_COUNT - 1 - i]; resp->data[ROW_COUNT - 1 - i] = v; } resp->size = ROW_COUNT; } else { resp->size = 0; } resp->cmd_sts = EC_SUCCESS; /* Flush the last response */ if (col != 0) if (spi_slave_send_response_flush() != EC_SUCCESS) goto slave_err; /* Start sending the response for the current column */ if (spi_slave_send_response_async(resp) != EC_SUCCESS) goto slave_err; /* Disable the current column and discharge */ if (col < COL_COUNT) { enable_col(col, 0); STM32_PMSE_MCCR = 0; } discharge(); } spi_slave_send_response_flush(); master_slave_sync(20); return; slave_err: if (col < COL_COUNT) enable_col(col, 0); STM32_PMSE_MCCR = 0; spi_slave_send_response_flush(); } int touch_scan_full_matrix(void) { struct spi_comm_packet cmd; const struct spi_comm_packet *resp; int col = 0; timestamp_t st = get_time(); uint8_t *dptr = NULL, *last_dptr = NULL; cmd.cmd_sts = TS_CMD_FULL_SCAN; cmd.size = 0; if (spi_master_send_command(&cmd)) goto master_err; encode_reset(); if (fast_scan(scan_needed) != EC_SUCCESS) goto master_err; for (col = 0; col < COL_COUNT * 2; ++col) { if (col >= COL_COUNT) { enable_col(col - COL_COUNT, 1); STM32_PMSE_MCCR = mccr_list[col - COL_COUNT]; } if (master_slave_sync(20) != EC_SUCCESS) goto master_err; last_dptr = dptr; dptr = buf[col & 1]; if (GET_SCAN_NEEDED(col)) scan_column(dptr + ROW_COUNT); else memset(dptr + ROW_COUNT, 0, ROW_COUNT); if (col > 0) { /* Flush the data from the slave for the last column */ resp = spi_master_wait_response_done(); if (resp == NULL) goto master_err; if (resp->size) memcpy(last_dptr, resp->data, ROW_COUNT); else memset(last_dptr, 0, ROW_COUNT); encode_add_column(last_dptr); } /* Start receiving data for the current column */ if (spi_master_wait_response_async() != EC_SUCCESS) goto master_err; /* Disable the current column and discharge */ if (col >= COL_COUNT) { enable_col(col - COL_COUNT, 0); STM32_PMSE_MCCR = 0; } discharge(); } resp = spi_master_wait_response_done(); if (resp == NULL) goto master_err; if (resp->size) memcpy(last_dptr, resp->data, ROW_COUNT); else memset(last_dptr, 0, ROW_COUNT); encode_add_column(last_dptr); master_slave_sync(20); debug_printf("Sampling took %d us\n", get_time().val - st.val); encode_dump_matrix(); return EC_SUCCESS; master_err: spi_master_wait_response_done(); if (col >= COL_COUNT) enable_col(col - COL_COUNT, 0); STM32_PMSE_MCCR = 0; return EC_ERROR_UNKNOWN; }