Merge tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
[linux-2.6-block.git] / drivers / soundwire / intel_auxdevice.c
index 0daa6ca9a224e1f92249bb4b3bc95e7e2c7d8fa5..7f15e3549e539d053a82b63b8346a362c6d0145c 100644 (file)
@@ -23,9 +23,6 @@
 #include "intel.h"
 #include "intel_auxdevice.h"
 
-/* IDA min selected to avoid conflicts with HDaudio/iDISP SDI values */
-#define INTEL_DEV_NUM_IDA_MIN           4
-
 #define INTEL_MASTER_SUSPEND_DELAY_MS  3000
 
 /*
@@ -44,6 +41,39 @@ static int md_flags;
 module_param_named(sdw_md_flags, md_flags, int, 0444);
 MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)");
 
+struct wake_capable_part {
+       const u16 mfg_id;
+       const u16 part_id;
+};
+
+static struct wake_capable_part wake_capable_list[] = {
+       {0x025d, 0x5682},
+       {0x025d, 0x700},
+       {0x025d, 0x711},
+       {0x025d, 0x1712},
+       {0x025d, 0x1713},
+       {0x025d, 0x1716},
+       {0x025d, 0x1717},
+       {0x025d, 0x712},
+       {0x025d, 0x713},
+       {0x025d, 0x714},
+       {0x025d, 0x715},
+       {0x025d, 0x716},
+       {0x025d, 0x717},
+       {0x025d, 0x722},
+};
+
+static bool is_wake_capable(struct sdw_slave *slave)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(wake_capable_list); i++)
+               if (slave->id.part_id == wake_capable_list[i].part_id &&
+                   slave->id.mfg_id == wake_capable_list[i].mfg_id)
+                       return true;
+       return false;
+}
+
 static int generic_pre_bank_switch(struct sdw_bus *bus)
 {
        struct sdw_cdns *cdns = bus_to_cdns(bus);
@@ -60,18 +90,32 @@ static int generic_post_bank_switch(struct sdw_bus *bus)
        return sdw->link_res->hw_ops->post_bank_switch(sdw);
 }
 
-static void generic_new_peripheral_assigned(struct sdw_bus *bus, int dev_num)
+static void generic_new_peripheral_assigned(struct sdw_bus *bus,
+                                           struct sdw_slave *slave,
+                                           int dev_num)
 {
        struct sdw_cdns *cdns = bus_to_cdns(bus);
        struct sdw_intel *sdw = cdns_to_intel(cdns);
+       int dev_num_min;
+       int dev_num_max;
+       bool wake_capable = slave->prop.wake_capable || is_wake_capable(slave);
+
+       if (wake_capable) {
+               dev_num_min = SDW_INTEL_DEV_NUM_IDA_MIN;
+               dev_num_max = SDW_MAX_DEVICES;
+       } else {
+               dev_num_min = 1;
+               dev_num_max = SDW_INTEL_DEV_NUM_IDA_MIN - 1;
+       }
 
        /* paranoia check, this should never happen */
-       if (dev_num < INTEL_DEV_NUM_IDA_MIN || dev_num > SDW_MAX_DEVICES)  {
-               dev_err(bus->dev, "%s: invalid dev_num %d\n", __func__, dev_num);
+       if (dev_num < dev_num_min || dev_num > dev_num_max)  {
+               dev_err(bus->dev, "%s: invalid dev_num %d, wake supported %d\n",
+                       __func__, dev_num, slave->prop.wake_capable);
                return;
        }
 
-       if (sdw->link_res->hw_ops->program_sdi)
+       if (sdw->link_res->hw_ops->program_sdi && wake_capable)
                sdw->link_res->hw_ops->program_sdi(sdw, dev_num);
 }
 
@@ -123,6 +167,30 @@ static int intel_prop_read(struct sdw_bus *bus)
        return 0;
 }
 
+static DEFINE_IDA(intel_peripheral_ida);
+
+static int intel_get_device_num_ida(struct sdw_bus *bus, struct sdw_slave *slave)
+{
+       int bit;
+
+       if (slave->prop.wake_capable || is_wake_capable(slave))
+               return ida_alloc_range(&intel_peripheral_ida,
+                                      SDW_INTEL_DEV_NUM_IDA_MIN, SDW_MAX_DEVICES,
+                                      GFP_KERNEL);
+
+       bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES);
+       if (bit == SDW_MAX_DEVICES)
+               return -ENODEV;
+
+       return bit;
+}
+
+static void intel_put_device_num_ida(struct sdw_bus *bus, struct sdw_slave *slave)
+{
+       if (slave->prop.wake_capable || is_wake_capable(slave))
+               ida_free(&intel_peripheral_ida, slave->dev_num);
+}
+
 static struct sdw_master_ops sdw_intel_ops = {
        .read_prop = intel_prop_read,
        .override_adr = sdw_dmi_override_adr,
@@ -132,6 +200,8 @@ static struct sdw_master_ops sdw_intel_ops = {
        .pre_bank_switch = generic_pre_bank_switch,
        .post_bank_switch = generic_post_bank_switch,
        .read_ping_status = cdns_read_ping_status,
+       .get_device_num =  intel_get_device_num_ida,
+       .put_device_num = intel_put_device_num_ida,
        .new_peripheral_assigned = generic_new_peripheral_assigned,
 };
 
@@ -165,7 +235,6 @@ static int intel_link_probe(struct auxiliary_device *auxdev,
        cdns->msg_count = 0;
 
        bus->link_id = auxdev->id;
-       bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN;
        bus->clk_stop_timeout = 1;
 
        sdw_cdns_probe(cdns);
@@ -248,13 +317,6 @@ int intel_link_startup(struct auxiliary_device *auxdev)
 
        sdw_intel_debugfs_init(sdw);
 
-       /* start bus */
-       ret = sdw_intel_start_bus(sdw);
-       if (ret) {
-               dev_err(dev, "bus start failed: %d\n", ret);
-               goto err_power_up;
-       }
-
        /* Enable runtime PM */
        if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) {
                pm_runtime_set_autosuspend_delay(dev,
@@ -264,6 +326,15 @@ int intel_link_startup(struct auxiliary_device *auxdev)
 
                pm_runtime_set_active(dev);
                pm_runtime_enable(dev);
+
+               pm_runtime_resume(bus->dev);
+       }
+
+       /* start bus */
+       ret = sdw_intel_start_bus(sdw);
+       if (ret) {
+               dev_err(dev, "bus start failed: %d\n", ret);
+               goto err_pm_runtime;
        }
 
        clock_stop_quirks = sdw->link_res->clock_stop_quirks;
@@ -293,12 +364,18 @@ int intel_link_startup(struct auxiliary_device *auxdev)
         * with a delay. A more complete solution would require the
         * definition of Master properties.
         */
-       if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
+       if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) {
+               pm_runtime_mark_last_busy(bus->dev);
+               pm_runtime_mark_last_busy(dev);
                pm_runtime_idle(dev);
+       }
 
        sdw->startup_done = true;
        return 0;
 
+err_pm_runtime:
+       if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME))
+               pm_runtime_disable(dev);
 err_power_up:
        sdw_intel_link_power_down(sdw);
 err_init:
@@ -552,6 +629,8 @@ static int __maybe_unused intel_resume(struct device *dev)
                pm_runtime_mark_last_busy(dev);
                pm_runtime_enable(dev);
 
+               pm_runtime_resume(bus->dev);
+
                link_flags = md_flags >> (bus->link_id * 8);
 
                if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE))
@@ -587,6 +666,7 @@ static int __maybe_unused intel_resume(struct device *dev)
         * counters and delay the pm_runtime suspend by several
         * seconds, by when all enumeration should be complete.
         */
+       pm_runtime_mark_last_busy(bus->dev);
        pm_runtime_mark_last_busy(dev);
 
        return 0;