diff --git a/Documentation/devicetree/bindings/iio/light/dynaimage,ap3216c.yaml b/Documentation/devicetree/bindings/iio/light/dynaimage,ap3216c.yaml new file mode 100644 index 00000000000000..a7ebd7be1a2525 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/dynaimage,ap3216c.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/dynaimage,ap3216c.yaml# +$schema: http://devicetree.org/schemas/meta-schemas/core.yaml# + +title: DynaImage AP3216C / AP3426 Ambient Light and Proximity Sensor + +maintainers: + - Vladislav Dubrovin + +description: | + DynaImage AP3216C and AP3426 are integrated digital ambient light, + proximity, and IR sensor. + + Specifications about the devices can be found at: + https://github.com/RT-Thread-packages/ap3216c/blob/master/ap3216c_datasheet.pdf + +properties: + compatible: + oneOf: + - items: + - const: dynaimage,ap3426 + - const: dynaimage,ap3216c + - enum: + - dynaimage,ap3216c + - dynaimage,ap3426 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: + description: Regulator powering the sensor VDD pin. + + vddio-supply: + description: Regulator powering the sensor VDDIO pin. + + proximity-near-level: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Raw proximity threshold level above which an object is considered "near". + default: 300 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@1e { + compatible = "dynaimage,ap3426", "dynaimage,ap3216c"; + reg = <0x1e>; + + interrupt-parent = <&tlmm>; + interrupts = <113 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&pm8916_l17>; + vddio-supply = <&pm8916_l6>; + proximity-near-level = <300>; + + pinctrl-0 = <&als_int_default>; + pinctrl-names = "default"; + }; + }; diff --git a/arch/arm64/boot/dts/qcom/msm8916-asus-z010d.dts b/arch/arm64/boot/dts/qcom/msm8916-asus-z010d.dts index fb0b88efdea961..ef4cb02bdf38e6 100644 --- a/arch/arm64/boot/dts/qcom/msm8916-asus-z010d.dts +++ b/arch/arm64/boot/dts/qcom/msm8916-asus-z010d.dts @@ -7,6 +7,8 @@ #include #include +#include +#include #include / { @@ -26,51 +28,50 @@ gpio-keys { compatible = "gpio-keys"; + label = "GPIO Buttons"; pinctrl-0 = <&gpio_keys_default>; pinctrl-names = "default"; - label = "GPIO Buttons"; - volume-up-button { label = "Volume Up"; - gpios = <&tlmm 107 GPIO_ACTIVE_LOW>; linux,code = ; + gpios = <&tlmm 107 GPIO_ACTIVE_LOW>; debounce-interval = <15>; }; }; gpio-hall-sensor { compatible = "gpio-keys"; + label = "GPIO Hall Effect Sensor"; pinctrl-0 = <&gpio_hall_sensor_default>; pinctrl-names = "default"; - label = "GPIO Hall Effect Sensor"; - hall-sensor-switch { label = "Hall Effect Sensor"; - gpios = <&tlmm 115 GPIO_ACTIVE_LOW>; linux,input-type = ; linux,code = ; linux,can-disable; + gpios = <&tlmm 115 GPIO_ACTIVE_LOW>; }; }; speaker_amp: audio-amplifier { compatible = "awinic,aw8738"; + sound-name-prefix = "Speaker Amp"; + mode-gpios = <&tlmm 112 GPIO_ACTIVE_HIGH>; pinctrl-0 = <&spk_ext_pa_default>; pinctrl-names = "default"; - mode-gpios = <&tlmm 112 GPIO_ACTIVE_HIGH>; awinic,mode = <2>; - sound-name-prefix = "Speaker Amp"; }; usb_id: usb-id { compatible = "linux,extcon-usb-gpio"; id-gpios = <&tlmm 110 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&usb_id_default>; pinctrl-names = "default"; }; @@ -89,6 +90,10 @@ vdd-supply = <&pm8916_l17>; vddio-supply = <&pm8916_l6>; + mount-matrix = "-1", "0", "0", + "0", "-1", "0", + "0", "0", "1"; + pinctrl-0 = <&accel_int_default>; pinctrl-names = "default"; }; @@ -97,15 +102,28 @@ compatible = "asahi-kasei,ak09911"; reg = <0x0c>; + reset-gpios = <&tlmm 36 GPIO_ACTIVE_LOW>; vdd-supply = <&pm8916_l17>; vid-supply = <&pm8916_l6>; - reset-gpios = <&tlmm 36 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&mag_reset_default>; pinctrl-names = "default"; }; + light-sensor@1e { + compatible = "dynaimage,ap3426", "dynaimage,ap3216c"; + reg = <0x1e>; + + interrupt-parent = <&tlmm>; + interrupts = <113 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&pm8916_l17>; + vddio-supply = <&pm8916_l6>; + proximity-near-level = <300>; + + pinctrl-0 = <&als_int_default>; + pinctrl-names = "default"; + }; + led-controller@45 { compatible = "awinic,aw2013"; reg = <0x45>; @@ -117,16 +135,39 @@ led@0 { reg = <0>; - led-max-microamp = <5000>; function = LED_FUNCTION_INDICATOR; color = ; + led-max-microamp = <5000>; }; led@1 { reg = <1>; - led-max-microamp = <5000>; function = LED_FUNCTION_INDICATOR; color = ; + led-max-microamp = <5000>; + }; + }; +}; + +&blsp_i2c4 { + status = "okay"; + + charger@14 { + compatible = "qcom,smb1360"; + reg = <0x14>; + + interrupt-parent = <&tlmm>; + interrupts = <62 IRQ_TYPE_LEVEL_LOW>; + + qcom,float-voltage-mv = <4380>; + qcom,iterm-ma = <200>; + qcom,recharge-thresh-mv = <100>; + + pinctrl-0 = <&smb1360_default>; + pinctrl-names = "default"; + + usb-otg-vbus { + regulator-name = "smb1360_otg_vreg"; }; }; }; @@ -142,11 +183,10 @@ interrupt-parent = <&tlmm>; interrupts = <13 IRQ_TYPE_EDGE_FALLING>; + reset-gpios = <&tlmm 12 GPIO_ACTIVE_LOW>; vcc-supply = <&pm8916_l17>; iovcc-supply = <&pm8916_l6>; - reset-gpios = <&tlmm 12 GPIO_ACTIVE_LOW>; - touchscreen-size-x = <720>; touchscreen-size-y = <1280>; @@ -176,8 +216,8 @@ compatible = "asus,z010d-r69339"; reg = <0>; - power-supply = <&pm8916_l17>; reset-gpios = <&tlmm 25 GPIO_ACTIVE_LOW>; + power-supply = <&pm8916_l17>; port { panel_in: endpoint { @@ -228,21 +268,18 @@ }; &sdhc_2 { + cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&sdc2_default &sdc2_cd_default>; pinctrl-1 = <&sdc2_sleep &sdc2_cd_default>; pinctrl-names = "default", "sleep"; - cd-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; - status = "okay"; }; &sound { model = "bq-paella"; - widgets = - "Speaker", "Speaker", - "Headphone", "Headphones"; - pin-switches = "Speaker"; + audio-routing = "Speaker", "Speaker Amp OUT", "Speaker Amp IN", "HPH_R", @@ -252,6 +289,10 @@ "AMIC2", "MIC BIAS Internal2", "AMIC3", "MIC BIAS Internal3"; aux-devs = <&speaker_amp>; + pin-switches = "Speaker"; + widgets = + "Speaker", "Speaker", + "Headphone", "Headphones"; }; &usb { @@ -284,6 +325,20 @@ }; &tlmm { + als_int_default: als-int-default-state { + pins = "gpio113"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + smb1360_default: smb1360-default-state { + pins = "gpio62"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + accel_int_default: accel-int-default-state { pins = "gpio114"; function = "gpio"; diff --git a/arch/arm64/configs/msm8916_defconfig b/arch/arm64/configs/msm8916_defconfig index 001d6fcc9a6238..776dfeb616d729 100644 --- a/arch/arm64/configs/msm8916_defconfig +++ b/arch/arm64/configs/msm8916_defconfig @@ -460,3 +460,4 @@ CONFIG_DYNAMIC_DEBUG=y CONFIG_MAGIC_SYSRQ=y CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x0 CONFIG_DEBUG_FS=y +CONFIG_AP3216C=m diff --git a/drivers/gpu/drm/panel/msm8916-generated/panel-asus-z010d-r69339.c b/drivers/gpu/drm/panel/msm8916-generated/panel-asus-z010d-r69339.c index 2b9f06fbdca079..8ec53350d52acf 100644 --- a/drivers/gpu/drm/panel/msm8916-generated/panel-asus-z010d-r69339.c +++ b/drivers/gpu/drm/panel/msm8916-generated/panel-asus-z010d-r69339.c @@ -43,12 +43,14 @@ static int r69339_on(struct r69339 *ctx) { struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; - mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x0001); - mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, - 0x24); + mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0xffff); /* awake brightness quickfix */ + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24); mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); - mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 20); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); /*changed awake direction */ + mipi_dsi_msleep(&dsi_ctx, 150); return dsi_ctx.accum_err; diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index ac1408d374c920..edaec80cf5a3c7 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -73,6 +73,16 @@ config AL3320A To compile this driver as a module, choose M here: the module will be called al3320a. +config AP3216C + tristate "AP3216C/AP3426 ambient light sensor" + depends on I2C + help + Say Y here if you want to build a driver for the Dynaimage + AP3216C or AP3426 ambient light and proximity sensor. + + To compile this driver as a module, choose M here: the + module will be called ap3216c. + config APDS9160 tristate "APDS9160 combined als and proximity sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index c0048e0d5ca8f8..4b66abbfaa164d 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ADUX1020) += adux1020.o obj-$(CONFIG_AL3000A) += al3000a.o obj-$(CONFIG_AL3010) += al3010.o obj-$(CONFIG_AL3320A) += al3320a.o +obj-$(CONFIG_AP3216C) += ap3216c.o obj-$(CONFIG_APDS9160) += apds9160.o obj-$(CONFIG_APDS9300) += apds9300.o obj-$(CONFIG_APDS9306) += apds9306.o diff --git a/drivers/iio/light/ap3216c.c b/drivers/iio/light/ap3216c.c new file mode 100644 index 00000000000000..993461682694fd --- /dev/null +++ b/drivers/iio/light/ap3216c.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Dynaimage AP3216C/AP3426 Ambient Light Sensor/Proximity/IR Driver + * + * Copyright (c) Vladislav Dubrovin + * + * Data sheets: + * https://github.com/RT-Thread-packages/ap3216c/blob/master/ap3216c_datasheet.pdf + * + */ + +#include +#include +#include +#include +#include +#include + +/* Registers and masks */ +#define AP3426_REG_SYS_CONF 0x00 +#define AP3426_REG_SYS_INTSTATUS 0x01 +#define AP3426_REG_SYS_INTCTRL 0x02 + +#define AP3426_REG_IR_DATA_LOW 0x0A +#define AP3426_REG_ALS_DATA_LOW 0x0C +#define AP3426_REG_PS_DATA_LOW 0x0E + +/* System modes */ +#define AP3426_SYS_DEV_DOWN 0x00 +#define AP3426_SYS_ALS_ENABLE 0x01 +#define AP3426_SYS_PS_ENABLE 0x02 +#define AP3426_SYS_ALS_PS_ENABLE 0x03 +#define AP3426_SYS_DEV_RESET 0x04 + +struct ap3216c_data { + struct i2c_client *client; + struct mutex lock; + u32 prox_near_level; +}; + +/* --- Nearlevel attribute --- */ +static ssize_t ap3216c_read_nearlevel(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ap3216c_data *data = iio_priv(indio_dev); + + return sysfs_emit(buf, "%u\n", data->prox_near_level); +} +static const struct iio_chan_spec_ext_info ap3216c_prox_ext_info[] = { + { + .name = "nearlevel", + .shared = IIO_SEPARATE, + .read = ap3216c_read_nearlevel, + }, + { } +}; +/* ---------------------------------------------- */ + +static const struct iio_chan_spec ap3216c_channels[] = { + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_IR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .ext_info = ap3216c_prox_ext_info, + }, +}; + +/** + * ap3216c_read_raw_data() - Read 16-bit raw data from the sensor + * @client: I2C client structure + * @reg_low: Address of the lower byte register + * + * The sensor stores 16-bit data in two consecutive registers (little-endian). + * + * Return: 16-bit positive integer on success, negative error code on failure. + */ +static int ap3216c_read_raw_data(struct i2c_client *client, u8 reg_low) +{ + return i2c_smbus_read_word_data(client, reg_low); +} + +/* Smart "on-demand" measurement with fixed delay */ +static int ap3216c_get_measurement(struct ap3216c_data *data, u8 mode, + u8 reg_low, int delay_ms, int *val) +{ + struct i2c_client *client = data->client; + int ret, ret2; + + ret = i2c_smbus_write_byte_data(client, AP3426_REG_SYS_CONF, mode); + if (ret < 0) + return ret; + + msleep(delay_ms); + + ret = ap3216c_read_raw_data(client, reg_low); + if (ret < 0) + goto out_power_down; + + *val = ret; + ret = 0; + +out_power_down: + ret2 = i2c_smbus_write_byte_data(client, AP3426_REG_SYS_CONF, + AP3426_SYS_DEV_DOWN); + if (ret == 0 && ret2 < 0) + ret = ret2; + + return ret; +} + +static int ap3216c_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ap3216c_data *data = iio_priv(indio_dev); + int ret, raw_val; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + mutex_lock(&data->lock); + + switch (chan->type) { + case IIO_INTENSITY: + ret = ap3216c_get_measurement(data, AP3426_SYS_PS_ENABLE, + AP3426_REG_IR_DATA_LOW, 20, &raw_val); + if (ret < 0) + goto out_unlock; + *val = raw_val & 0x3FF; + ret = IIO_VAL_INT; + break; + + case IIO_LIGHT: + ret = ap3216c_get_measurement(data, AP3426_SYS_ALS_ENABLE, + AP3426_REG_ALS_DATA_LOW, 120, &raw_val); + if (ret < 0) + goto out_unlock; + *val = raw_val & 0xFFFF; + ret = IIO_VAL_INT; + break; + + case IIO_PROXIMITY: + ret = ap3216c_get_measurement(data, AP3426_SYS_PS_ENABLE, + AP3426_REG_PS_DATA_LOW, 20, &raw_val); + if (ret < 0) + goto out_unlock; + *val = raw_val & 0x3FF; + ret = IIO_VAL_INT; + break; + + default: + ret = -EINVAL; + } + +out_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static const struct iio_info ap3216c_info = { + .read_raw = ap3216c_read_raw, +}; + +static int ap3216c_chip_init(struct i2c_client *client) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, AP3426_REG_SYS_CONF, AP3426_SYS_DEV_RESET); + if (ret < 0) + return ret; + + msleep(20); + + ret = i2c_smbus_write_byte_data(client, AP3426_REG_SYS_CONF, AP3426_SYS_DEV_DOWN); + if (ret < 0) + return ret; + + return 0; +} + +static int ap3216c_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct ap3216c_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + mutex_init(&data->lock); + + if (device_property_read_u32(&client->dev, "proximity-near-level", + &data->prox_near_level)) { + data->prox_near_level = 300; + dev_warn(&client->dev, + "proximity near-level property missing, using default %u\n", + data->prox_near_level); + } + + indio_dev->name = "ap3216c"; + indio_dev->info = &ap3216c_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ap3216c_channels; + indio_dev->num_channels = ARRAY_SIZE(ap3216c_channels); + + ret = ap3216c_chip_init(client); + if (ret < 0) + return dev_err_probe(&client->dev, ret, "Failed to reset chip via I2C\n"); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id ap3216c_of_match[] = { + { .compatible = "dynaimage,ap3426" }, + { .compatible = "dynaimage,ap3216c" }, + { } +}; +MODULE_DEVICE_TABLE(of, ap3216c_of_match); + +static const struct i2c_device_id ap3216c_id[] = { + { "ap3426", 0 }, + { "ap3216c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ap3216c_id); + +static struct i2c_driver ap3216c_driver = { + .driver = { + .name = "ap3216c", + .of_match_table = ap3216c_of_match, + }, + .probe = ap3216c_probe, + .id_table = ap3216c_id, +}; +module_i2c_driver(ap3216c_driver); + +MODULE_AUTHOR("Vladislav Dubrovin "); +MODULE_DESCRIPTION("AP3216C/AP3426 Ambient Light Sensor/Proximity/IR Driver"); +MODULE_LICENSE("GPL");