From 223cd288c5903d9378e1ec2411d0930c359336b1 Mon Sep 17 00:00:00 2001 From: leviport Date: Fri, 4 Nov 2022 11:01:34 -0600 Subject: [PATCH] Heavy (#30) * Add launch_heavy_1 * Gets basic keymap functionality working on launch_heavy_1 LEDs are not working yet. * Fix mapping of column M on the tenkey * Fix launch heavy LEDS * Increment PRODUCT_ID to 0007 * launch_heavy_1: Update `LAYOUT` define to what configurator expects `keyboard-configurator/layouts.py` relies on the naming here to follow this convention. It shouldn't impact behavior. * Add Levi's Heavy layout * Align keyboard led to keyswitch map Followed argb trace for 10 key from kicad file * Fix spacing * Increase EEPROM size to 2048 for Heavy Ok because our new EC has double the EEPROM of previous launch keyboards https://www.microchip.com/en-us/product/AT90USB646 Co-authored-by: 13r0ck Co-authored-by: Ian Douglas Scott --- keyboards/system76/launch_heavy_1/README.md | 40 ++ keyboards/system76/launch_heavy_1/config.h | 95 ++++ keyboards/system76/launch_heavy_1/i2c.c | 176 +++++++ keyboards/system76/launch_heavy_1/i2c.h | 46 ++ .../launch_heavy_1/keymaps/default/keymap.c | 90 ++++ .../launch_heavy_1/keymaps/levi/keymap.c | 112 +++++ .../system76/launch_heavy_1/launch_heavy_1.c | 271 +++++++++++ .../system76/launch_heavy_1/launch_heavy_1.h | 30 ++ .../system76/launch_heavy_1/rgb_matrix_kb.inc | 142 ++++++ keyboards/system76/launch_heavy_1/rules.mk | 47 ++ keyboards/system76/launch_heavy_1/usb_mux.c | 458 ++++++++++++++++++ keyboards/system76/launch_heavy_1/usb_mux.h | 7 + 12 files changed, 1514 insertions(+) create mode 100644 keyboards/system76/launch_heavy_1/README.md create mode 100644 keyboards/system76/launch_heavy_1/config.h create mode 100644 keyboards/system76/launch_heavy_1/i2c.c create mode 100644 keyboards/system76/launch_heavy_1/i2c.h create mode 100644 keyboards/system76/launch_heavy_1/keymaps/default/keymap.c create mode 100644 keyboards/system76/launch_heavy_1/keymaps/levi/keymap.c create mode 100644 keyboards/system76/launch_heavy_1/launch_heavy_1.c create mode 100644 keyboards/system76/launch_heavy_1/launch_heavy_1.h create mode 100644 keyboards/system76/launch_heavy_1/rgb_matrix_kb.inc create mode 100644 keyboards/system76/launch_heavy_1/rules.mk create mode 100644 keyboards/system76/launch_heavy_1/usb_mux.c create mode 100644 keyboards/system76/launch_heavy_1/usb_mux.h diff --git a/keyboards/system76/launch_heavy_1/README.md b/keyboards/system76/launch_heavy_1/README.md new file mode 100644 index 000000000000..8ad57e3ff41c --- /dev/null +++ b/keyboards/system76/launch_heavy_1/README.md @@ -0,0 +1,40 @@ +## Flashing firmware: +* Clone this repository and `cd` into the `qmk_firmware` directory. +* After cloning, you probably need to run `make git-submodule` as well as `./util/qmk_install`. + - You may also need to install dependencies: `sudo apt install avrdude gcc-avr avr-libc` +* To build the firmware without flashing the keyboard, use `make (keyboard name):(layout name)` + - For example, if you want to build the `default` layout for the Launch keyboard, run: +``` +make system76/launch_heavy_1:default +``` +* To flash the firmware, you'll use the same build command, but with `dfu` added to the end: +``` +make system76/launch_heavy_1:default:dfu +``` + - After it builds, you will see a repeating message that says: +``` +dfu-programmer: no device present. +ERROR: Bootloader not found. Trying again in 5s. +``` +Next, unplug your keyboard from your computer, hold the ESC key (while the keyboard is unplugged), and plug the keyboard back in while holding the ESC key. Once the keyboard is plugged in, the ESC key can be released. +* Note: on some distros, dfu-programmer has trouble detecting the keyboard unless you run the `make` command with `sudo`. +* To flash the firmware using ISP, you will need a USBasp device, and a tag connect cable. + - Build the firmware and bootloader with: +``` +make system76/launch_heavy_1:default:production +``` + - Run avrdude to flash the fuses: +``` +avrdude -c usbasp -p at90usb646 -U lfuse:w:0x5E:m -U hfuse:w:0xDB:m -U efuse:w:0xFB:m -U lock:w:0xFF:m +``` + - Run avrdude to flash the ROM: +``` +avrdude -c usbasp -p at90usb646 -U flash:w:system76_launch_heavy_1_default_production.hex +``` + +## Making your own layout: +If you want to create your own layout, go to the `keymaps` directory and copy one of the maps in there. It will likely be easiest to start with the default layout, but the other layouts in there may be helpful references. The name of the directory you create will be the name of your layout. To prevent build errors, it is recommended to use only lowercase letters, underscores, and numbers for the name of your layout. + +Inside of each layout directory, there is a file called `keymap.c`. The commented out grid area in this file is a visual reference for the actual key assignments below it. When modifying a layout, modifying this visual reference first makes it easier to design a layout, as well as keeping the actual layout below it organized. + +Once your layout is designed, change the keycodes below to match your design. A full list of available keycodes can be found in the [QMK docs](https://beta.docs.qmk.fm/reference/keycodes). Use the shorter keycode alias to help keep these lined up (e.g. use `KC_ESC` instead of `KC_ESCAPE`). diff --git a/keyboards/system76/launch_heavy_1/config.h b/keyboards/system76/launch_heavy_1/config.h new file mode 100644 index 000000000000..cbca03f0ad0f --- /dev/null +++ b/keyboards/system76/launch_heavy_1/config.h @@ -0,0 +1,95 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include "config_common.h" + +/* USB Device descriptor parameter */ +#define VENDOR_ID 0x3384 +#define PRODUCT_ID 0x0007 +#define DEVICE_VER 0x0001 +#define MANUFACTURER System76 +#define PRODUCT Launch Heavy Configurable Keyboard (launch_heavy_1) +#define DESCRIPTION Launch Heavy Configurable Keyboard (launch_heavy_1) + +/* key matrix size */ +#define MATRIX_ROWS 12 +#define MATRIX_COLS 14 + +/* key matrix pins */ +#define MATRIX_ROW_PINS { F0, F1, F2, F3, F4, F5, D2, D4, D5, D6, D7, E0 } +#define MATRIX_COL_PINS { C6, C5, C4, C3, C2, C1, C7, A7, A6, A5, A4, E6, C0, E7 } +#define UNUSED_PINS + +/* COL2ROW or ROW2COL */ +#define DIODE_DIRECTION COL2ROW + +/* Set 0 if debouncing isn't needed */ +#define DEBOUNCE 5 + +// NKRO must be used +#define FORCE_NKRO + +#if RGBLIGHT_ENABLE + #define RGB_DI_PIN F7 + #define RGBLED_NUM 70 + #define RGBLIGHT_ANIMATIONS + // Limit brightness to support USB-A at 0.5A + //TODO: do this dynamically based on power source + #define RGBLIGHT_LIMIT_VAL 176 +#endif + +#if RGB_MATRIX_ENABLE + #define RGB_DI_PIN F7 + #define DRIVER_LED_TOTAL 105 + #define RGB_MATRIX_KEYPRESSES // reacts to keypresses + //#define RGB_MATRIX_KEYRELEASES // reacts to keyreleases (instead of keypresses) + //#define RGB_MATRIX_FRAMEBUFFER_EFFECTS + #define RGB_DISABLE_TIMEOUT 0 // number of milliseconds to wait until rgb automatically turns off + #define RGB_DISABLE_AFTER_TIMEOUT 0 // OBSOLETE: number of ticks to wait until disabling effects + #define RGB_DISABLE_WHEN_USB_SUSPENDED true // turn off effects when suspended + #define RGB_MATRIX_MAXIMUM_BRIGHTNESS 176 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255 + #define RGB_MATRIX_STARTUP_MODE RGB_MATRIX_RAINBOW_MOVING_CHEVRON // Sets the default mode, if none has been set + #define RGB_MATRIX_STARTUP_HUE 142 // 200 degrees + #define RGB_MATRIX_STARTUP_SAT 255 + #define RGB_MATRIX_STARTUP_VAL RGB_MATRIX_MAXIMUM_BRIGHTNESS + #define RGB_MATRIX_STARTUP_SPD 127 + #define RGB_MATRIX_DISABLE_KEYCODES // disables control of rgb matrix by keycodes (must use code functions to control the feature) +#endif + +/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */ +#define LOCKING_SUPPORT_ENABLE + +/* Locking resynchronize hack */ +#define LOCKING_RESYNC_ENABLE + +// EEPROM { +#define EEPROM_SIZE 2048 + +// TODO: refactor with new user EEPROM code (coming soon) +#define EEPROM_MAGIC 0x76EC +#define EEPROM_MAGIC_ADDR 64 +// Bump this every time we change what we store +// This will automatically reset the EEPROM with defaults +// and avoid loading invalid data from the EEPROM +#define EEPROM_VERSION 0x02 +#define EEPROM_VERSION_ADDR (EEPROM_MAGIC_ADDR + 2) +// } EEPROM + +// Dynamic keyboard support { +#define DYNAMIC_KEYMAP_LAYER_COUNT 4 +// Dynamic keymap starts after EEPROM version +#define DYNAMIC_KEYMAP_EEPROM_ADDR (EEPROM_VERSION_ADDR + 1) +// `* 2` Beacuse each key needs 16 bits (2 bytes) for storing the keycode +#define DYNAMIC_KEYMAP_EEPROM_SIZE (DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2) +// Dynamic macro starts after dynamic keymaps, it is disabled +#define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_EEPROM_ADDR + DYNAMIC_KEYMAP_EEPROM_SIZE) +#define DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE 0 +#define DYNAMIC_KEYMAP_MACRO_COUNT 0 +// } Dynamic keyboard support + +// System76 EC { +#define SYSTEM76_EC_EEPROM_ADDR (DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) +#define SYSTEM76_EC_EEPROM_SIZE (EEPROM_SIZE - SYSTEM76_EC_EEPROM_ADDR) +// } System76 EC + +#endif // CONFIG_H diff --git a/keyboards/system76/launch_heavy_1/i2c.c b/keyboards/system76/launch_heavy_1/i2c.c new file mode 100644 index 000000000000..a46927fa59e2 --- /dev/null +++ b/keyboards/system76/launch_heavy_1/i2c.c @@ -0,0 +1,176 @@ +#include +#include +#include + +#include "i2c.h" + +#define TIMEOUT (F_CPU/1000) + +// Initialize I2C with specified buad rate +void i2c_init(unsigned long baud) { + TWAR = 0; + TWBR = (uint8_t)(((F_CPU / baud) - 16 ) / 2); + TWCR = 0; +} + +// Send an I2C start condition, a 7-bit address, and a read bit +// Returns zero on success or negative number on error +int i2c_start(uint8_t addr, bool read) { + uint32_t count; + uint8_t twst; + + // transmit START condition + TWCR = (1< 0) count -= 1; + if (count == 0) return -1; + + // check if the start condition was successfully transmitted + twst = TW_STATUS & 0xF8; + if ((twst != TW_START) && (twst != TW_REP_START)) return -1; + + // load slave addr into data register + TWDR = ((addr << 1) | read); + // start transmission of addr + TWCR = (1< 0) count -= 1; + if (count == 0) return -1; + + // check if the device has acknowledged the READ / WRITE mode + twst = TW_STATUS & 0xF8; + if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK)) return -1; + + return 0; +} + +// Send an I2C stop condition +// Always successful +void i2c_stop(void) { + // transmit STOP condition + TWCR = (1< 0) count -= 1; + // timed out + if (count == 0) return -1; + // failed to receive ack + if((TWSR & 0xF8) != TW_MT_DATA_ACK) return -1; + } + + return i; +} + +// Read a byte from the I2C bus, sending an ack if specified +// Returns byte data on success or negative number on error +int i2c_read_byte(bool ack) { + if (ack) { + // start TWI module and acknowledge data after reception + TWCR = (1< 0) count -= 1; + if (count == 0) return -1; + // return received data from TWDR + return (int)TWDR; +} + +// Read data from the I2C bus +// Returns bytes read on success or negative number on error +int i2c_read(uint8_t * data, int length) { + int res; + int i; + + for (i = 0; i < length; i++) { + bool ack = (i + 1) < length; + res = i2c_read_byte(ack); + if (res < 0) return res; + data[i] = (uint8_t)res; + } + + return i; +} + +// Receive data from a specified address on the I2C bus +// Returns bytes read on success or negative number on error +int i2c_recv(uint8_t addr, uint8_t* data, int length) { + int res = 0; + + res = i2c_start(addr, true); + if (res < 0) return res; + + res = i2c_read(data, length); + if (res < 0) return res; + + i2c_stop(); + + return res; +} + +// Send data to a specified address on the I2C bus +// Returns bytes written on success or negative number on error +int i2c_send(uint8_t addr, uint8_t* data, int length) { + int res = 0; + + res = i2c_start(addr, false); + if (res < 0) return res; + + res = i2c_write(data, length); + if (res < 0) return res; + + i2c_stop(); + + return res; +} + +// Get data from a specified address and register on the I2C bus +// Returns bytes read on success or negative number on error +int i2c_get(uint8_t addr, uint8_t reg, uint8_t* data, int length) { + int res = 0; + + res = i2c_start(addr, false); + if (res < 0) return res; + + res = i2c_write(®, 1); + if (res < 0) return res; + + return i2c_recv(addr, data, length); +} + +// Set data in a specified address and register on the I2C bus +// Returns bytes written on success or negative number on error +int i2c_set(uint8_t addr, uint8_t reg, uint8_t* data, int length) { + int res = 0; + + res = i2c_start(addr, false); + if (res < 0) return res; + + res = i2c_write(®, 1); + if (res < 0) return res; + + res = i2c_write(data, length); + if (res < 0) return res; + + i2c_stop(); + + return res; +} diff --git a/keyboards/system76/launch_heavy_1/i2c.h b/keyboards/system76/launch_heavy_1/i2c.h new file mode 100644 index 000000000000..f980a308dced --- /dev/null +++ b/keyboards/system76/launch_heavy_1/i2c.h @@ -0,0 +1,46 @@ +#ifndef I2C_H +#define I2C_H + +#include +#include + +// Initialize I2C with specified buad rate +void i2c_init(unsigned long baud); + +// Send an I2C start condition, a 7-bit address, and a read bit +// Returns zero on success or negative number on error +int i2c_start(uint8_t addr, bool read); + +// Send an I2C stop condition +// Always successful +void i2c_stop(void); + +// Write data to the I2C bus +// Returns bytes written on success or negative number on error +int i2c_write(uint8_t * data, int length); + +// Read a byte from the I2C bus, sending an ack if specified +// Returns byte data on success or negative number on error +int i2c_read_byte(bool ack); + +// Read data from the I2C bus +// Returns bytes read on success or negative number on error +int i2c_read(uint8_t * data, int length); + +// Receive data from a specified address on the I2C bus +// Returns bytes read on success or negative number on error +int i2c_recv(uint8_t addr, uint8_t* data, int length); + +// Send data to a specified address on the I2C bus +// Returns bytes written on success or negative number on error +int i2c_send(uint8_t addr, uint8_t* data, int length); + +// Get data from a specified address and register on the I2C bus +// Returns bytes read on success or negative number on error +int i2c_get(uint8_t addr, uint8_t reg, uint8_t* data, int length); + +// Set data in a specified address and register on the I2C bus +// Returns bytes written on success or negative number on error +int i2c_set(uint8_t addr, uint8_t reg, uint8_t* data, int length); + +#endif // I2C_H diff --git a/keyboards/system76/launch_heavy_1/keymaps/default/keymap.c b/keyboards/system76/launch_heavy_1/keymaps/default/keymap.c new file mode 100644 index 000000000000..5a89eca57788 --- /dev/null +++ b/keyboards/system76/launch_heavy_1/keymaps/default/keymap.c @@ -0,0 +1,90 @@ +#include QMK_KEYBOARD_H + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + + /* Layer 0, default layer +__________________________________________________________________________________________________________________________________ ________ ___________________________________ +| | | | | | | | | | | | | | || || PRINT | MEDIA | PLAY/ | MEDIA | +| ESC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | DELETE || HOME || SCREEN |PREVIOUS| PAUSE | NEXT | +|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________||________|________|________|________| +| | | | | | | | | | | | | | || || NUM | NUM | NUM | NUM | +| ~` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | _ - | = + | BACKSPACE || PGUP || LOCK | SLASH | * | - | +|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________||________|________|________|________| +| | | | | | | | | | | | [ | ] | || || NUM | NUM | NUM | | +| TAB | Q | W | E | R | T | Y | U | I | O | P | { | } | | \ || PGDN || 7 | 8 | 9 | | +|____________|________|________|________|________|________|________|________|________|________|________|________|________|________||________||________|________|________| NUM | + | | | | | | | | | | | ; | ' | | | || NUM | NUM | NUM | + | + | CAPS | A | S | D | F | G | H | J | K | L | : | " | ENTER | | END || 4 | 5 | 6 | | + |____________|________|________|________|________|________|________|________|________|________|________|________|____________|___|________||________|________|________|________| + | | | | | | | | | , | . | / | | | | NUM | NUM | NUM | | + | SHIFT | Z | X | C | V | B | N | M | < | > | ? | SHIFT | UP | | 1 | 2 | 3 | | + |________________|________|________|________|________|________|________|________|________|________|________|____________|________|________ |________|________|________| NUM | + | | | | | | | | | | | | | || NUM | NUM | ENTER | + | CTRL | LALT | FN | LGUI | SPACE | SPACE | RCTRL | RALT | FN | | LEFT | DOWN | RIGHT || 0 | . | | + |____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________||_________________|________|________| +*/ + + [0] = LAYOUT( + KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_DEL, KC_HOME, KC_PSCR, KC_MPRV, KC_MPLY, KC_MNXT, + KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_PGUP, KC_NLCK, KC_PSLS, KC_PAST, KC_PMNS, + KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGDN, KC_P7, KC_P8, KC_P9, KC_PPLS, + KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_END, KC_P4, KC_P5, KC_P6, + KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3, KC_PENT, + KC_LCTL, KC_LALT, MO(1), KC_LGUI, KC_SPC, KC_SPC, KC_RCTL, KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT + ), + + /* Layer 1, function layer +__________________________________________________________________________________________________________________________________ ________ ___________________________________ +| | | | | | | | | | | | | | || PLAY/ || | | | | +| RESET | | | | | | | | | | | | | || PAUSE || | | | | +|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________||________|________|________|________| +| | | | | | | | | | | LED | LED | LED | || VOLUME || | | | | +| | | | | | | | | | | TOGGLE | DOWN | UP | || UP || | | | | +|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________||________|________|________|________| +| | | | | | | | | | | | | | || VOLUME || | | | | +|PRINT SCREEN| | | | | | HOME | PGDN | PGUP | END | | | | || DOWN || | | | | +|____________|________|________|________|________|________|________|________|________|________|________|________|________|________||________||________|________|________| | + | | | | | | | | | | | | | | | || | | | | + | | | | | | | LEFT | DOWN | UP | RIGHT | | | | | MUTE || | | | | + |____________|________|________|________|________|________|________|________|________|________|________|________|____________|___|________||________|________|________|________| + | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | PGUP | | | | | | + |________________|________|________|________|________|________|________|________|________|________|________|____________|________|________ |________|________|________| | + | | | | | | | | | | | | | || | | | + | | | | | | | | | | | HOME | PGDN | END || | | | + |____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________||_________________|________|________| + * 'RESET' resets the controller and puts the board into firmware flashing mode. If this key is hit accidentally, just unplug the board + * and plug it back in. + */ + + [1] = LAYOUT( + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MPLY, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, RGB_TOG, RGB_VAD, RGB_VAI, KC_TRNS, KC_VOLU, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_PSCR, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_HOME, KC_PGDN, KC_PGUP, KC_END, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_VOLD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, KC_TRNS, KC_TRNS, KC_TRNS, KC_MUTE, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS + ), + + [2] = LAYOUT( + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS + ), + + [3] = LAYOUT( + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS + ), +}; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + return true; +} diff --git a/keyboards/system76/launch_heavy_1/keymaps/levi/keymap.c b/keyboards/system76/launch_heavy_1/keymaps/levi/keymap.c new file mode 100644 index 000000000000..1824581a352a --- /dev/null +++ b/keyboards/system76/launch_heavy_1/keymaps/levi/keymap.c @@ -0,0 +1,112 @@ +#include QMK_KEYBOARD_H + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + + /* Layer 0, Dvorak +__________________________________________________________________________________________________________________________________ ________ ___________________________________ +| | | | | | | | | | | | | | || || PRINT | MEDIA | PLAY/ | MEDIA | +| ESC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | DELETE || HOME || SCREEN |PREVIOUS| PAUSE | NEXT | +|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________||________|________|________|________| +| | | | | | | | | | | | [ | ] | || || NUM | NUM | NUM | NUM | +| ~` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | { | } | BACKSPACE || PGUP || LOCK | SLASH | * | - | +|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________||________|________|________|________| +| | ' | , | . | | | | | | | | / | = | || || NUM | NUM | NUM | | +| TAB | " | < | > | P | Y | F | G | C | R | L | ? | + | | \ || PGDN || 7 | 8 | 9 | | +|____________|________|________|________|________|________|________|________|________|________|________|________|________|________||________||________|________|________| NUM | + | | | | | | | | | | | | - | | | || NUM | NUM | NUM | + | + | LCTL | A | O | E | U | I | D | H | T | N | S | _ | ENTER | | END || 4 | 5 | 6 | | + |____________|________|________|________|________|________|________|________|________|________|________|________|____________|___|________||________|________|________|________| + | | ; | | | | | | | | | | | | | NUM | NUM | NUM | | + | SHIFT | : | Q | J | K | X | B | M | W | V | Z | SHIFT | UP | | 1 | 2 | 3 | | + |________________|________|________|________|________|________|________|________|________|________|________|____________|________|________ |________|________|________| NUM | + | | | | | | | | | | | | | || NUM | NUM | ENTER | + | FN | UNDO | LALT | LGUI | SPACE | FN | RCTL | RALT | FN | | LEFT | DOWN | RIGHT || 0 | . | | + |____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________||_________________|________|________| +*/ + + [0] = LAYOUT( + KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_DEL, KC_HOME, KC_PSCR, KC_MPRV, KC_MPLY, KC_MNXT, + KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_LBRC, KC_RBRC, KC_BSPC, KC_PGUP, KC_NLCK, KC_PSLS, KC_PAST, KC_PMNS, + KC_TAB, KC_QUOT, KC_COMM, KC_DOT, KC_P, KC_Y, KC_F, KC_G, KC_C, KC_R, KC_L, KC_SLSH, KC_EQL, KC_BSLS, KC_PGDN, KC_P7, KC_P8, KC_P9, KC_PPLS, + KC_LCTL, KC_A, KC_O, KC_E, KC_U, KC_I, KC_D, KC_H, KC_T, KC_N, KC_S, KC_MINS, KC_ENT, KC_END, KC_P4, KC_P5, KC_P6, + KC_LSFT, KC_SCLN, KC_Q, KC_J, KC_K, KC_X, KC_B, KC_M, KC_W, KC_V, KC_Z, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3, KC_PENT, + MO(2), C(KC_Z), KC_LALT, KC_LGUI, KC_SPC, MO(2), KC_RCTL, KC_RALT, MO(2), KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT + ), + + /* Layer 1, QWERTY +__________________________________________________________________________________________________________________________________ ________ ___________________________________ +| | | | | | | | | | | | | | || || PRINT | MEDIA | PLAY/ | MEDIA | +| ESC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | DELETE || HOME || SCREEN |PREVIOUS| PAUSE | NEXT | +|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________||________|________|________|________| +| | | | | | | | | | | | | | || || NUM | NUM | NUM | NUM | +| ~` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | _ - | = + | BACKSPACE || PGUP || LOCK | SLASH | * | - | +|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________||________|________|________|________| +| | | | | | | | | | | | [ | ] | || || NUM | NUM | NUM | | +| TAB | Q | W | E | R | T | Y | U | I | O | P | { | } | | \ || PGDN || 7 | 8 | 9 | | +|____________|________|________|________|________|________|________|________|________|________|________|________|________|________||________||________|________|________| NUM | + | | | | | | | | | | | ; | ' | | | || NUM | NUM | NUM | + | + | LCTL | A | S | D | F | G | H | J | K | L | : | " | ENTER | | END || 4 | 5 | 6 | | + |____________|________|________|________|________|________|________|________|________|________|________|________|____________|___|________||________|________|________|________| + | | | | | | | | | , | . | / | | | | NUM | NUM | NUM | | + | SHIFT | Z | X | C | V | B | N | M | < | > | ? | SHIFT | UP | | 1 | 2 | 3 | | + |________________|________|________|________|________|________|________|________|________|________|________|____________|________|________ |________|________|________| NUM | + | | | | | | | | | | | | | || NUM | NUM | ENTER | + | FN | UNDO | LALT | LGUI | SPACE | FN | RCTRL | RALT | FN | | LEFT | DOWN | RIGHT || 0 | . | | + |____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________||_________________|________|________| +*/ + + [1] = LAYOUT( + KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_DEL, KC_HOME, KC_PSCR, KC_MPRV, KC_MPLY, KC_MNXT, + KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_PGUP, KC_NLCK, KC_PSLS, KC_PAST, KC_PMNS, + KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGDN, KC_P7, KC_P8, KC_P9, KC_PPLS, + KC_LCTL, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_END, KC_P4, KC_P5, KC_P6, + KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3, KC_PENT, + MO(2), C(KC_Z), KC_LALT, KC_LGUI, KC_SPC, MO(2), KC_RCTL, KC_RALT, MO(2), KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT + ), + + /* Layer 2, function layer +__________________________________________________________________________________________________________________________________ ________ ___________________________________ +| | | | | | | | | | | | | | || DVORAK || | | | | +| RESET | | | | | | | | | | | | | || || | | | | +|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________||________|________|________|________| +| | | | | | | | | | | LED | LED | LED | || QWERTY || | | | | +| | | | | | | | | | | TOGGLE | DOWN | UP | || || | | | | +|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________||________|________|________|________| +| | | | | | | | | | | | | | || || | | | | +| | HOME | UP | END | PGUP | | PGUP | HOME | UP | END | | | | PRTSC || || | | | | +|____________|________|________|________|________|________|________|________|________|________|________|________|________|________||________||________|________|________| VOLUP | + | | | | | | | | | | | | | | | || | | | | + | | LEFT | DOWN | RIGHT | PGDN | | PGDN | LEFT | DOWN | RIGHT | | | | | || | | | | + |____________|________|________|________|________|________|________|________|________|________|________|________|____________|___|________||________|________|________|________| + | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | PGUP | | | | | | + |________________|________|________|________|________|________|________|________|________|________|________|____________|________|________ |________|________|________| VOLDN | + | | | | | | | | | | | | | || | | | + | | | | | | | | | | | HOME | PGDN | END || | | | + |____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________||_________________|________|________| + * 'RESET' resets the controller and puts the board into firmware flashing mode. If this key is hit accidentally, just unplug the board + * and plug it back in. + */ + + [2] = LAYOUT( + RESET, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, TO(0), KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, RGB_TOG, RGB_VAD, RGB_VAI, KC_TRNS, TO(1), KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_HOME, KC_UP, KC_END, KC_PGUP, KC_TRNS, KC_PGUP, KC_HOME, KC_UP, KC_END, KC_TRNS, KC_TRNS, KC_TRNS, KC_PSCR, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_VOLU, + KC_TRNS, KC_LEFT, KC_DOWN, KC_RGHT, KC_PGDN, KC_TRNS, KC_PGDN, KC_LEFT, KC_DOWN, KC_RGHT, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_VOLD, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS + ), + + [3] = LAYOUT( + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS + ), +}; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + return true; +} diff --git a/keyboards/system76/launch_heavy_1/launch_heavy_1.c b/keyboards/system76/launch_heavy_1/launch_heavy_1.c new file mode 100644 index 000000000000..072ead543138 --- /dev/null +++ b/keyboards/system76/launch_heavy_1/launch_heavy_1.c @@ -0,0 +1,271 @@ +#include "dynamic_keymap.h" +#include "tmk_core/common/eeprom.h" + +#include "launch_heavy_1.h" +#include "usb_mux.h" +#include "rgb_matrix.h" + +#if RGB_MATRIX_ENABLE +// LEDs by index +// 0 1 2 3 4 5 6 7 8 9 +// 000 LM4 LL4 LK4 LJ4 LI4 LH4 LG4 LF4 LE4 LD4 +// 010 LC4 LB4 LA4 LA5 LB5 LC5 LD5 LE5 LG5 LH5 +// 020 LI5 LJ5 LK5 LL5 LM5 LO3 LM3 LL3 LK3 LJ3 +// 030 LI3 LH3 LG3 LF3 LE3 LD3 LC3 LB3 LA3 LA2 +// 040 LB2 LC2 LD2 LE2 LF2 LG2 LH2 LI2 LJ2 LK2 +// 050 LL2 LM2 LN2 LO2 LO1 LN1 LM1 LL1 LK1 LJ1 +// 060 LI1 LH1 LG1 LF1 LE1 LD1 LC1 LB1 LA1 LA0 +// 070 LB0 LC0 LD0 LE0 LF0 LG0 LH0 LI0 LJ0 LK0 +// 080 LL0 LM0 LN0 LO0 LD4 LC5 LA5 LA4 LB4 LC4 +// 090 LC3 LB3 LA3 LA2 LB2 LC2 LD2 LD1 LC1 LB1 +// 100 LA1 LA0 LB0 LC0 LD0 +led_config_t g_led_config = { LAYOUT( + // Key matrix to LED index + /* A B C D E F G H I J K L M N O P Q R S */ +/* 0 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,101,102,103,104, +/* 1 */ 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54,100, 99, 98, 97, +/* 2 */ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 93, 94, 95, 96, +/* 3 */ 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 92, 91, 90, +/* 4 */ 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 87, 88, 89, 84, +/* 5 */ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 86, 85 +), { + // LED index to physical position (this is a pain, see qmk.sh in launch repo) +/* 000 */ {169, 40}, {157, 40}, {144, 40}, {134, 40}, {125, 40}, {115, 40}, {105, 40}, {95, 40}, {85, 40}, {75, 40}, +/* 010 */ {65, 40}, {55, 40}, {40, 40}, {37, 50}, {50, 50}, {60, 50}, {70, 50}, {85, 50}, {105, 50}, {120, 50}, +/* 020 */ {129, 50}, {142, 50}, {159, 50}, {169, 50}, {179, 50}, {179, 30}, {162, 30}, {149, 30}, {139, 30}, {129, 30}, +/* 030 */ {120, 30}, {110, 30}, {100, 30}, {90, 30}, {80, 30}, {70, 30}, {60, 30}, {50, 30}, {37, 30}, {35, 20}, +/* 040 */ {47, 20}, {57, 20}, {67, 20}, {77, 20}, {87, 20}, {97, 20}, {107, 20}, {117, 20}, {127, 20}, {137, 20}, +/* 050 */ {147, 20}, {157, 20}, {167, 20}, {179, 20}, {179, 10}, {164, 10}, {152, 10}, {142, 10}, {132, 10}, {122, 10}, +/* 060 */ {112, 10}, {102, 10}, {92, 10}, {82, 10}, {72, 10}, {62, 10}, {52, 10}, {42, 10}, {32, 10}, {32, 0}, +/* 070 */ {42, 0}, {52, 0}, {62, 0}, {72, 0}, {82, 0}, {92, 0}, {102, 0}, {112, 0}, {122, 0}, {132, 0}, +/* 080 */ {142, 0}, {152, 0}, {164, 0}, {179, 0}, {224, 45}, {214, 50}, {199, 50}, {194, 40}, {204, 40}, {214, 40}, +/* 090 */ {214, 30}, {204, 30}, {194, 30}, {194, 20}, {204, 20}, {214, 20}, {224, 25}, {224, 10}, {214, 10}, {204, 10}, +/* 100 */ {194, 10}, {194, 0}, {204, 0}, {214, 0}, {224, 0} + +}, { + // LED index to flags (set all to LED_FLAG_KEYLIGHT) + /* 0 1 2 3 4 5 6 7 8 9 */ +/* 00 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +/* 10 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +/* 20 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +/* 30 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +/* 40 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +/* 50 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +/* 60 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +/* 70 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +/* 80 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +/* 90 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +/*100 */ 4, 4, 4, 4, 4, +} }; +#endif // RGB_MATRIX_ENABLE + +static bool lctl_pressed, rctl_pressed = false; + +bool eeprom_is_valid(void) { + return (eeprom_read_word(((void*)EEPROM_MAGIC_ADDR)) == EEPROM_MAGIC && + eeprom_read_byte(((void*)EEPROM_VERSION_ADDR)) == EEPROM_VERSION); +} + +void eeprom_set_valid(bool valid) { + eeprom_update_word(((void*)EEPROM_MAGIC_ADDR), valid ? EEPROM_MAGIC : 0xFFFF); + eeprom_update_byte(((void*)EEPROM_VERSION_ADDR), valid ? EEPROM_VERSION : 0xFF); +} + +void eeprom_reset(void) { + // Set the keyboard specific EEPROM state as invalid. + eeprom_set_valid(false); + // Set the TMK/QMK EEPROM state as invalid. + eeconfig_disable(); +} + +void bootmagic_lite(void) { + // The lite version of TMK's bootmagic. + // 100% less potential for accidentally making the + // keyboard do stupid things. + + // We need multiple scans because debouncing can't be turned off. + matrix_scan(); + wait_ms(DEBOUNCE); + wait_ms(DEBOUNCE); + matrix_scan(); + + // If the Esc (matrix 0,0) is held down on power up, + // reset the EEPROM valid state and jump to bootloader. + if ( matrix_get_row(0) & (1<<0) ) { + eeprom_reset(); + bootloader_jump(); + } +} + +void system76_ec_rgb_eeprom(bool write); +void system76_ec_rgb_layer(layer_state_t layer_state); +void system76_ec_unlock(void); +bool system76_ec_is_unlocked(void); +rgb_config_t layer_rgb[DYNAMIC_KEYMAP_LAYER_COUNT]; + +void matrix_init_kb(void) { + usb_mux_init(); + + bootmagic_lite(); + if (!eeprom_is_valid()) { + dynamic_keymap_reset(); + dynamic_keymap_macro_reset(); + system76_ec_rgb_eeprom(true); + eeprom_set_valid(true); + } else { + system76_ec_rgb_eeprom(false); + } + + system76_ec_rgb_layer(layer_state); +} + +void matrix_scan_kb(void) { + usb_mux_event(); + + matrix_scan_user(); +} + +#define LEVEL(value) (uint8_t)(\ + ((uint16_t)value) \ + * ((uint16_t)RGB_MATRIX_MAXIMUM_BRIGHTNESS) \ + / ((uint16_t)255) \ +) + +static const uint8_t levels[] = { + LEVEL(48), + LEVEL(72), + LEVEL(96), + LEVEL(144), + LEVEL(192), + LEVEL(255) +}; + +static uint8_t toggle_level = RGB_MATRIX_MAXIMUM_BRIGHTNESS; +extern bool input_disabled; + +static void set_value_all_layers(uint8_t value) { + if (!system76_ec_is_unlocked()) { + for (int8_t layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) { + layer_rgb[layer].hsv.v = value; + } + system76_ec_rgb_layer(layer_state); + } +} + +bool process_record_kb(uint16_t keycode, keyrecord_t *record) { + if (input_disabled) + return false; + + switch(keycode) { + case RESET: + if (record->event.pressed) { + system76_ec_unlock(); + } + return false; + case RGB_VAD: + if (record->event.pressed) { + uint8_t level = rgb_matrix_config.hsv.v; + for (int i = sizeof(levels) - 1; i >= 0; i--) { + if (levels[i] < level) { + level = levels[i]; + break; + } + } + set_value_all_layers(level); + } + return false; + case RGB_VAI: + if (record->event.pressed) { + uint8_t level = rgb_matrix_config.hsv.v; + for (int i = 0; i < sizeof(levels); i++) { + if (levels[i] > level) { + level = levels[i]; + break; + } + } + set_value_all_layers(level); + } + return false; + case RGB_TOG: + if (record->event.pressed) { + uint8_t level = 0; + if (rgb_matrix_config.hsv.v == 0) { + level = toggle_level; + } else { + toggle_level = rgb_matrix_config.hsv.v; + } + set_value_all_layers(level); + } + return false; + case KC_LCTL: + lctl_pressed = record->event.pressed; + break; + case KC_RCTL: + rctl_pressed = record->event.pressed; + break; + case KC_ESC: + if (lctl_pressed && rctl_pressed) { + if (record->event.pressed) system76_ec_unlock(); + return false; + } + break; + } + + return process_record_user(keycode, record); +} + +layer_state_t layer_state_set_kb(layer_state_t layer_state) { + system76_ec_rgb_layer(layer_state); + + return layer_state_set_user(layer_state); +} + +void suspend_power_down_kb(void) { + rgb_matrix_set_suspend_state(true); + suspend_power_down_user(); +} + +void suspend_wakeup_init_kb(void) { + rgb_matrix_set_suspend_state(false); + suspend_wakeup_init_user(); +} + +void bootloader_jump(void) { + + // Disable all peripherals on AT90USB646 + UDCON = 1; + USBCON = (1<> 4; + uint8_t col = rowcol & 0xF; + uint16_t keycode = dynamic_keymap_get_keycode(layer, row, col); + switch (keycode) { + case KC_NO: + case KC_TRNS: + rgb_matrix_set_color(i, 0, 0, 0); + break; + default: + rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + break; + } + } + + return led_max < DRIVER_LED_TOTAL; +} + +RGB raw_rgb_data[DRIVER_LED_TOTAL] = { 0 }; + +static uint8_t normalize_component(uint8_t component) { + uint16_t x = (uint16_t)component; + x *= rgb_matrix_config.hsv.v; // Multiply by current brightness + x /= 255; // Divide by maximum brightness + return (uint8_t)x; +} + +static RGB normalize_index(uint8_t i) { + RGB raw = raw_rgb_data[i]; + RGB rgb = { + .r = normalize_component(raw.r), + .g = normalize_component(raw.g), + .b = normalize_component(raw.b), + }; + return rgb; +} + +static bool raw_rgb(effect_params_t* params) { + RGB_MATRIX_USE_LIMITS(led_min, led_max); + for (uint8_t i = led_min; i < led_max; i++) { + RGB_MATRIX_TEST_LED_FLAGS(); + RGB rgb = normalize_index(i); + rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + } + return led_max < DRIVER_LED_TOTAL; +} + +static uint8_t unlocked_keys[8][2] = { + {2, 7}, // U + {4, 6}, // N + {3, 9}, // L + {2, 9}, // O + {4, 3}, // C + {3, 8}, // K + {2, 3}, // E + {3, 3}, // D +}; + +static uint8_t unlocked_ticks = 0; +static uint8_t unlocked_i = 0; +static uint8_t unlocked_leds_count = 0; +static uint8_t unlocked_leds[2] = { 0, 0 }; + +static bool unlocked(effect_params_t* params) { + RGB_MATRIX_USE_LIMITS(led_min, led_max); + + unlocked_ticks++; + + if (params->init) { + unlocked_ticks = 0; + unlocked_i = 0; + } + + if (unlocked_ticks == 0) { + if (unlocked_i == 8) { + unlocked_leds_count = 0; + unlocked_i = 0; + } else { + unlocked_leds_count = rgb_matrix_map_row_column_to_led( + unlocked_keys[unlocked_i][0], + unlocked_keys[unlocked_i][1], + unlocked_leds + ); + unlocked_i++; + } + } + + for (uint8_t i = led_min; i < led_max; i++) { + RGB_MATRIX_TEST_LED_FLAGS(); + + HSV hsv = { + .h = i + unlocked_ticks, + .s = 0xFF, + .v = 0x70, + }; + for (uint8_t j = 0; j < unlocked_leds_count; j++) { + if (i == unlocked_leds[j]) { + hsv.s = 0; + hsv.v = 0xFF; + } + } + + RGB rgb = hsv_to_rgb(hsv); + rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + } + return led_max < DRIVER_LED_TOTAL; +} +#endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS diff --git a/keyboards/system76/launch_heavy_1/rules.mk b/keyboards/system76/launch_heavy_1/rules.mk new file mode 100644 index 000000000000..73b7980de39d --- /dev/null +++ b/keyboards/system76/launch_heavy_1/rules.mk @@ -0,0 +1,47 @@ +# MCU name +MCU = at90usb646 + +# Allow flashing with usbasp +AVRDUDE_MCU = $(MCU) + +# CPU frequency divided by two since AVR is at 3.3V +F_CPU = 8000000 +# External oscillator is 16Mhz +F_USB = 16000000 + +# Bootloader selection +# Teensy halfkay +# Pro Micro caterina +# Atmel DFU atmel-dfu +# LUFA DFU lufa-dfu +# QMK DFU qmk-dfu +# ATmega32A bootloadHID +# ATmega328P USBasp +BOOTLOADER = qmk-dfu + +# Build Options +# comment out to disable the options. +# +BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000) +MOUSEKEY_ENABLE = no # Mouse keys(+4700) +EXTRAKEY_ENABLE = yes # Audio control and System control(+450) +CONSOLE_ENABLE = no # Console for debug(+400) +COMMAND_ENABLE = no # Commands for debug and configuration +DYNAMIC_KEYMAP_ENABLE = yes # Reconfigurable keyboard without flashing firmware +NKRO_ENABLE = yes # USB Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work +RAW_ENABLE = yes # Enable RAW HID commands (used by keyboard configurator) +RGBLIGHT_ENABLE = no # Support for RGB backlight (conflicts with RGB_MATRIX) +RGB_MATRIX_ENABLE = yes # Support for RGB matrix +RGB_MATRIX_DRIVER = WS2812 +RGB_MATRIX_CUSTOM_KB = yes # Allow custom keyboard effect +USB_6KRO_ENABLE = no # 6key Rollover +LTO_ENABLE = yes # Link-time optimization for smaller binary + +# Add System76 EC command interface +SRC+=../system76_ec.c + +# Add I2C driver +SRC+=i2c.c + +# Add USB mux driver +SRC+=usb_mux.c diff --git a/keyboards/system76/launch_heavy_1/usb_mux.c b/keyboards/system76/launch_heavy_1/usb_mux.c new file mode 100644 index 000000000000..d1177716ab81 --- /dev/null +++ b/keyboards/system76/launch_heavy_1/usb_mux.c @@ -0,0 +1,458 @@ +#include +#include "quantum.h" + +#include "i2c.h" +#include "usb_mux.h" + +#define GPIO_RESET_USB A3 + +#define REG_PF1_CTL 0xBF800C04 +#define REG_PIO64_OEN 0xBF800908 +#define REG_PIO64_OUT 0xBF800928 +#define REG_VID 0xBF803000 +#define REG_PRT_SWAP 0xBF8030FA +#define REG_USB3_HUB_VID 0xBF809048 +#define REG_RUNTIME_FLAGS2 0xBFD23408 +#define REG_I2S_FEAT_SEL 0xBFD23412 + +struct USB7006 { + uint8_t addr; +}; + +struct USB7006 usb_hub = { .addr = 0x2D }; + +// Perform USB7006 register access +// Returns bytes written on success or negative number on error +int usb7006_register_access(struct USB7006 * self) { + uint8_t data[3] = { + 0x99, + 0x37, + 0x00, + }; + return i2c_send(self->addr, data, sizeof(data)); +} + +// Read data from USB7006 register region +// Returns number of bytes read on success or negative number on error +int usb7006_read_reg(struct USB7006 * self, uint32_t addr, uint8_t * data, int length) { + int res; + + uint8_t command[9] = { + // Buffer address high: always 0 + 0x00, + // Buffer address low: always 0 + 0x00, + // Number of bytes to write to command block buffer area + 0x06, + // Direction: 0 = write, 1 = read + 0x01, + // Number of bytes to read from register + (uint8_t)length, + // Register address byte 3 + (uint8_t)(addr >> 24), + // Register address byte 2 + (uint8_t)(addr >> 16), + // Register address byte 1 + (uint8_t)(addr >> 8), + // Register address byte 0 + (uint8_t)(addr >> 0), + }; + res = i2c_send(self->addr, command, sizeof(command)); + if (res < 0) return res; + + res = usb7006_register_access(self); + if (res < 0) return res; + + res = i2c_start(self->addr, false); + if (res < 0) return res; + + uint8_t command2[2] = { + // Buffer address high: always 0 + 0x00, + // Buffer address low: 6 to skip header + 0x06, + }; + res = i2c_write(command2, sizeof(command2)); + if (res < 0) return res; + + res = i2c_start(self->addr, true); + if (res < 0) return res; + + // Read and ignore buffer length + res = i2c_read_byte(true); + if (res < 0) return res; + + res = i2c_read(data, length); + if (res < 0) return res; + + i2c_stop(); + + return length; +} + +// Read 32-bit value from USB7006 register region +// Returns number of bytes read on success or negative number on error +int usb7006_read_reg_32(struct USB7006 * self, uint32_t addr, uint32_t * data) { + int res; + + // First byte is available length + uint8_t bytes[4] = { 0, 0, 0, 0, }; + res = usb7006_read_reg(self, addr, bytes, sizeof(bytes)); + if (res < 0) return res; + + // Must convert from little endian + *data = + (((uint32_t)bytes[0]) << 0) | + (((uint32_t)bytes[1]) << 8) | + (((uint32_t)bytes[2]) << 16) | + (((uint32_t)bytes[3]) << 24); + + return res; +} + +// Write data to USB7006 register region +// Returns number of bytes written on success or negative number on error +int usb7006_write_reg(struct USB7006 * self, uint32_t addr, uint8_t * data, int length) { + int res; + + res = i2c_start(self->addr, false); + if (res < 0) return res; + + uint8_t command[9] = { + // Buffer address high: always 0 + 0x00, + // Buffer address low: always 0 + 0x00, + // Number of bytes to write to command block buffer area + //TODO: check length! + ((uint8_t)length) + 6, + // Direction: 0 = write, 1 = read + 0x00, + // Number of bytes to write to register + (uint8_t)length, + // Register address byte 3 + (uint8_t)(addr >> 24), + // Register address byte 2 + (uint8_t)(addr >> 16), + // Register address byte 1 + (uint8_t)(addr >> 8), + // Register address byte 0 + (uint8_t)(addr >> 0), + }; + res = i2c_write(command, sizeof(command)); + if (res < 0) return res; + + res = i2c_write(data, length); + if (res < 0) return res; + + i2c_stop(); + + res = usb7006_register_access(self); + if (res < 0) return res; + + return length; +} + +// Write 8-bit value to USB7006 register region +// Returns number of bytes written on success or negative number on error +int usb7006_write_reg_8(struct USB7006 * self, uint32_t addr, uint8_t data) { + return usb7006_write_reg(self, addr, &data, sizeof(data)); +} + +// Write 32-bit value to USB7006 register region +// Returns number of bytes written on success or negative number on error +int usb7006_write_reg_32(struct USB7006 * self, uint32_t addr, uint32_t data) { + // Must convert to little endian + uint8_t bytes[4] = { + (uint8_t)(data >> 0), + (uint8_t)(data >> 8), + (uint8_t)(data >> 16), + (uint8_t)(data >> 24), + }; + return usb7006_write_reg(self, addr, bytes, sizeof(bytes)); +} + +// Initialize USB7006 +// Returns zero on success or negative number on error +int usb7006_init(struct USB7006 * self) { + int res; + + // DM and DP are swapped on ports 2 and 3 + res = usb7006_write_reg_8(self, REG_PRT_SWAP, 0x0C); + if (res < 0) return res; + + // Disable audio + res = usb7006_write_reg_8(self, REG_I2S_FEAT_SEL, 0); + if (res < 0) return res; + + // Set HFC_DISABLE + uint32_t data = 0; + res = usb7006_read_reg_32(self, REG_RUNTIME_FLAGS2, &data); + if (res < 0) return res; + data |= 1; + res = usb7006_write_reg_32(self, REG_RUNTIME_FLAGS2, data); + if (res < 0) return res; + + // Set Vendor ID and Product ID of USB 2 hub + res = usb7006_write_reg_32(self, REG_VID, 0x00033384); + if (res < 0) return res; + + // Set Vendor ID and Product ID of USB 3 hub + res = usb7006_write_reg_32(self, REG_USB3_HUB_VID, 0x00043384); + if (res < 0) return res; + + return 0; +} + +// Attach USB7006 +// Returns bytes written on success or negative number on error +int usb7006_attach(struct USB7006 * self) { + uint8_t data[3] = { + 0xAA, + 0x56, + 0x00, + }; + return i2c_send(self->addr, data, sizeof(data)); +} + +struct USB7006_GPIO { + struct USB7006 * usb7006; + uint32_t pf; +}; + +// UP_SEL = PF29 = GPIO93 +struct USB7006_GPIO usb_gpio_sink = { + .usb7006 = &usb_hub, + .pf = 29, +}; + +// CL_SEL = PF10 = GPIO74 +struct USB7006_GPIO usb_gpio_source_left = { + .usb7006 = &usb_hub, + .pf = 10, +}; + +// CR_SEL = PF25 = GPIO88 +struct USB7006_GPIO usb_gpio_source_right = { + .usb7006 = &usb_hub, + .pf = 25, +}; + +// Set USB7006 GPIO to specified value +// Returns zero on success or negative number on error +int usb7006_gpio_set(struct USB7006_GPIO * self, bool value) { + int res; + + uint32_t data = 0; + res = usb7006_read_reg_32(self->usb7006, REG_PIO64_OUT, &data); + if (res < 0) return res; + + if (value) { + data |= (((uint32_t)1) << self->pf); + } else { + data &= ~(((uint32_t)1) << self->pf); + } + res = usb7006_write_reg_32(self->usb7006, REG_PIO64_OUT, data); + if (res < 0) return res; + + return 0; +} + +// Initialize USB7006 GPIO +// Returns zero on success or negative number on error +int usb7006_gpio_init(struct USB7006_GPIO * self) { + int res = 0; + + // Set programmable function to GPIO + res = usb7006_write_reg_8(self->usb7006, REG_PF1_CTL + (self->pf - 1), 0); + if (res < 0) return res; + + // Set GPIO to false by default + usb7006_gpio_set(self, false); + + // Set GPIO to output + uint32_t data = 0; + res = usb7006_read_reg_32(self->usb7006, REG_PIO64_OEN, &data); + if (res < 0) return res; + + data |= (((uint32_t)1) << self->pf); + res = usb7006_write_reg_32(self->usb7006, REG_PIO64_OEN, data); + if (res < 0) return res; + + return 0; +} + +#define TCPC_CC_STATUS 0x1D +#define TCPC_ROLE_CONTROL 0x1A +#define TCPC_COMMAND 0x23 + +enum TCPC_TYPE { + TCPC_TYPE_SINK, + TCPC_TYPE_SOURCE, +}; + +struct PTN5110 { + enum TCPC_TYPE type; + uint8_t addr; + uint8_t cc; + struct USB7006_GPIO * gpio; +}; + +struct PTN5110 usb_sink = { .type = TCPC_TYPE_SINK, .addr = 0x51, .gpio = &usb_gpio_sink }; +struct PTN5110 usb_source_left = { .type = TCPC_TYPE_SOURCE, .addr = 0x52, .gpio = &usb_gpio_source_left }; +struct PTN5110 usb_source_right = { .type = TCPC_TYPE_SOURCE, .addr = 0x50, .gpio = &usb_gpio_source_right }; + +// Read PTN5110 CC_STATUS +// Returns bytes read on success or negative number on error +int ptn5110_get_cc_status(struct PTN5110 * self, uint8_t * cc) { + return i2c_get(self->addr, TCPC_CC_STATUS, cc, 1); +} + +// Write PTN5110 ROLE_CONTROL +// Returns bytes written on success or negative number on error +int ptn5110_set_role_control(struct PTN5110 * self, uint8_t role_control) { + return i2c_set(self->addr, TCPC_ROLE_CONTROL, &role_control, 1); +} + +// Set PTN5110 SSMUX orientation +// Returns zero on success or negative number on error +int ptn5110_set_ssmux(struct PTN5110 * self, bool orientation) { + return usb7006_gpio_set(self->gpio, orientation); +} + +// Write PTN5110 COMMAND +// Returns bytes written on success or negative number on error +int ptn5110_command(struct PTN5110 * self, uint8_t command) { + return i2c_set(self->addr, TCPC_COMMAND, &command, 1); +} + +// Set orientation of PTN5110 operating as a sink, call this once +// Returns zero on success or negative number on error +int ptn5110_sink_set_orientation(struct PTN5110 * self) { + int res; + + uint8_t cc; + res = ptn5110_get_cc_status(self, &cc); + if (res < 0) return res; + + if ((cc & 3) == 0) { + res = ptn5110_set_ssmux(self, false); + if (res < 0) return res; + } else { + res = ptn5110_set_ssmux(self, true); + if (res < 0) return res; + } + + return 0; +} + +// Update PTN5110 operating as a source, call this repeatedly +// Returns zero on success or negative number on error +int ptn5110_source_update(struct PTN5110 * self) { + int res; + + uint8_t cc; + res = ptn5110_get_cc_status(self, &cc); + if (res < 0) return res; + + if (cc != self->cc) { + //WARNING: Setting this here will disable retries + self->cc = cc; + + bool connected = false; + bool orientation = false; + if ((cc & 3) == 2) { + connected = true; + orientation = true; + } else if (((cc >> 2) & 3) == 2) { + connected = true; + orientation = false; + } + + if (connected) { + // Set SS mux orientation + res = ptn5110_set_ssmux(self, orientation); + if (res < 0) return res; + + // Enable source vbus command + res = ptn5110_command(self, 0b01110111); + if (res < 0) return res; + } else { + // Disable source vbus command + res = ptn5110_command(self, 0b01100110); + if (res < 0) return res; + } + } + + return 0; +} + +// Initialize PTN5110 +// Returns zero on success or negative number on error +int ptn5110_init(struct PTN5110 * self) { + int res; + + // Set last cc to invalid value, to force update + self->cc = 0xFF; + + // Initialize GPIO + res = usb7006_gpio_init(self->gpio); + if (res < 0) return res; + + switch (self->type) { + case TCPC_TYPE_SINK: + res = ptn5110_sink_set_orientation(self); + if (res < 0) return res; + break; + case TCPC_TYPE_SOURCE: + res = ptn5110_set_role_control(self, 0x05); + if (res < 0) return res; + break; + } + + return 0; +} + +void usb_mux_event(void) { + // Run this on every 1000th matrix scan + static int cycle = 0; + if (cycle >= 1000) { + cycle = 0; + ptn5110_source_update(&usb_source_left); + ptn5110_source_update(&usb_source_right); + } else { + cycle += 1; + } +} + +void usb_mux_init(void) { + // Run I2C bus at 100 KHz + i2c_init(100000); + + // Sleep 10 ms, bring hub out of reset + _delay_ms(10); + setPinOutput(GPIO_RESET_USB); + writePinHigh(GPIO_RESET_USB); + // Per Microchip support, wait 100 ms after reset with I2C idle + _delay_ms(100); + + // Set up hub + usb7006_init(&usb_hub); + + // Set up sink + ptn5110_init(&usb_sink); + + // Set up sources + ptn5110_init(&usb_source_left); + ptn5110_init(&usb_source_right); + + // Attach hub + usb7006_attach(&usb_hub); + + // Ensure orientation is correct after attaching hub + //TODO: find reason why GPIO for sink orientation is reset + for(int i = 0; i < 100; i++) { + ptn5110_sink_set_orientation(&usb_sink); + _delay_ms(10); + } +} diff --git a/keyboards/system76/launch_heavy_1/usb_mux.h b/keyboards/system76/launch_heavy_1/usb_mux.h new file mode 100644 index 000000000000..7268125dee9f --- /dev/null +++ b/keyboards/system76/launch_heavy_1/usb_mux.h @@ -0,0 +1,7 @@ +#ifndef USB_MUX_H +#define USB_MUX_H + +void usb_mux_init(void); +void usb_mux_event(void); + +#endif // USB_MUX_H