156 lines
4.3 KiB
C
156 lines
4.3 KiB
C
/*
|
|
* Ambient light sensor driver for Nintendo Switch's Rohm BH1730
|
|
*
|
|
* Copyright (c) 2018 CTCaer
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "als.h"
|
|
#include <power/max7762x.h>
|
|
#include <soc/clock.h>
|
|
#include <soc/i2c.h>
|
|
#include <soc/pinmux.h>
|
|
#include <utils/util.h>
|
|
|
|
#define BH1730_DEFAULT_GAIN BH1730_GAIN_64X
|
|
#define BH1730_DEFAULT_ICYCLE 38
|
|
|
|
#define BH1730_INTERNAL_CLOCK_NS 2800
|
|
#define BH1730_ADC_CALC_DELAY_US 2000 /* BH1730_INTERNAL_CLOCK_MS * 714 */
|
|
#define BH1730_ITIME_CYCLE_TO_US 2700 /* BH1730_INTERNAL_CLOCK_MS * 964 */
|
|
|
|
#define BH1730_DEFAULT_ITIME_MS 100
|
|
|
|
#define BH1730_LUX_MULTIPLIER 3600
|
|
#define BH1730_LUX_MULTIPLIER_AULA 1410
|
|
|
|
#define BH1730_LUX_MAX 100000
|
|
|
|
typedef struct _opt_win_cal_t
|
|
{
|
|
u32 rc;
|
|
u32 cv;
|
|
u32 ci;
|
|
} opt_win_cal_t;
|
|
|
|
// Nintendo Switch Icosa/Iowa Optical Window calibration.
|
|
const opt_win_cal_t opt_win_cal_default[] = {
|
|
{ 500, 5002, 7502 },
|
|
{ 754, 2250, 2000 },
|
|
{ 1029, 1999, 1667 },
|
|
{ 1373, 884, 583 },
|
|
{ 1879, 309, 165 }
|
|
};
|
|
|
|
// Nintendo Switch Aula Optical Window calibration.
|
|
const opt_win_cal_t opt_win_cal_aula[] = {
|
|
{ 231, 9697, 30300 },
|
|
{ 993, 3333, 2778 },
|
|
{ 1478, 1621, 1053 },
|
|
{ 7500, 81, 10 }
|
|
};
|
|
|
|
const u32 als_gain_idx_tbl[4] = { 1, 2, 64, 128 };
|
|
|
|
void set_als_cfg(als_ctxt_t *als_ctxt, u8 gain, u8 cycle)
|
|
{
|
|
if (gain > BH1730_GAIN_128X)
|
|
gain = BH1730_GAIN_128X;
|
|
|
|
if (!cycle)
|
|
cycle = 1;
|
|
else if (cycle > 255)
|
|
cycle = 255;
|
|
|
|
i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_GAIN_REG), gain);
|
|
i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_TIMING_REG), (256 - cycle));
|
|
|
|
als_ctxt->gain = gain;
|
|
als_ctxt->cycle = cycle;
|
|
}
|
|
|
|
void get_als_lux(als_ctxt_t *als_ctxt)
|
|
{
|
|
u32 data[2];
|
|
u32 visible_light;
|
|
u32 ir_light;
|
|
u64 lux = 0;
|
|
u32 itime_us = BH1730_ITIME_CYCLE_TO_US * als_ctxt->cycle;
|
|
|
|
// Get visible and ir light raw data. Mode is continuous so waiting for new values doesn't matter.
|
|
data[0] = i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA0LOW_REG)) +
|
|
(i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA0HIGH_REG)) << 8);
|
|
data[1] = i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA1LOW_REG)) +
|
|
(i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA1HIGH_REG)) << 8);
|
|
|
|
visible_light = data[0];
|
|
ir_light = data[1];
|
|
|
|
als_ctxt->over_limit = visible_light > 65534 || ir_light > 65534;
|
|
als_ctxt->vi_light = visible_light;
|
|
als_ctxt->ir_light = ir_light;
|
|
|
|
if (!visible_light)
|
|
{
|
|
als_ctxt->lux = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
// Set calibration parameters.
|
|
u32 lux_multiplier = BH1730_LUX_MULTIPLIER;
|
|
u32 opt_win_cal_count = ARRAY_SIZE(opt_win_cal_default);
|
|
const opt_win_cal_t *opt_win_cal = opt_win_cal_default;
|
|
|
|
// Apply optical window calibration coefficients.
|
|
for (u32 i = 0; i < opt_win_cal_count; i++)
|
|
{
|
|
if (1000 * ir_light / visible_light < opt_win_cal[i].rc)
|
|
{
|
|
lux = ((u64)opt_win_cal[i].cv * data[0]) - (opt_win_cal[i].ci * data[1]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
lux *= BH1730_DEFAULT_ITIME_MS * lux_multiplier;
|
|
lux /= als_gain_idx_tbl[als_ctxt->gain] * itime_us;
|
|
lux /= 1000;
|
|
|
|
if (lux > BH1730_LUX_MAX)
|
|
lux = BH1730_LUX_MAX;
|
|
|
|
als_ctxt->lux = lux;
|
|
}
|
|
|
|
u8 als_power_on(als_ctxt_t *als_ctxt)
|
|
{
|
|
// Enable power to ALS IC.
|
|
max7762x_regulator_set_voltage(REGULATOR_LDO6, 2900000);
|
|
max7762x_regulator_enable(REGULATOR_LDO6, true);
|
|
|
|
// Init I2C2.
|
|
pinmux_config_i2c(I2C_2);
|
|
clock_enable_i2c(I2C_2);
|
|
i2c_init(I2C_2);
|
|
|
|
// Initialize ALS.
|
|
u8 id = i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(0x12));
|
|
i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_SPEC(BH1730_SPECCMD_RESET), 0);
|
|
|
|
set_als_cfg(als_ctxt, BH1730_DEFAULT_GAIN, BH1730_DEFAULT_ICYCLE);
|
|
|
|
i2c_send_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_CONTROL_REG), BH1730_CTL_POWER_ON | BH1730_CTL_ADC_EN);
|
|
|
|
return id;
|
|
}
|