/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2008 by Mark Arigo * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include "config.h" #include "cpu.h" #include "system.h" #include "button.h" #include "kernel.h" #include "backlight.h" #include "backlight-target.h" #include "system.h" #define LOGF_ENABLE #include "logf.h" /* Driver for the Synaptics Touchpad based on the "Synaptics Modular Embedded Protocol: 3-Wire Interface Specification" documentation */ #define ACK (GPIOD_INPUT_VAL & 0x1) #define ACK_HI GPIOD_OUTPUT_VAL |= 0x1 #define ACK_LO GPIOD_OUTPUT_VAL &= ~0x1 #define CLK ((GPIOD_INPUT_VAL & 0x2) >> 1) #define CLK_HI GPIOD_OUTPUT_VAL |= 0x2 #define CLK_LO GPIOD_OUTPUT_VAL &= ~0x2 #define DATA ((GPIOD_INPUT_VAL & 0x4) >> 2) #define DATA_HI GPIOD_OUTPUT_EN |= 0x4; GPIOD_OUTPUT_VAL |= 0x4 #define DATA_LO GPIOD_OUTPUT_EN |= 0x4; GPIOD_OUTPUT_VAL &= ~0x4 #define LO 0 #define HI 1 #define STATUS_READY 1 #define READ_RETRY 8 #define READ_ERROR -1 #define HELLO_HEADER 0x19 #define HELLO_ID 0x1 #define BUTTONS_HEADER 0x1a #define BUTTONS_ID 0x9 #define ABSOLUTE_HEADER 0x0b static int syn_status = 0; static int int_btn = BUTTON_NONE; static int syn_wait_clk_change(unsigned int val) { int i; for (i = 0; i < 10000; i++) { if (CLK == val) return 1; } return 0; } static inline int syn_get_data(void) { GPIOD_OUTPUT_EN &= ~0x4; return DATA; } static void syn_wait_guest_flush(void) { /* Flush receiving (flushee) state: handshake until DATA goes high during P3 stage */ if (CLK == LO) { ACK_HI; /* P1 -> P2 */ syn_wait_clk_change(HI); /* P2 -> P3 */ } while (syn_get_data() == LO) { ACK_HI; /* P3 -> P0 */ syn_wait_clk_change(LO); /* P0 -> P1 */ ACK_LO; /* P1 -> P2 */ syn_wait_clk_change(HI); /* P2 -> P3 */ } /* Continue handshaking until back to P0 */ ACK_HI; /* P3 -> P0 */ } static void syn_flush(void) { int i; logf("syn_flush..."); /* Flusher holds DATA low for at least 36 handshake cycles */ DATA_LO; for (i = 0; i < 36; i++) { syn_wait_clk_change(LO); /* P0 -> P1 */ ACK_LO; /* P1 -> P2 */ syn_wait_clk_change(HI); /* P2 -> P3 */ ACK_HI; /* P3 -> P0 */ } /* Raise DATA in P1 stage */ syn_wait_clk_change(LO); /* P0 -> P1 */ DATA_HI; /* After a flush, the flushing device enters a flush-receiving (flushee) state */ syn_wait_guest_flush(); } static int syn_send_data(int *data, int len) { int i, bit; int parity = 0; logf("syn_send_data..."); /* 1. Lower DATA line to issue a request-to-send to guest */ DATA_LO; /* 2. Wait for guest to lower CLK */ syn_wait_clk_change(LO); /* 3. Lower ACK (with DATA still low) */ ACK_LO; /* 4. Wait for guest to raise CLK */ syn_wait_clk_change(HI); /* 5. Send data */ for (i = 0; i < len; i++) { logf(" sending byte: %d", data[i]); bit = 0; while (bit < 8) { /* 5a. Drive data low if bit is 0, or high if bit is 1 */ if (data[i] & (1 << bit)) { DATA_HI; parity++; } else { DATA_LO; } bit++; /* 5b. Invert ACK to indicate that the data bit is ready */ ACK_HI; /* 5c. Wait for guest to invert CLK */ syn_wait_clk_change(LO); /* Repeat for next bit */ if (data[i] & (1 << bit)) { DATA_HI; parity++; } else { DATA_LO; } bit++; ACK_LO; syn_wait_clk_change(HI); } } /* 7. Transmission termination sequence: */ /* 7a. Host may put parity bit on DATA. Hosts that do not generate parity should set DATA high. Parity is 1 if there's an odd number of '1' bits, or 0 if there's an even number of '1' bits. */ parity = parity % 2; logf(" send parity = %d", parity); if (parity) { DATA_HI; } else { DATA_LO; } /* 7b. Raise ACK to indicate that the optional parity bit is ready */ ACK_HI; /* 7c. Guest lowers CLK */ syn_wait_clk_change(LO); /* 7d. Pull DATA high (if parity bit was 0) */ DATA_HI; /* 7e. Lower ACK to indicate that the stop bit is ready */ ACK_LO; /* 7f. Guest raises CLK */ syn_wait_clk_change(HI); /* 7g. If DATA is low, guest is flushing this transfer. Host should enter the flushee state. */ if (syn_get_data() == LO) { logf(" module flushing"); syn_wait_guest_flush(); return -1; } /* 7h. Host raises ACK and the link enters the idle state */ ACK_HI; return len; } static int syn_read_data(int *data, int data_len) { int i, len, bit, parity, tmp; int *data_ptr; logf("syn_read_data..."); /* 1. Guest drives CLK low */ if (CLK != LO) return 0; /* 1a. If the host is willing to receive a packet it lowers ACK */ ACK_LO; /* 2. Guest may issue a request-to-send by lowering DATA. If the guest decides not to transmit a packet, it may abort the transmission by not lowering DATA. */ /* 3. The guest raises CLK */ syn_wait_clk_change(HI); /* 4. If the guest is still driving DATA low, the transfer is commited to occur. Otherwise, the transfer is aborted. In either case, the host raises ACK. */ if (syn_get_data() == HI) { logf(" read abort"); ACK_HI; return READ_ERROR; } else { ACK_HI; } /* 5. Read the incoming data packet */ i = 0; len = 0; parity = 0; while (i <= len) { bit = 0; if (i < data_len) data_ptr = &data[i]; else data_ptr = &tmp; *data_ptr = 0; while (bit < 8) { /* 5b. Guset inverts CLK to indicate that data is ready */ syn_wait_clk_change(LO); /* 5d. Read the data bit from DATA */ if (syn_get_data() == HI) { *data_ptr |= (1 << bit); parity++; } bit++; /* 5e. Invert ACK to indicate that data has been read */ ACK_LO; /* Repeat for next bit */ syn_wait_clk_change(HI); if (syn_get_data() == HI) { *data_ptr |= (1 << bit); parity++; } bit++; ACK_HI; } /* First byte is the packet header */ if (i == 0) { /* Format control (bit 3) should be 1 */ if (*data_ptr & 0x8) { /* Packet length is bits 0:2 */ len = *data_ptr & 0x7; logf(" packet length = %d", len); } else { logf(" invalid format ctrl bit"); return READ_ERROR; } } i++; } /* 7. Transmission termination cycle */ /* 7a. The guest generates a parity bit on DATA */ /* 7b. The host waits for guest to lower CLK */ syn_wait_clk_change(LO); /* 7c. The host verifies the parity bit is correct */ parity = parity % 2; logf(" parity check: %d / %d", syn_get_data(), parity); /* TODO: parity error handling */ /* 7d. The host lowers ACK */ ACK_LO; /* 7e. The host waits for the guest to raise CLK indicating that the stop bit is ready */ syn_wait_clk_change(HI); /* 7f. The host reads DATA and verifies that it is 1 */ if (syn_get_data() == LO) { logf(" framing error"); ACK_HI; return READ_ERROR; } ACK_HI; return len; } static int syn_read_device(int *data, int len) { int i; int ret = READ_ERROR; for (i = 0; i < READ_RETRY; i++) { if (syn_wait_clk_change(LO)) { /* module is sending data */ ret = syn_read_data(data, len); if (ret != READ_ERROR) return ret; syn_flush(); } else { /* module is idle */ return 0; } } return ret; } static int syn_reset(void) { int val, id; int data[2]; logf("syn_reset..."); /* reset module 0 */ val = (0 << 4) | (1 << 3) | 0; syn_send_data(&val, 1); val = syn_read_device(data, 2); if (val == 1) { val = data[0] & 0xff; /* packet header */ id = (data[1] >> 4) & 0xf; /* packet id */ if ((val == HELLO_HEADER) && (id == HELLO_ID)) { logf(" module 0 reset"); return 1; } } logf(" reset failed"); return 0; } #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) static void syn_info(void) { int i, val; int data[8]; logf("syn_info..."); /* module base info */ logf("module base info:"); data[0] = (0 << 4) | (0 << 3) | 1; data[1] = 0x80; syn_send_data(data, 2); val = syn_read_device(data, 8); if (val > 0) { for (i = 0; i < 8; i++) logf(" data[%d] = 0x%02x", i, data[i]); } /* module product info */ logf("module product info:"); data[0] = (0 << 4) | (0 << 3) | 1; data[1] = 0x81; syn_send_data(data, 2); val = syn_read_device(data, 8); if (val > 0) { for (i = 0; i < 8; i++) logf(" data[%d] = 0x%02x", i, data[i]); } /* module serialization */ logf("module serialization:"); data[0] = (0 << 4) | (0 << 3) | 1; data[1] = 0x82; syn_send_data(data, 2); val = syn_read_device(data, 8); if (val > 0) { for (i = 0; i < 8; i++) logf(" data[%d] = 0x%02x", i, data[i]); } /* 1-D sensor info */ logf("1-d sensor info:"); data[0] = (0 << 4) | (0 << 3) | 1; data[1] = 0x80 + 0x20; syn_send_data(data, 2); val = syn_read_device(data, 8); if (val > 0) { for (i = 0; i < 8; i++) logf(" data[%d] = 0x%02x", i, data[i]); } } #endif void button_init_device(void) { /* enable touchpad leds */ GPIOA_ENABLE |= BUTTONLIGHT_ALL; GPIOA_OUTPUT_EN |= BUTTONLIGHT_ALL; /* enable touchpad */ GPO32_ENABLE |= 0x40000000; GPO32_VAL &= ~0x40000000; /* enable ACK, CLK, DATA lines */ GPIOD_ENABLE |= (0x1 | 0x2 | 0x4); GPIOD_OUTPUT_EN |= 0x1; /* ACK */ GPIOD_OUTPUT_VAL |= 0x1; /* high */ GPIOD_OUTPUT_EN &= ~0x2; /* CLK */ GPIOD_OUTPUT_EN |= 0x4; /* DATA */ GPIOD_OUTPUT_VAL |= 0x4; /* high */ syn_flush(); if (syn_reset()) { #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) syn_info(); #endif syn_status = STATUS_READY; /* enable interrupts */ GPIOD_INT_LEV &= ~0x2; GPIOD_INT_CLR |= 0x2; GPIOD_INT_EN |= 0x2; CPU_INT_EN |= HI_MASK; CPU_HI_INT_EN |= GPIO0_MASK; } } /* * Button interrupt handler */ void button_int(void) { int data[4]; int val, id; int_btn = BUTTON_NONE; if (syn_status == STATUS_READY) { /* disable interrupt while we read the touchpad */ GPIOD_INT_EN &= ~0x2; val = syn_read_device(data, 4); if (val > 0) { val = data[0] & 0xff; /* packet header */ id = (data[1] >> 4) & 0xf; /* packet id */ logf("button_read_device..."); logf(" data[0] = 0x%08x", data[0]); logf(" data[1] = 0x%08x", data[1]); logf(" data[2] = 0x%08x", data[2]); logf(" data[3] = 0x%08x", data[3]); if ((val == BUTTONS_HEADER) && (id == BUTTONS_ID)) { /* Buttons packet - touched one of the 5 "buttons" */ if (data[1] & 0x1) int_btn |= BUTTON_PLAY; if (data[1] & 0x2) int_btn |= BUTTON_MENU; if (data[1] & 0x4) int_btn |= BUTTON_LEFT; if (data[1] & 0x8) int_btn |= BUTTON_DISPLAY; if (data[2] & 0x1) int_btn |= BUTTON_RIGHT; /* An Absolute packet should follow which we ignore */ val = syn_read_device(data, 4); logf(" int_btn = 0x%04x", int_btn); } else if (val == ABSOLUTE_HEADER) { /* Absolute packet - the finger is on the vertical strip. Position ranges from 1-4095, with 1 at the bottom. */ val = ((data[1] >> 4) << 8) | data[2]; /* position */ if ((val > 0) && (val <= 1365)) int_btn |= BUTTON_DOWN; else if ((val > 1365) && (val <= 2730)) int_btn |= BUTTON_SELECT; else if ((val > 2730) && (val <= 4095)) int_btn |= BUTTON_UP; } } /* re-enable interrupts */ GPIOD_INT_LEV &= ~0x2; GPIOD_INT_CLR |= 0x2; GPIOD_INT_EN |= 0x2; } } /* * Get button pressed from hardware */ int button_read_device(void) { int btn = int_btn; if (~GPIOA_INPUT_VAL & 0x40) btn |= BUTTON_POWER; return btn; } bool button_hold(void) { return (GPIOD_INPUT_VAL & 0x10) ? false : true; } bool headphones_inserted(void) { return (GPIOD_INPUT_VAL & 0x80) ? true : false; }