coreboot-libre-fam15h-rdimm/3rdparty/chromeec/common/tablet_mode.c

185 lines
5.1 KiB
C
Raw Normal View History

2024-03-04 11:14:53 +01:00
/* 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 "acpi.h"
#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;
static int forced_tablet_mode = -1;
/* 1: GMR sensor is reporting 360 degrees. */
static int gmr_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)
{
if (forced_tablet_mode != -1)
return !!forced_tablet_mode;
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 (gmr_sensor_at_360 && !mode) {
CPRINTS("Ignoring tablet mode exit while gmr sensor "
"reports 360-degree tablet mode.");
return;
}
tablet_mode = mode;
if (forced_tablet_mode != -1)
return;
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
}
static void tabletmode_force_state(int mode)
{
if (forced_tablet_mode == mode)
return;
forced_tablet_mode = mode;
hook_notify(HOOK_TABLET_MODE_CHANGE);
if (IS_ENABLED(CONFIG_HOSTCMD_EVENTS))
host_set_single_event(EC_HOST_EVENT_MODE_CHANGE);
}
void tablet_disable(void)
{
tablet_mode = 0;
disabled = 1;
}
/* This ifdef can be removed once we clean up past projects which do own init */
#ifdef CONFIG_GMR_TABLET_MODE
#ifndef GMR_TABLET_MODE_GPIO_L
#error GMR_TABLET_MODE_GPIO_L must be defined
#endif
#ifdef CONFIG_DPTF_MOTION_LID_NO_GMR_SENSOR
#error The board has GMR sensor
#endif
static void gmr_tablet_switch_interrupt_debounce(void)
{
gmr_sensor_at_360 = IS_ENABLED(CONFIG_GMR_TABLET_MODE_CUSTOM)
? board_sensor_at_360()
: !gpio_get_level(GMR_TABLET_MODE_GPIO_L);
/*
* DPTF table is updated only when the board enters/exits completely
* flipped tablet mode. If the board has no GMR sensor, we determine
* if the board is in completely-flipped tablet mode by lid angle
* calculation and update DPTF table when lid angle > 300 degrees.
*/
if (IS_ENABLED(CONFIG_HOSTCMD_X86) && IS_ENABLED(CONFIG_DPTF)) {
acpi_dptf_set_profile_num(gmr_sensor_at_360 ?
DPTF_PROFILE_FLIPPED_360_MODE :
DPTF_PROFILE_CLAMSHELL);
}
/*
* 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) || gmr_sensor_at_360)
tablet_set_mode(gmr_sensor_at_360);
if (IS_ENABLED(CONFIG_LID_ANGLE_UPDATE) && gmr_sensor_at_360)
lid_angle_peripheral_enable(0);
}
DECLARE_DEFERRED(gmr_tablet_switch_interrupt_debounce);
/* Debounce time for gmr sensor tablet mode interrupt */
#define GMR_SENSOR_DEBOUNCE_US (30 * MSEC)
void gmr_tablet_switch_isr(enum gpio_signal signal)
{
hook_call_deferred(&gmr_tablet_switch_interrupt_debounce_data,
GMR_SENSOR_DEBOUNCE_US);
}
static void gmr_tablet_switch_init(void)
{
/* If this sub-system was disabled before initializing, honor that. */
if (disabled)
return;
gpio_enable_interrupt(GMR_TABLET_MODE_GPIO_L);
/*
* Ensure tablet mode is initialized according to the hardware state
* so that the cached state reflects reality.
*/
gmr_tablet_switch_interrupt_debounce();
}
DECLARE_HOOK(HOOK_INIT, gmr_tablet_switch_init, HOOK_PRIO_DEFAULT);
void gmr_tablet_switch_disable(void)
{
gpio_disable_interrupt(GMR_TABLET_MODE_GPIO_L);
/* Cancel any pending debounce calls */
hook_call_deferred(&gmr_tablet_switch_interrupt_debounce_data, -1);
tablet_disable();
}
#endif
static int command_settabletmode(int argc, char **argv)
{
if (argc != 2)
return EC_ERROR_PARAM_COUNT;
if (argv[1][0] == 'o' && argv[1][1] == 'n')
tabletmode_force_state(1);
else if (argv[1][0] == 'o' && argv[1][1] == 'f')
tabletmode_force_state(0);
else if (argv[1][0] == 'r')
tabletmode_force_state(-1);
else
return EC_ERROR_PARAM1;
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(tabletmode, command_settabletmode,
"[on | off | reset]",
"Manually force tablet mode to on, off or reset.");