hekate/bdk/input/als.c

157 lines
4.3 KiB
C
Raw Permalink Normal View History

/*
* 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.
2024-06-02 04:09:34 +00:00
static 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.
2024-06-02 04:09:34 +00:00
static const opt_win_cal_t opt_win_cal_aula[] = {
{ 231, 9697, 30300 },
{ 993, 3333, 2778 },
{ 1478, 1621, 1053 },
{ 7500, 81, 10 }
};
2024-06-02 04:09:34 +00:00
static 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;
2022-07-11 19:10:11 +00:00
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];
2022-07-11 19:10:11 +00:00
u32 vi_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)) +
2022-07-11 19:10:11 +00:00
(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)) +
2022-07-11 19:10:11 +00:00
(i2c_recv_byte(I2C_2, BH1730_I2C_ADDR, BH1730_ADDR(BH1730_DATA1HIGH_REG)) << 8);
2022-07-11 19:10:11 +00:00
vi_light = data[0];
ir_light = data[1];
2022-07-11 19:10:11 +00:00
als_ctxt->vi_light = vi_light;
als_ctxt->ir_light = ir_light;
2022-07-11 19:10:11 +00:00
als_ctxt->over_limit = vi_light > 65534 || ir_light > 65534;
2022-07-11 19:10:11 +00:00
if (!vi_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++)
{
2022-07-11 19:10:11 +00:00
if (1000 * ir_light / vi_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;
}