All of lore.kernel.org
 help / color / mirror / Atom feed
From: Hans de Goede <hdegoede@redhat.com>
To: Andrey Smirnov <andrew.smirnov@gmail.com>,
	Hans de Goede <hans@hansg.org>
Cc: platform-driver-x86@vger.kernel.org,
	Mark Gross <markgross@kernel.org>,
	Jean Delvare <jdelvare@suse.com>,
	Guenter Roeck <linux@roeck-us.net>,
	linux-kernel <linux-kernel@vger.kernel.org>,
	linux-hwmon@vger.kernel.org
Subject: Re: [PATCH] platform/x86: Add Steam Deck driver
Date: Tue, 22 Feb 2022 18:17:05 +0100	[thread overview]
Message-ID: <e4aa0035-ff12-f6a7-a481-983f8cfc3dcb@redhat.com> (raw)
In-Reply-To: <CAHQ1cqHi5DmCB4ThQ_enr6BSOR-Oej19BprGZrphKeVzp5it4Q@mail.gmail.com>

Hi,

On 2/19/22 22:08, Andrey Smirnov wrote:
> On Thu, Feb 17, 2022 at 8:26 AM Hans de Goede <hans@hansg.org> wrote:
>>
>> Hi Andrey,
>>
>> On 2/6/22 03:20, Andrey Smirnov wrote:
>>> Add a driver exposing various bits and pieces of functionality
>>> provided by Steam Deck specific VLV0100 device presented by EC
>>> firmware. This includes but not limited to:
>>>
>>>     - CPU/device's fan control
>>>     - Read-only access to DDIC registers
>>>     - Battery tempreature measurements
>>>     - Various display related control knobs
>>>     - USB Type-C connector event notification
>>>
>>> Cc: Hans de Goede <hdegoede@redhat.com>
>>> Cc: Mark Gross <markgross@kernel.org>
>>> Cc: Jean Delvare <jdelvare@suse.com>
>>> Cc: Guenter Roeck <linux@roeck-us.net>
>>> Cc: linux-kernel@vger.kernel.org (open list)
>>> Cc: platform-driver-x86@vger.kernel.org
>>> Cc: linux-hwmon@vger.kernel.org
>>> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
>>
>> The .c file says: "Copyright (C) 2021-2022 Valve Corporation"
>> yet you are using a personal email address. This is not really
>> an issue, but it does look a bit weird.
>>
> 
> I'm not an FTE at Valve, although I might have a dedicated address in
> their e-mail system, however in my experience corporate emails
> addresses come and go, so I prefer to put an address a) I won't have
> to change few years down the road b) check regularly and try to
> respond promptly on, so almost all of my kernel contributions have
> been done using that address.

Ok, I understand thanks for the explaination.

Regards,

Hans





>>> ---
>>>
>>> This driver is really a kitchen sink of various small bits. Maybe it
>>> is worth splitting into an MFD + child drivers/devices?
>>
>> Yes with the extcon thing I think you should definitely go for
>> a MFD device. In which case the main driver registering the
>> regmap + the cells would go under drivers/mfd and most of the
>> other drivers would go in their own subsystems.
>>
>> And as the drivers/platform/x86/ subsystem maintainer I guess
>> that means I don't have to do much with this driver :)
>>
> 
> Yep, that's my plan :-)
> 
>> I would still be happy to yake any bits which don't fit
>> anywhere else attaching to say a "misc" MFD cell.
>>
>> Regards,
>>
>> Hans
>>
>>
>>
>>>
>>>  drivers/platform/x86/Kconfig     |  15 +
>>>  drivers/platform/x86/Makefile    |   2 +
>>>  drivers/platform/x86/steamdeck.c | 523 +++++++++++++++++++++++++++++++
>>>  3 files changed, 540 insertions(+)
>>>  create mode 100644 drivers/platform/x86/steamdeck.c
>>>
>>> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
>>> index c23612d98126..86f014e78a6e 100644
>>> --- a/drivers/platform/x86/Kconfig
>>> +++ b/drivers/platform/x86/Kconfig
>>> @@ -1136,6 +1136,21 @@ config SIEMENS_SIMATIC_IPC
>>>         To compile this driver as a module, choose M here: the module
>>>         will be called simatic-ipc.
>>>
>>> +config STEAMDECK
>>> +       tristate "Valve Steam Deck platform driver"
>>> +       depends on X86_64
>>> +       help
>>> +         Driver exposing various bits and pieces of functionality
>>> +      provided by Steam Deck specific VLV0100 device presented by
>>> +      EC firmware. This includes but not limited to:
>>> +          - CPU/device's fan control
>>> +          - Read-only access to DDIC registers
>>> +          - Battery tempreature measurements
>>> +          - Various display related control knobs
>>> +          - USB Type-C connector event notification
>>> +
>>> +      Say N unless you are running on a Steam Deck.
>>> +
>>>  endif # X86_PLATFORM_DEVICES
>>>
>>>  config PMC_ATOM
>>> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
>>> index c12a9b044fd8..2eb965e14ced 100644
>>> --- a/drivers/platform/x86/Makefile
>>> +++ b/drivers/platform/x86/Makefile
>>> @@ -129,3 +129,5 @@ obj-$(CONFIG_PMC_ATOM)                    += pmc_atom.o
>>>
>>>  # Siemens Simatic Industrial PCs
>>>  obj-$(CONFIG_SIEMENS_SIMATIC_IPC)    += simatic-ipc.o
>>> +
>>> +obj-$(CONFIG_STEAMDECK)                      += steamdeck.o
>>> diff --git a/drivers/platform/x86/steamdeck.c b/drivers/platform/x86/steamdeck.c
>>> new file mode 100644
>>> index 000000000000..77a6677ec19e
>>> --- /dev/null
>>> +++ b/drivers/platform/x86/steamdeck.c
>>> @@ -0,0 +1,523 @@
>>> +// SPDX-License-Identifier: GPL-2.0+
>>> +
>>> +/*
>>> + * Steam Deck ACPI platform driver
>>> + *
>>> + * Copyright (C) 2021-2022 Valve Corporation
>>> + *
>>> + */
>>> +#include <linux/acpi.h>
>>> +#include <linux/hwmon.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/extcon-provider.h>
>>> +
>>> +#define ACPI_STEAMDECK_NOTIFY_STATUS 0x80
>>> +
>>> +/* 0 - port connected, 1 -port disconnected */
>>> +#define ACPI_STEAMDECK_PORT_CONNECT  BIT(0)
>>> +/* 0 - Upstream Facing Port, 1 - Downdstream Facing Port */
>>> +#define ACPI_STEAMDECK_CUR_DATA_ROLE BIT(3)
>>> +/*
>>> + * Debouncing delay to allow negotiation process to settle. 2s value
>>> + * was arrived at via trial and error.
>>> + */
>>> +#define STEAMDECK_ROLE_SWITCH_DELAY  (msecs_to_jiffies(2000))
>>> +
>>> +struct steamdeck {
>>> +     struct acpi_device *adev;
>>> +     struct device *hwmon;
>>> +     void *regmap;
>>> +     long fan_target;
>>> +     struct delayed_work role_work;
>>> +     struct extcon_dev *edev;
>>> +     struct device *dev;
>>> +};
>>> +
>>> +static ssize_t
>>> +steamdeck_simple_store(struct device *dev, const char *buf, size_t count,
>>> +                    const char *method,
>>> +                    unsigned long upper_limit)
>>> +{
>>> +     struct steamdeck *fan = dev_get_drvdata(dev);
>>> +     unsigned long value;
>>> +
>>> +     if (kstrtoul(buf, 10, &value) || value >= upper_limit)
>>> +             return -EINVAL;
>>> +
>>> +     if (ACPI_FAILURE(acpi_execute_simple_method(fan->adev->handle,
>>> +                                                 (char *)method, value)))
>>> +             return -EIO;
>>> +
>>> +     return count;
>>> +}
>>> +
>>> +#define STEAMDECK_ATTR_WO(_name, _method, _upper_limit)                      \
>>> +     static ssize_t _name##_store(struct device *dev,                \
>>> +                                  struct device_attribute *attr,     \
>>> +                                  const char *buf, size_t count)     \
>>> +     {                                                               \
>>> +             return steamdeck_simple_store(dev, buf, count,          \
>>> +                                         _method,                    \
>>> +                                         _upper_limit);              \
>>> +     }                                                               \
>>> +     static DEVICE_ATTR_WO(_name)
>>> +
>>> +STEAMDECK_ATTR_WO(target_cpu_temp, "STCT", U8_MAX / 2);
>>> +STEAMDECK_ATTR_WO(gain, "SGAN", U16_MAX);
>>> +STEAMDECK_ATTR_WO(ramp_rate, "SFRR", U8_MAX);
>>> +STEAMDECK_ATTR_WO(hysteresis, "SHTS",  U16_MAX);
>>> +STEAMDECK_ATTR_WO(maximum_battery_charge_rate, "CHGR", U16_MAX);
>>> +STEAMDECK_ATTR_WO(recalculate, "SCHG", U16_MAX);
>>> +
>>> +STEAMDECK_ATTR_WO(led_brightness, "CHBV", U8_MAX);
>>> +STEAMDECK_ATTR_WO(content_adaptive_brightness, "CABC", U8_MAX);
>>> +STEAMDECK_ATTR_WO(gamma_set, "GAMA", U8_MAX);
>>> +STEAMDECK_ATTR_WO(display_brightness, "WDBV", U8_MAX);
>>> +STEAMDECK_ATTR_WO(ctrl_display, "WCDV", U8_MAX);
>>> +STEAMDECK_ATTR_WO(cabc_minimum_brightness, "WCMB", U8_MAX);
>>> +STEAMDECK_ATTR_WO(memory_data_access_control, "MDAC", U8_MAX);
>>> +
>>> +#define STEAMDECK_ATTR_WO_NOARG(_name, _method)                              \
>>> +     static ssize_t _name##_store(struct device *dev,                \
>>> +                                  struct device_attribute *attr,     \
>>> +                                  const char *buf, size_t count)     \
>>> +     {                                                               \
>>> +             struct steamdeck *fan = dev_get_drvdata(dev);           \
>>> +                                                                     \
>>> +             if (ACPI_FAILURE(acpi_evaluate_object(fan->adev->handle, \
>>> +                                                   _method, NULL, NULL))) \
>>> +                     return -EIO;                                    \
>>> +                                                                     \
>>> +             return count;                                           \
>>> +     }                                                               \
>>> +     static DEVICE_ATTR_WO(_name)
>>> +
>>> +STEAMDECK_ATTR_WO_NOARG(power_cycle_display, "DPCY");
>>> +STEAMDECK_ATTR_WO_NOARG(display_normal_mode_on, "NORO");
>>> +STEAMDECK_ATTR_WO_NOARG(display_inversion_off, "INOF");
>>> +STEAMDECK_ATTR_WO_NOARG(display_inversion_on, "INON");
>>> +STEAMDECK_ATTR_WO_NOARG(idle_mode_on, "WRNE");
>>> +
>>> +#define STEAMDECK_ATTR_RO(_name, _method)                            \
>>> +     static ssize_t _name##_show(struct device *dev,                 \
>>> +                                 struct device_attribute *attr,      \
>>> +                                 char *buf)                          \
>>> +     {                                                               \
>>> +             struct steamdeck *jup = dev_get_drvdata(dev);           \
>>> +             unsigned long long val;                                 \
>>> +                                                                     \
>>> +             if (ACPI_FAILURE(acpi_evaluate_integer(                 \
>>> +                                      jup->adev->handle,             \
>>> +                                      _method, NULL, &val)))         \
>>> +                     return -EIO;                                    \
>>> +                                                                     \
>>> +             return sprintf(buf, "%llu\n", val);                     \
>>> +     }                                                               \
>>> +     static DEVICE_ATTR_RO(_name)
>>> +
>>> +STEAMDECK_ATTR_RO(firmware_version, "PDFW");
>>> +STEAMDECK_ATTR_RO(board_id, "BOID");
>>> +STEAMDECK_ATTR_RO(pdcs, "PDCS");
>>> +
>>> +static umode_t
>>> +steamdeck_is_visible(struct kobject *kobj, struct attribute *attr, int index)
>>> +{
>>> +     return attr->mode;
>>> +}
>>> +
>>> +static struct attribute *steamdeck_attributes[] = {
>>> +     &dev_attr_target_cpu_temp.attr,
>>> +     &dev_attr_gain.attr,
>>> +     &dev_attr_ramp_rate.attr,
>>> +     &dev_attr_hysteresis.attr,
>>> +     &dev_attr_maximum_battery_charge_rate.attr,
>>> +     &dev_attr_recalculate.attr,
>>> +     &dev_attr_power_cycle_display.attr,
>>> +
>>> +     &dev_attr_led_brightness.attr,
>>> +     &dev_attr_content_adaptive_brightness.attr,
>>> +     &dev_attr_gamma_set.attr,
>>> +     &dev_attr_display_brightness.attr,
>>> +     &dev_attr_ctrl_display.attr,
>>> +     &dev_attr_cabc_minimum_brightness.attr,
>>> +     &dev_attr_memory_data_access_control.attr,
>>> +
>>> +     &dev_attr_display_normal_mode_on.attr,
>>> +     &dev_attr_display_inversion_off.attr,
>>> +     &dev_attr_display_inversion_on.attr,
>>> +     &dev_attr_idle_mode_on.attr,
>>> +
>>> +     &dev_attr_firmware_version.attr,
>>> +     &dev_attr_board_id.attr,
>>> +     &dev_attr_pdcs.attr,
>>> +
>>> +     NULL
>>> +};
>>> +
>>> +static const struct attribute_group steamdeck_group = {
>>> +     .attrs = steamdeck_attributes,
>>> +     .is_visible = steamdeck_is_visible,
>>> +};
>>> +
>>> +static const struct attribute_group *steamdeck_groups[] = {
>>> +     &steamdeck_group,
>>> +     NULL
>>> +};
>>> +
>>> +static int steamdeck_read_fan_speed(struct steamdeck *jup, long *speed)
>>> +{
>>> +     unsigned long long val;
>>> +
>>> +     if (ACPI_FAILURE(acpi_evaluate_integer(jup->adev->handle,
>>> +                                            "FANR", NULL, &val)))
>>> +             return -EIO;
>>> +
>>> +     *speed = val;
>>> +     return 0;
>>> +}
>>> +
>>> +static int
>>> +steamdeck_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
>>> +                  u32 attr, int channel, long *out)
>>> +{
>>> +     struct steamdeck *sd = dev_get_drvdata(dev);
>>> +     unsigned long long val;
>>> +
>>> +     switch (type) {
>>> +     case hwmon_temp:
>>> +             if (attr != hwmon_temp_input)
>>> +                     return -EOPNOTSUPP;
>>> +
>>> +             if (ACPI_FAILURE(acpi_evaluate_integer(sd->adev->handle,
>>> +                                                    "BATT", NULL, &val)))
>>> +                     return -EIO;
>>> +             /*
>>> +              * Assuming BATT returns deg C we need to mutiply it
>>> +              * by 1000 to convert to mC
>>> +              */
>>> +             *out = val * 1000;
>>> +             break;
>>> +     case hwmon_fan:
>>> +             switch (attr) {
>>> +             case hwmon_fan_input:
>>> +                     return steamdeck_read_fan_speed(sd, out);
>>> +             case hwmon_fan_target:
>>> +                     *out = sd->fan_target;
>>> +                     break;
>>> +             case hwmon_fan_fault:
>>> +                     if (ACPI_FAILURE(acpi_evaluate_integer(
>>> +                                              sd->adev->handle,
>>> +                                              "FANC", NULL, &val)))
>>> +                             return -EIO;
>>> +                     /*
>>> +                      * FANC (Fan check):
>>> +                      * 0: Abnormal
>>> +                      * 1: Normal
>>> +                      */
>>> +                     *out = !val;
>>> +                     break;
>>> +             default:
>>> +                     return -EOPNOTSUPP;
>>> +             }
>>> +             break;
>>> +     default:
>>> +             return -EOPNOTSUPP;
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int
>>> +steamdeck_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
>>> +                         u32 attr, int channel, const char **str)
>>> +{
>>> +     switch (type) {
>>> +     case hwmon_temp:
>>> +             *str = "Battery Temp";
>>> +             break;
>>> +     case hwmon_fan:
>>> +             *str = "System Fan";
>>> +             break;
>>> +     default:
>>> +             return -EOPNOTSUPP;
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int
>>> +steamdeck_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
>>> +                   u32 attr, int channel, long val)
>>> +{
>>> +     struct steamdeck *sd = dev_get_drvdata(dev);
>>> +
>>> +     if (type != hwmon_fan ||
>>> +         attr != hwmon_fan_target)
>>> +             return -EOPNOTSUPP;
>>> +
>>> +     if (val > U16_MAX)
>>> +             return -EINVAL;
>>> +
>>> +     sd->fan_target = val;
>>> +
>>> +     if (ACPI_FAILURE(acpi_execute_simple_method(sd->adev->handle,
>>> +                                                 "FANS", val)))
>>> +             return -EIO;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static umode_t
>>> +steamdeck_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
>>> +                        u32 attr, int channel)
>>> +{
>>> +     if (type == hwmon_fan &&
>>> +         attr == hwmon_fan_target)
>>> +             return 0644;
>>> +
>>> +     return 0444;
>>> +}
>>> +
>>> +static const struct hwmon_channel_info *steamdeck_info[] = {
>>> +     HWMON_CHANNEL_INFO(temp,
>>> +                        HWMON_T_INPUT | HWMON_T_LABEL),
>>> +     HWMON_CHANNEL_INFO(fan,
>>> +                        HWMON_F_INPUT | HWMON_F_LABEL |
>>> +                        HWMON_F_TARGET | HWMON_F_FAULT),
>>> +     NULL
>>> +};
>>> +
>>> +static const struct hwmon_ops steamdeck_hwmon_ops = {
>>> +     .is_visible = steamdeck_hwmon_is_visible,
>>> +     .read = steamdeck_hwmon_read,
>>> +     .read_string = steamdeck_hwmon_read_string,
>>> +     .write = steamdeck_hwmon_write,
>>> +};
>>> +
>>> +static const struct hwmon_chip_info steamdeck_chip_info = {
>>> +     .ops = &steamdeck_hwmon_ops,
>>> +     .info = steamdeck_info,
>>> +};
>>> +
>>> +#define STEAMDECK_STA_OK                     \
>>> +     (ACPI_STA_DEVICE_ENABLED |              \
>>> +      ACPI_STA_DEVICE_PRESENT |              \
>>> +      ACPI_STA_DEVICE_FUNCTIONING)
>>> +
>>> +static int
>>> +steamdeck_ddic_reg_read(void *context, unsigned int reg, unsigned int *val)
>>> +{
>>> +     union acpi_object obj = { .type = ACPI_TYPE_INTEGER };
>>> +     struct acpi_object_list arg_list = { .count = 1, .pointer = &obj, };
>>> +     struct steamdeck *sd = context;
>>> +     unsigned long long _val;
>>> +
>>> +     obj.integer.value = reg;
>>> +
>>> +     if (ACPI_FAILURE(acpi_evaluate_integer(sd->adev->handle,
>>> +                                            "RDDI", &arg_list, &_val)))
>>> +             return -EIO;
>>> +
>>> +     *val = _val;
>>> +     return 0;
>>> +}
>>> +
>>> +static int steamdeck_read_pdcs(struct steamdeck *sd, unsigned long long *pdcs)
>>> +{
>>> +     acpi_status status;
>>> +
>>> +     status = acpi_evaluate_integer(sd->adev->handle, "PDCS", NULL, pdcs);
>>> +     if (ACPI_FAILURE(status)) {
>>> +             dev_err(sd->dev, "PDCS evaluation failed: %s\n",
>>> +                     acpi_format_exception(status));
>>> +             return -EIO;
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static void steamdeck_usb_role_work(struct work_struct *work)
>>> +{
>>> +     struct steamdeck *sd =
>>> +             container_of(work, struct steamdeck, role_work.work);
>>> +     unsigned long long pdcs;
>>> +     bool usb_host;
>>> +
>>> +     if (steamdeck_read_pdcs(sd, &pdcs))
>>> +             return;
>>> +
>>> +     /*
>>> +      * We only care about these two
>>> +      */
>>> +     pdcs &= ACPI_STEAMDECK_PORT_CONNECT | ACPI_STEAMDECK_CUR_DATA_ROLE;
>>> +
>>> +     /*
>>> +      * For "connect" events our role is determined by a bit in
>>> +      * PDCS, for "disconnect" we switch to being a gadget
>>> +      * unconditionally. The thinking for the latter is we don't
>>> +      * want to start acting as a USB host until we get
>>> +      * confirmation from the firmware that we are a USB host
>>> +      */
>>> +     usb_host = (pdcs & ACPI_STEAMDECK_PORT_CONNECT) ?
>>> +             pdcs & ACPI_STEAMDECK_CUR_DATA_ROLE : false;
>>> +
>>> +     WARN_ON(extcon_set_state_sync(sd->edev, EXTCON_USB_HOST,
>>> +                                   usb_host));
>>> +     dev_dbg(sd->dev, "USB role is %s\n", usb_host ? "host" : "device");
>>> +}
>>> +
>>> +static void steamdeck_notify(acpi_handle handle, u32 event, void *context)
>>> +{
>>> +     struct device *dev = context;
>>> +     struct steamdeck *sd = dev_get_drvdata(dev);
>>> +     unsigned long long pdcs;
>>> +     unsigned long delay;
>>> +
>>> +     switch (event) {
>>> +     case ACPI_STEAMDECK_NOTIFY_STATUS:
>>> +             if (steamdeck_read_pdcs(sd, &pdcs))
>>> +                     return;
>>> +             /*
>>> +              * We process "disconnect" events immediately and
>>> +              * "connect" events with a delay to give the HW time
>>> +              * to settle. For example attaching USB hub (at least
>>> +              * for HW used for testing) will generate intermediary
>>> +              * event with "host" bit not set, followed by the one
>>> +              * that does have it set.
>>> +              */
>>> +             delay = (pdcs & ACPI_STEAMDECK_PORT_CONNECT) ?
>>> +                     STEAMDECK_ROLE_SWITCH_DELAY : 0;
>>> +
>>> +             queue_delayed_work(system_long_wq, &sd->role_work, delay);
>>> +             break;
>>> +     default:
>>> +             dev_err(dev, "Unsupported event [0x%x]\n", event);
>>> +     }
>>> +}
>>> +
>>> +static void steamdeck_remove_notify_handler(void *data)
>>> +{
>>> +     struct steamdeck *sd = data;
>>> +
>>> +     acpi_remove_notify_handler(sd->adev->handle, ACPI_DEVICE_NOTIFY,
>>> +                                steamdeck_notify);
>>> +     cancel_delayed_work_sync(&sd->role_work);
>>> +}
>>> +
>>> +static const unsigned int steamdeck_extcon_cable[] = {
>>> +     EXTCON_USB,
>>> +     EXTCON_USB_HOST,
>>> +     EXTCON_CHG_USB_SDP,
>>> +     EXTCON_CHG_USB_CDP,
>>> +     EXTCON_CHG_USB_DCP,
>>> +     EXTCON_CHG_USB_ACA,
>>> +     EXTCON_NONE,
>>> +};
>>> +
>>> +static int steamdeck_probe(struct platform_device *pdev)
>>> +{
>>> +     struct device *dev = &pdev->dev;
>>> +     struct steamdeck *sd;
>>> +     acpi_status status;
>>> +     unsigned long long sta;
>>> +     int ret;
>>> +
>>> +     static const struct regmap_config regmap_config = {
>>> +             .reg_bits = 8,
>>> +             .val_bits = 8,
>>> +             .max_register = 255,
>>> +             .cache_type = REGCACHE_NONE,
>>> +             .reg_read = steamdeck_ddic_reg_read,
>>> +     };
>>> +
>>> +     sd = devm_kzalloc(dev, sizeof(*sd), GFP_KERNEL);
>>> +     if (!sd)
>>> +             return -ENOMEM;
>>> +     sd->adev = ACPI_COMPANION(&pdev->dev);
>>> +     sd->dev = dev;
>>> +     platform_set_drvdata(pdev, sd);
>>> +     INIT_DELAYED_WORK(&sd->role_work, steamdeck_usb_role_work);
>>> +
>>> +     status = acpi_evaluate_integer(sd->adev->handle, "_STA",
>>> +                                    NULL, &sta);
>>> +     if (ACPI_FAILURE(status)) {
>>> +             dev_err(dev, "Status check failed (0x%x)\n", status);
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     if ((sta & STEAMDECK_STA_OK) != STEAMDECK_STA_OK) {
>>> +             dev_err(dev, "Device is not ready\n");
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     /*
>>> +      * Our ACPI interface doesn't expose a method to read current
>>> +      * fan target, so we use current fan speed as an
>>> +      * approximation.
>>> +      */
>>> +     if (steamdeck_read_fan_speed(sd, &sd->fan_target))
>>> +             dev_warn(dev, "Failed to read fan speed");
>>> +
>>> +     sd->hwmon = devm_hwmon_device_register_with_info(dev,
>>> +                                                      "steamdeck",
>>> +                                                      sd,
>>> +                                                      &steamdeck_chip_info,
>>> +                                                      steamdeck_groups);
>>> +     if (IS_ERR(sd->hwmon)) {
>>> +             dev_err(dev, "Failed to register HWMON device");
>>> +             return PTR_ERR(sd->hwmon);
>>> +     }
>>> +
>>> +     sd->regmap = devm_regmap_init(dev, NULL, sd, &regmap_config);
>>> +     if (IS_ERR(sd->regmap))
>>> +             dev_err(dev, "Failed to register REGMAP");
>>> +
>>> +     sd->edev = devm_extcon_dev_allocate(dev, steamdeck_extcon_cable);
>>> +     if (IS_ERR(sd->edev))
>>> +             return -ENOMEM;
>>> +
>>> +     ret = devm_extcon_dev_register(dev, sd->edev);
>>> +     if (ret < 0) {
>>> +             dev_err(dev, "Failed to register extcon device: %d\n", ret);
>>> +             return ret;
>>> +     }
>>> +
>>> +     /*
>>> +      * Set initial role value
>>> +      */
>>> +     queue_delayed_work(system_long_wq, &sd->role_work, 0);
>>> +     flush_delayed_work(&sd->role_work);
>>> +
>>> +     status = acpi_install_notify_handler(sd->adev->handle,
>>> +                                          ACPI_DEVICE_NOTIFY,
>>> +                                          steamdeck_notify,
>>> +                                          dev);
>>> +     if (ACPI_FAILURE(status)) {
>>> +             dev_err(dev, "Error installing ACPI notify handler\n");
>>> +             return -EIO;
>>> +     }
>>> +
>>> +     ret = devm_add_action_or_reset(dev, steamdeck_remove_notify_handler,
>>> +                                    sd);
>>> +     return ret;
>>> +}
>>> +
>>> +static const struct acpi_device_id steamdeck_device_ids[] = {
>>> +     { "VLV0100", 0 },
>>> +     { "", 0 },
>>> +};
>>> +MODULE_DEVICE_TABLE(acpi, steamdeck_device_ids);
>>> +
>>> +static struct platform_driver steamdeck_driver = {
>>> +     .probe = steamdeck_probe,
>>> +     .driver = {
>>> +             .name = "steamdeck",
>>> +             .acpi_match_table = steamdeck_device_ids,
>>> +     },
>>> +};
>>> +module_platform_driver(steamdeck_driver);
>>> +
>>> +MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
>>> +MODULE_DESCRIPTION("Steam Deck ACPI platform driver");
>>> +MODULE_LICENSE("GPL");
>>> --
>>> 2.25.1
>>>
> 


  reply	other threads:[~2022-02-22 17:17 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-02-06  2:20 [PATCH] platform/x86: Add Steam Deck driver Andrey Smirnov
2022-02-06  5:33 ` Guenter Roeck
2022-02-12 22:36   ` Andrey Smirnov
2022-02-06 15:19 ` Barnabás Pőcze
2022-02-12 23:30   ` Andrey Smirnov
2022-02-17 16:28     ` Hans de Goede
2022-02-06 17:51 ` Guenter Roeck
2022-02-12 23:34   ` Andrey Smirnov
2022-02-08  8:51 ` Greg KH
2022-02-12 23:37   ` Andrey Smirnov
2024-04-24 15:40     ` Clayton Craft
2024-05-12 17:21       ` Andrey Smirnov
2022-02-17 16:26 ` Hans de Goede
2022-02-17 16:41   ` Hans de Goede
2022-02-19 21:08   ` Andrey Smirnov
2022-02-22 17:17     ` Hans de Goede [this message]
2022-02-17 16:35 ` Hans de Goede

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=e4aa0035-ff12-f6a7-a481-983f8cfc3dcb@redhat.com \
    --to=hdegoede@redhat.com \
    --cc=andrew.smirnov@gmail.com \
    --cc=hans@hansg.org \
    --cc=jdelvare@suse.com \
    --cc=linux-hwmon@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@roeck-us.net \
    --cc=markgross@kernel.org \
    --cc=platform-driver-x86@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.