/* Copyright 2016 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. */ #include "console.h" #include "gpio.h" #include "hooks.h" #include "host_command.h" #include "lid_angle.h" #include "tablet_mode.h" #include "timer.h" #define CPRINTS(format, args...) cprints(CC_MOTION_LID, format, ## args) #define CPRINTF(format, args...) cprintf(CC_MOTION_LID, format, ## args) /* 1: in tablet mode; 0: notebook mode; -1: uninitialized */ static int tablet_mode = -1; /* 1: hall sensor is reporting 360 degrees. */ static int hall_sensor_at_360; /* * 1: all calls to tablet_set_mode are ignored and tablet_mode if forced to 0 * 0: all calls to tablet_set_mode are honored */ static int disabled; int tablet_get_mode(void) { return !!tablet_mode; } void tablet_set_mode(int mode) { if (tablet_mode == mode) return; if (disabled) { CPRINTS("Tablet mode set while disabled (ignoring)!"); return; } if (hall_sensor_at_360 && !mode) { CPRINTS("Ignoring tablet mode exit while hall sensor active."); return; } tablet_mode = mode; CPRINTS("tablet mode %sabled", mode ? "en" : "dis"); hook_notify(HOOK_TABLET_MODE_CHANGE); #ifdef CONFIG_HOSTCMD_EVENTS /* * When tablet mode changes, send an event to ACPI to retrieve * tablet mode value and send an event to the kernel. */ host_set_single_event(EC_HOST_EVENT_MODE_CHANGE); #endif } /* This ifdef can be removed once we clean up past projects which do own init */ #ifdef CONFIG_HALL_SENSOR #ifndef HALL_SENSOR_GPIO_L #error HALL_SENSOR_GPIO_L must be defined #endif static void hall_sensor_interrupt_debounce(void) { hall_sensor_at_360 = IS_ENABLED(CONFIG_HALL_SENSOR_CUSTOM) ? board_sensor_at_360() : !gpio_get_level(HALL_SENSOR_GPIO_L); /* * 1. Peripherals are disabled only when lid reaches 360 position (It's * probably already disabled by motion_sense task). We deliberately do * not enable peripherals when the lid is leaving 360 position. Instead, * we let motion sense task enable it once it is reaches laptop zone * (180 or less). * 2. Similarly, tablet mode is set here when lid reaches 360 * position. It should already be set by motion lid driver. We * deliberately do not clear tablet mode when lid is leaving 360 * position(if motion lid driver is used). Instead, we let motion lid * driver to clear it when lid goes into laptop zone. */ if (!IS_ENABLED(CONFIG_LID_ANGLE) || hall_sensor_at_360) tablet_set_mode(hall_sensor_at_360); if (IS_ENABLED(CONFIG_LID_ANGLE_UPDATE) && hall_sensor_at_360) lid_angle_peripheral_enable(0); } DECLARE_DEFERRED(hall_sensor_interrupt_debounce); /* Debounce time for hall sensor interrupt */ #define HALL_SENSOR_DEBOUNCE_US (30 * MSEC) void hall_sensor_isr(enum gpio_signal signal) { hook_call_deferred(&hall_sensor_interrupt_debounce_data, HALL_SENSOR_DEBOUNCE_US); } static void hall_sensor_init(void) { /* If this sub-system was disabled before initializing, honor that. */ if (disabled) return; gpio_enable_interrupt(HALL_SENSOR_GPIO_L); /* * Ensure tablet mode is initialized according to the hardware state * so that the cached state reflects reality. */ hall_sensor_interrupt_debounce(); } DECLARE_HOOK(HOOK_INIT, hall_sensor_init, HOOK_PRIO_DEFAULT); void hall_sensor_disable(void) { gpio_disable_interrupt(HALL_SENSOR_GPIO_L); /* Cancel any pending debounce calls */ hook_call_deferred(&hall_sensor_interrupt_debounce_data, -1); tablet_set_mode(0); disabled = 1; } #endif