174 lines
5.2 KiB
C
174 lines
5.2 KiB
C
|
/* Copyright 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.
|
||
|
*/
|
||
|
|
||
|
/* Lid angle module for Chrome EC */
|
||
|
|
||
|
#include "chipset.h"
|
||
|
#include "common.h"
|
||
|
#include "console.h"
|
||
|
#include "hooks.h"
|
||
|
#include "keyboard_scan.h"
|
||
|
#include "lid_angle.h"
|
||
|
#include "lid_switch.h"
|
||
|
#include "math_util.h"
|
||
|
#include "motion_lid.h"
|
||
|
#include "motion_sense.h"
|
||
|
|
||
|
/* Console output macros */
|
||
|
#define CPUTS(outstr) cputs(CC_LIDANGLE, outstr)
|
||
|
#define CPRINTS(format, args...) cprints(CC_LIDANGLE, format, ## args)
|
||
|
|
||
|
/*
|
||
|
* Define the number of previous lid angle measurements to keep for determining
|
||
|
* whether to enable or disable peripherals that are only needed for laptop
|
||
|
* mode. These incude keyboard and trackpad. Note, that in order to change the
|
||
|
* enable/disable state of these peripherals, all stored measurements of the
|
||
|
* lid angle buffer must be in the specified range.
|
||
|
*/
|
||
|
#define LID_ANGLE_BUFFER_SIZE 4
|
||
|
|
||
|
/*
|
||
|
* Define two variables to determine if wake source peripherals that are only
|
||
|
* applicable for laptop mode should be enabled or disabled in S3 based on the
|
||
|
* current lid angle. Note, the lid angle is bound to [0, 360]. Here are two
|
||
|
* angles, defined such that we segregate the lid angle space into two regions.
|
||
|
* The first region is the region in which we enable peripherals in S3 and is
|
||
|
* when the lid angle CCW of the small_angle and CW of the large_angle. The
|
||
|
* second region is the region in which we disable peripherals in S3 and is when
|
||
|
* the lid angle is CCW of the large_angle and CW of the small_angle.
|
||
|
*
|
||
|
* Note, the most sensical values are small_angle = 0 and large_angle = 180,
|
||
|
* but, the angle measurement is not perfect, and we know that if the angle is
|
||
|
* near 0 and the lid isn't closed, then the lid must be near 360. So, the
|
||
|
* small_angle is set to a small positive value to make sure we don't swap modes
|
||
|
* when the lid is open all the way but is measuring a small positive value.
|
||
|
*/
|
||
|
static int wake_large_angle = 180;
|
||
|
static const int wake_small_angle = 13;
|
||
|
|
||
|
/* Define hysteresis value to add stability to the flags. */
|
||
|
#define LID_ANGLE_HYSTERESIS_DEG 2
|
||
|
|
||
|
/* Define max and min values for wake_large_angle. */
|
||
|
#define LID_ANGLE_MIN_LARGE_ANGLE 0
|
||
|
#define LID_ANGLE_MAX_LARGE_ANGLE 360
|
||
|
|
||
|
/**
|
||
|
* Determine if given angle is in region to enable peripherals.
|
||
|
*
|
||
|
* @param ang Some lid angle in degrees [0, 360]
|
||
|
*
|
||
|
* @return true/false
|
||
|
*/
|
||
|
static int lid_in_range_to_enable_peripherals(int ang)
|
||
|
{
|
||
|
/*
|
||
|
* If the wake large angle is min or max, then this function should
|
||
|
* return false or true respectively, independent of input angle.
|
||
|
*/
|
||
|
if (wake_large_angle == LID_ANGLE_MIN_LARGE_ANGLE)
|
||
|
return 0;
|
||
|
else if (wake_large_angle == LID_ANGLE_MAX_LARGE_ANGLE)
|
||
|
return 1;
|
||
|
|
||
|
return (ang >= (wake_small_angle + LID_ANGLE_HYSTERESIS_DEG)) &&
|
||
|
(ang <= (wake_large_angle - LID_ANGLE_HYSTERESIS_DEG));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Determine if given angle is in region to ignore peripherals.
|
||
|
*
|
||
|
* @param ang Some lid angle in degrees [0, 360]
|
||
|
*
|
||
|
* @return true/false
|
||
|
*/
|
||
|
static int lid_in_range_to_ignore_peripherals(int ang)
|
||
|
{
|
||
|
/*
|
||
|
* If the wake large angle is min or max, then this function should
|
||
|
* return true or false respectively, independent of input angle.
|
||
|
*/
|
||
|
if (wake_large_angle == LID_ANGLE_MIN_LARGE_ANGLE)
|
||
|
return 1;
|
||
|
else if (wake_large_angle == LID_ANGLE_MAX_LARGE_ANGLE)
|
||
|
return 0;
|
||
|
|
||
|
return (ang <= (wake_small_angle - LID_ANGLE_HYSTERESIS_DEG)) ||
|
||
|
(ang >= (wake_large_angle + LID_ANGLE_HYSTERESIS_DEG));
|
||
|
}
|
||
|
|
||
|
|
||
|
int lid_angle_get_wake_angle(void)
|
||
|
{
|
||
|
return wake_large_angle;
|
||
|
}
|
||
|
|
||
|
void lid_angle_set_wake_angle(int ang)
|
||
|
{
|
||
|
if (ang < LID_ANGLE_MIN_LARGE_ANGLE)
|
||
|
ang = LID_ANGLE_MIN_LARGE_ANGLE;
|
||
|
else if (ang > LID_ANGLE_MAX_LARGE_ANGLE)
|
||
|
ang = LID_ANGLE_MAX_LARGE_ANGLE;
|
||
|
|
||
|
wake_large_angle = ang;
|
||
|
}
|
||
|
|
||
|
void lid_angle_update(int lid_ang)
|
||
|
{
|
||
|
static int lidangle_buffer[LID_ANGLE_BUFFER_SIZE];
|
||
|
static int index;
|
||
|
int i;
|
||
|
int accept = 1, ignore = 1;
|
||
|
|
||
|
/* Record most recent lid angle in circular buffer. */
|
||
|
lidangle_buffer[index] = lid_ang;
|
||
|
index = (index == LID_ANGLE_BUFFER_SIZE-1) ? 0 : index+1;
|
||
|
|
||
|
/*
|
||
|
* Manage whether or not peripherals are enabled based on lid angle
|
||
|
* history.
|
||
|
*/
|
||
|
for (i = 0; i < LID_ANGLE_BUFFER_SIZE; i++) {
|
||
|
/*
|
||
|
* If any lid angle samples are unreliable, then
|
||
|
* don't change peripheral state.
|
||
|
*/
|
||
|
if (lidangle_buffer[i] == LID_ANGLE_UNRELIABLE)
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
* Force all elements of the lid angle buffer to be
|
||
|
* in range of one of the conditions in order to change
|
||
|
* to the corresponding peripheral state.
|
||
|
*/
|
||
|
if (!lid_in_range_to_enable_peripherals(lidangle_buffer[i]))
|
||
|
accept = 0;
|
||
|
if (!lid_in_range_to_ignore_peripherals(lidangle_buffer[i]))
|
||
|
ignore = 0;
|
||
|
}
|
||
|
|
||
|
/* Enable or disable peripherals as necessary. */
|
||
|
if (accept)
|
||
|
lid_angle_peripheral_enable(1);
|
||
|
else if (ignore && !accept)
|
||
|
lid_angle_peripheral_enable(0);
|
||
|
}
|
||
|
|
||
|
static void enable_peripherals(void)
|
||
|
{
|
||
|
/*
|
||
|
* Make sure lid angle is not disabling peripherals when AP is running.
|
||
|
*/
|
||
|
lid_angle_peripheral_enable(1);
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_CHIPSET_RESUME, enable_peripherals, HOOK_PRIO_DEFAULT);
|
||
|
|
||
|
/* Board level callback was not linked in test build, implement it here. */
|
||
|
#ifdef TEST_BUILD
|
||
|
void lid_angle_peripheral_enable(int enable)
|
||
|
{
|
||
|
}
|
||
|
#endif
|