/* * 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 . */ #include "als.h" #include #include #include #include #include #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 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)) + (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); vi_light = data[0]; ir_light = data[1]; als_ctxt->vi_light = vi_light; als_ctxt->ir_light = ir_light; als_ctxt->over_limit = vi_light > 65534 || ir_light > 65534; 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++) { 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; }