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
195
|
/* 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, ®);
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, ®);
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 enum ec_status
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);
|