Re: [PATCH] platform/x86: Add Steam Deck driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 2/19/22 22:08, Andrey Smirnov wrote:
> On Thu, Feb 17, 2022 at 8:26 AM Hans de Goede <hans@xxxxxxxxx> 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@xxxxxxxxxx>
>>> Cc: Mark Gross <markgross@xxxxxxxxxx>
>>> Cc: Jean Delvare <jdelvare@xxxxxxxx>
>>> Cc: Guenter Roeck <linux@xxxxxxxxxxxx>
>>> Cc: linux-kernel@xxxxxxxxxxxxxxx (open list)
>>> Cc: platform-driver-x86@xxxxxxxxxxxxxxx
>>> Cc: linux-hwmon@xxxxxxxxxxxxxxx
>>> Signed-off-by: Andrey Smirnov <andrew.smirnov@xxxxxxxxx>
>>
>> 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@xxxxxxxxx>");
>>> +MODULE_DESCRIPTION("Steam Deck ACPI platform driver");
>>> +MODULE_LICENSE("GPL");
>>> --
>>> 2.25.1
>>>
> 




[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux