Hi, On 1/30/23 11:12, Sakari Ailus wrote: > Hi Hans, > > On Fri, Jan 27, 2023 at 09:37:27PM +0100, Hans de Goede wrote: >> On some systems, e.g. the Lenovo ThinkPad X1 Yoga gen 7 and the ThinkPad >> X1 Nano gen 2 there is no clock-enable pin, triggering the: >> "No clk GPIO. The privacy LED won't work" warning and causing the privacy >> LED to not work. >> >> Fix this by modeling the privacy LED as a LED class device rather then >> integrating it with the registered clock. >> >> Note this relies on media subsys changes to actually turn the LED on/off >> when the sensor's v4l2_subdev's s_stream() operand gets called. >> >> Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx> >> --- >> Changes in v4: >> - Make struct led_classdev the first member of the pled struct >> - Use strchr to replace the : with _ in the acpi_dev_name() >> --- >> drivers/platform/x86/intel/int3472/Makefile | 2 +- >> .../x86/intel/int3472/clk_and_regulator.c | 3 - >> drivers/platform/x86/intel/int3472/common.h | 15 +++- >> drivers/platform/x86/intel/int3472/discrete.c | 58 ++++----------- >> drivers/platform/x86/intel/int3472/led.c | 74 +++++++++++++++++++ >> 5 files changed, 105 insertions(+), 47 deletions(-) >> create mode 100644 drivers/platform/x86/intel/int3472/led.c >> >> diff --git a/drivers/platform/x86/intel/int3472/Makefile b/drivers/platform/x86/intel/int3472/Makefile >> index cfec7784c5c9..9f16cb514397 100644 >> --- a/drivers/platform/x86/intel/int3472/Makefile >> +++ b/drivers/platform/x86/intel/int3472/Makefile >> @@ -1,4 +1,4 @@ >> obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \ >> intel_skl_int3472_tps68470.o >> -intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o common.o >> +intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o led.o common.o >> intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o common.o >> diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c >> index 74dc2cff799e..e3b597d93388 100644 >> --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c >> +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c >> @@ -23,8 +23,6 @@ static int skl_int3472_clk_prepare(struct clk_hw *hw) >> struct int3472_gpio_clock *clk = to_int3472_clk(hw); >> >> gpiod_set_value_cansleep(clk->ena_gpio, 1); >> - gpiod_set_value_cansleep(clk->led_gpio, 1); >> - >> return 0; >> } >> >> @@ -33,7 +31,6 @@ static void skl_int3472_clk_unprepare(struct clk_hw *hw) >> struct int3472_gpio_clock *clk = to_int3472_clk(hw); >> >> gpiod_set_value_cansleep(clk->ena_gpio, 0); >> - gpiod_set_value_cansleep(clk->led_gpio, 0); >> } >> >> static int skl_int3472_clk_enable(struct clk_hw *hw) >> diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h >> index 53270d19c73a..82dc37e08882 100644 >> --- a/drivers/platform/x86/intel/int3472/common.h >> +++ b/drivers/platform/x86/intel/int3472/common.h >> @@ -6,6 +6,7 @@ >> >> #include <linux/clk-provider.h> >> #include <linux/gpio/machine.h> >> +#include <linux/leds.h> >> #include <linux/regulator/driver.h> >> #include <linux/regulator/machine.h> >> #include <linux/types.h> >> @@ -28,6 +29,8 @@ >> #define GPIO_REGULATOR_NAME_LENGTH 21 >> #define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9 >> >> +#define INT3472_LED_MAX_NAME_LEN 32 >> + >> #define CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET 86 >> >> #define INT3472_REGULATOR(_name, _supply, _ops) \ >> @@ -96,10 +99,16 @@ struct int3472_discrete_device { >> struct clk_hw clk_hw; >> struct clk_lookup *cl; >> struct gpio_desc *ena_gpio; >> - struct gpio_desc *led_gpio; >> u32 frequency; >> } clock; >> >> + struct int3472_pled { >> + struct led_classdev classdev; >> + struct led_lookup_data lookup; >> + char name[INT3472_LED_MAX_NAME_LEN]; >> + struct gpio_desc *gpio; >> + } pled; >> + >> unsigned int ngpios; /* how many GPIOs have we seen */ >> unsigned int n_sensor_gpios; /* how many have we mapped to sensor */ >> struct gpiod_lookup_table gpios; >> @@ -119,4 +128,8 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, >> struct acpi_resource_gpio *agpio); >> void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); >> >> +int skl_int3472_register_pled(struct int3472_discrete_device *int3472, >> + struct acpi_resource_gpio *agpio, u32 polarity); >> +void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472); >> + >> #endif >> diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c >> index 708d51f9b41d..38b1372e0745 100644 >> --- a/drivers/platform/x86/intel/int3472/discrete.c >> +++ b/drivers/platform/x86/intel/int3472/discrete.c >> @@ -155,37 +155,21 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347 >> } >> >> static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472, >> - struct acpi_resource_gpio *agpio, u8 type) >> + struct acpi_resource_gpio *agpio) >> { >> char *path = agpio->resource_source.string_ptr; >> u16 pin = agpio->pin_table[0]; >> struct gpio_desc *gpio; >> >> - switch (type) { >> - case INT3472_GPIO_TYPE_CLK_ENABLE: >> - gpio = acpi_get_and_request_gpiod(path, pin, "int3472,clk-enable"); >> - if (IS_ERR(gpio)) >> - return (PTR_ERR(gpio)); >> - >> - int3472->clock.ena_gpio = gpio; >> - /* Ensure the pin is in output mode and non-active state */ >> - gpiod_direction_output(int3472->clock.ena_gpio, 0); >> - break; >> - case INT3472_GPIO_TYPE_PRIVACY_LED: >> - gpio = acpi_get_and_request_gpiod(path, pin, "int3472,privacy-led"); >> - if (IS_ERR(gpio)) >> - return (PTR_ERR(gpio)); >> + gpio = acpi_get_and_request_gpiod(path, pin, "int3472,clk-enable"); >> + if (IS_ERR(gpio)) >> + return (PTR_ERR(gpio)); >> >> - int3472->clock.led_gpio = gpio; >> - /* Ensure the pin is in output mode and non-active state */ >> - gpiod_direction_output(int3472->clock.led_gpio, 0); >> - break; >> - default: >> - dev_err(int3472->dev, "Invalid GPIO type 0x%02x for clock\n", type); >> - break; >> - } >> + int3472->clock.ena_gpio = gpio; >> + /* Ensure the pin is in output mode and non-active state */ >> + gpiod_direction_output(int3472->clock.ena_gpio, 0); >> >> - return 0; >> + return skl_int3472_register_clock(int3472); >> } >> >> static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polarity) >> @@ -293,11 +277,16 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, >> >> break; >> case INT3472_GPIO_TYPE_CLK_ENABLE: >> - case INT3472_GPIO_TYPE_PRIVACY_LED: >> - ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); >> + ret = skl_int3472_map_gpio_to_clk(int3472, agpio); >> if (ret) >> err_msg = "Failed to map GPIO to clock\n"; >> >> + break; >> + case INT3472_GPIO_TYPE_PRIVACY_LED: >> + ret = skl_int3472_register_pled(int3472, agpio, polarity); >> + if (ret) >> + err_msg = "Failed to register LED\n"; >> + >> break; >> case INT3472_GPIO_TYPE_POWER_ENABLE: >> ret = skl_int3472_register_regulator(int3472, agpio); >> @@ -341,21 +330,6 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) >> >> acpi_dev_free_resource_list(&resource_list); >> >> - /* >> - * If we find no clock enable GPIO pin then the privacy LED won't work. >> - * We've never seen that situation, but it's possible. Warn the user so >> - * it's clear what's happened. >> - */ >> - if (int3472->clock.ena_gpio) { >> - ret = skl_int3472_register_clock(int3472); >> - if (ret) >> - return ret; >> - } else { >> - if (int3472->clock.led_gpio) >> - dev_warn(int3472->dev, >> - "No clk GPIO. The privacy LED won't work\n"); >> - } >> - >> int3472->gpios.dev_id = int3472->sensor_name; >> gpiod_add_lookup_table(&int3472->gpios); >> >> @@ -372,8 +346,8 @@ static int skl_int3472_discrete_remove(struct platform_device *pdev) >> skl_int3472_unregister_clock(int3472); >> >> gpiod_put(int3472->clock.ena_gpio); >> - gpiod_put(int3472->clock.led_gpio); >> >> + skl_int3472_unregister_pled(int3472); >> skl_int3472_unregister_regulator(int3472); >> >> return 0; >> diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c >> new file mode 100644 >> index 000000000000..251c6524458e >> --- /dev/null >> +++ b/drivers/platform/x86/intel/int3472/led.c >> @@ -0,0 +1,74 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* Author: Hans de Goede <hdegoede@xxxxxxxxxx> */ >> + >> +#include <linux/acpi.h> >> +#include <linux/gpio/consumer.h> >> +#include <linux/leds.h> >> +#include "common.h" >> + >> +static int int3472_pled_set(struct led_classdev *led_cdev, >> + enum led_brightness brightness) >> +{ >> + struct int3472_discrete_device *int3472 = >> + container_of(led_cdev, struct int3472_discrete_device, pled.classdev); >> + >> + gpiod_set_value_cansleep(int3472->pled.gpio, brightness); >> + return 0; >> +} >> + >> +int skl_int3472_register_pled(struct int3472_discrete_device *int3472, >> + struct acpi_resource_gpio *agpio, u32 polarity) >> +{ >> + char *p, *path = agpio->resource_source.string_ptr; >> + int ret; >> + >> + if (int3472->pled.classdev.dev) >> + return -EBUSY; >> + >> + int3472->pled.gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0], >> + "int3472,privacy-led"); >> + if (IS_ERR(int3472->pled.gpio)) >> + return dev_err_probe(int3472->dev, PTR_ERR(int3472->pled.gpio), >> + "getting privacy LED GPIO\n"); >> + >> + if (polarity == GPIO_ACTIVE_LOW) >> + gpiod_toggle_active_low(int3472->pled.gpio); >> + >> + /* Ensure the pin is in output mode and non-active state */ >> + gpiod_direction_output(int3472->pled.gpio, 0); >> + >> + /* Generate the name, replacing the ':' in the ACPI devname with '_' */ >> + snprintf(int3472->pled.name, sizeof(int3472->pled.name), >> + "%s::privacy_led", acpi_dev_name(int3472->sensor)); >> + p = strchr(int3472->pled.name, ':'); >> + *p = '_'; > > While I suppose ACPI device names generally are shorter than > sizeof(int3472->pled.name), it'd be nice to still check p is non-NULL here, > just to be sure. Sure, I've added a check for this while merging this. Regards, Hans > >> + >> + int3472->pled.classdev.name = int3472->pled.name; >> + int3472->pled.classdev.max_brightness = 1; >> + int3472->pled.classdev.brightness_set_blocking = int3472_pled_set; >> + >> + ret = led_classdev_register(int3472->dev, &int3472->pled.classdev); >> + if (ret) >> + goto err_free_gpio; >> + >> + int3472->pled.lookup.provider = int3472->pled.name; >> + int3472->pled.lookup.dev_id = int3472->sensor_name; >> + int3472->pled.lookup.con_id = "privacy-led"; >> + led_add_lookup(&int3472->pled.lookup); >> + >> + return 0; >> + >> +err_free_gpio: >> + gpiod_put(int3472->pled.gpio); >> + return ret; >> +} >> + >> +void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472) >> +{ >> + if (IS_ERR_OR_NULL(int3472->pled.classdev.dev)) >> + return; >> + >> + led_remove_lookup(&int3472->pled.lookup); >> + led_classdev_unregister(&int3472->pled.classdev); >> + gpiod_put(int3472->pled.gpio); >> +} >