summaryrefslogtreecommitdiff
path: root/board/samus/panel.c
blob: beb0d32e9d904681ecf0e0ceccd89ca1aa7154f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/* Copyright 2013 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 "chipset.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "i2c.h"
#include "lid_switch.h"
#include "timer.h"

#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)

#define I2C_ADDR_BACKLIGHT_FLAGS (0x2C | I2C_FLAG_BIG_ENDIAN)
#define I2C_RETRIES 3
#define I2C_RETRY_DELAY (5*MSEC)

#define LP8555_REG_COMMAND           0x00
#define  LP8555_REG_COMMAND_ON       0x01
#define LP8555_REG_CONFIG            0x10
#define  LP8555_REG_CONFIG_MODE_MASK 0x03
#define  LP8555_REG_CONFIG_MODE_PWM  0x00
#define LP8555_REG_CURRENT           0x11
#define  LP8555_REG_CURRENT_MAXCURR_5MA   0x00
#define  LP8555_REG_CURRENT_MAXCURR_10MA  0x01
#define  LP8555_REG_CURRENT_MAXCURR_15MA  0x02
#define  LP8555_REG_CURRENT_MAXCURR_20MA  0x03
#define  LP8555_REG_CURRENT_MAXCURR_23MA  0x04
#define  LP8555_REG_CURRENT_MAXCURR_25MA  0x05
#define  LP8555_REG_CURRENT_MAXCURR_30MA  0x06
#define  LP8555_REG_CURRENT_MAXCURR_50MA  0x07
#define LP8555_REG_STEP           0x15
#define  LP8555_REG_STEP_STEP_0MS           (0 << 0)
#define  LP8555_REG_STEP_STEP_8MS           BIT(0)
#define  LP8555_REG_STEP_STEP_16MS          (2 << 0)
#define  LP8555_REG_STEP_STEP_24MS          (3 << 0)
#define  LP8555_REG_STEP_STEP_28MS          (4 << 0)
#define  LP8555_REG_STEP_STEP_32MS          (5 << 0)
#define  LP8555_REG_STEP_STEP_100MS         (6 << 0)
#define  LP8555_REG_STEP_STEP_200MS         (7 << 0)
#define  LP8555_REG_STEP_PWM_IN_HYST_NONE   (0 << 3)
#define  LP8555_REG_STEP_PWM_IN_HYST_1LSB   BIT(3)
#define  LP8555_REG_STEP_PWM_IN_HYST_2LSB   (2 << 3)
#define  LP8555_REG_STEP_PWM_IN_HYST_4LSB   (3 << 3)
#define  LP8555_REG_STEP_PWM_IN_HYST_8LSB   (4 << 3)
#define  LP8555_REG_STEP_PWM_IN_HYST_16LSB  (5 << 3)
#define  LP8555_REG_STEP_PWM_IN_HYST_32LSB  (6 << 3)
#define  LP8555_REG_STEP_PWM_IN_HYST_64LSB  (7 << 3)
#define  LP8555_REG_STEP_SMOOTH_NONE        (0 << 6)
#define  LP8555_REG_STEP_SMOOTH_LIGHT       BIT(6)
#define  LP8555_REG_STEP_SMOOTH_MEDIUM      (2 << 6)
#define  LP8555_REG_STEP_SMOOTH_HEAVY       (3 << 6)

/* Read from lp8555 with automatic i2c retries */
static int lp8555_read_with_retry(int reg, int *data)
{
	int i, rv;

	for (i = 0; i < I2C_RETRIES; i++) {
		rv = i2c_read8(I2C_PORT_BACKLIGHT,
			       I2C_ADDR_BACKLIGHT_FLAGS,
			       reg, data);
		if (rv == EC_SUCCESS)
			return EC_SUCCESS;
		usleep(I2C_RETRY_DELAY);
	}

	CPRINTS("Backlight read fail: reg 0x%02x", reg);
	return rv;
}

/* Write to lp8555 with automatic i2c retries */
static int lp8555_write_with_retry(int reg, int data)
{
	int i, rv;

	for (i = 0; i < I2C_RETRIES; i++) {
		rv = i2c_write8(I2C_PORT_BACKLIGHT,
				I2C_ADDR_BACKLIGHT_FLAGS,
				reg, data);
		if (rv == EC_SUCCESS)
			return EC_SUCCESS;
		usleep(I2C_RETRY_DELAY);
	}

	CPRINTS("Backlight write fail: reg 0x%02x data %d", reg, data);
	return rv;
}

/**
 * Setup backlight controller and turn it on.
 */
static void lp8555_enable_pwm_mode(void)
{
	int reg;
	int rv;

	/*
	 * If not in S0, then PCH backlight enable will not be on, and if
	 * lid is closed EC backlight enable will not be on. Since these
	 * two signals are AND'ed together, no point in trying to talk to
	 * the lp8555 if either one of them is not true.
	 */
	if (!chipset_in_state(CHIPSET_STATE_ON) || !lid_is_open())
		return;

	/* Enable PWM mode. */
	rv = lp8555_read_with_retry(LP8555_REG_CONFIG, &reg);
	if (rv != EC_SUCCESS)
		return;
	reg &= ~LP8555_REG_CONFIG_MODE_MASK;
	reg |= LP8555_REG_CONFIG_MODE_PWM;
	rv = lp8555_write_with_retry(LP8555_REG_CONFIG, reg);
	if (rv != EC_SUCCESS)
		return;

	/* Set max LED current to 23mA. */
	rv = lp8555_write_with_retry(LP8555_REG_CURRENT,
				     LP8555_REG_CURRENT_MAXCURR_23MA);
	if (rv != EC_SUCCESS)
		return;

	/* Set the rate of brightness change. */
	rv = lp8555_write_with_retry(LP8555_REG_STEP,
				     LP8555_REG_STEP_STEP_200MS |
				     LP8555_REG_STEP_PWM_IN_HYST_8LSB |
				     LP8555_REG_STEP_SMOOTH_HEAVY);
	if (rv != EC_SUCCESS)
		return;

	/* Power on. */
	rv = lp8555_read_with_retry(LP8555_REG_COMMAND, &reg);
	if (rv != EC_SUCCESS)
		return;
	reg |= LP8555_REG_COMMAND_ON;
	rv = lp8555_write_with_retry(LP8555_REG_COMMAND, reg);
}
DECLARE_DEFERRED(lp8555_enable_pwm_mode);

/**
 * Host command to toggle backlight.
 */
static int switch_command_enable_backlight(struct host_cmd_handler_args *args)
{
	const struct ec_params_switch_enable_backlight *p = args->params;

	gpio_set_level(GPIO_ENABLE_BACKLIGHT, p->enabled);

	if (p->enabled)
		lp8555_enable_pwm_mode();

	return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_SWITCH_ENABLE_BKLIGHT,
		     switch_command_enable_backlight,
		     EC_VER_MASK(0));

void backlight_interrupt(enum gpio_signal signal)
{
	/*
	 * PCH indicates it is turning on backlight so we should
	 * attempt to put the backlight controller into PWM mode.
	 */
	hook_call_deferred(&lp8555_enable_pwm_mode_data, 0);
}

/**
 * Update backlight state.
 */
static void update_backlight(void)
{
	/*
	 * Enable backlight if lid is open; this is AND'd with the request from
	 * the AP in hardware.
	 */
	gpio_set_level(GPIO_ENABLE_BACKLIGHT, lid_is_open());
	if (lid_is_open())
		hook_call_deferred(&lp8555_enable_pwm_mode_data, 0);
}
DECLARE_HOOK(HOOK_LID_CHANGE, update_backlight, HOOK_PRIO_DEFAULT);

/**
 * Initialize backlight module.
 */
static void backlight_init(void)
{
	gpio_enable_interrupt(GPIO_PCH_BL_EN);
	gpio_set_level(GPIO_ENABLE_BACKLIGHT, lid_is_open());
}
DECLARE_HOOK(HOOK_INIT, backlight_init, HOOK_PRIO_DEFAULT);