1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
2 | // Copyright(c) 2015-2021 Intel Corporation. |
3 | |
4 | /* |
5 | * SDW Intel ACPI scan helpers |
6 | */ |
7 | |
8 | #include <linux/acpi.h> |
9 | #include <linux/bits.h> |
10 | #include <linux/bitfield.h> |
11 | #include <linux/device.h> |
12 | #include <linux/errno.h> |
13 | #include <linux/export.h> |
14 | #include <linux/fwnode.h> |
15 | #include <linux/module.h> |
16 | #include <linux/soundwire/sdw_intel.h> |
17 | #include <linux/string.h> |
18 | |
19 | #define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */ |
20 | #define SDW_MAX_LINKS 4 |
21 | |
22 | static int ctrl_link_mask; |
23 | module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444); |
24 | MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)" ); |
25 | |
26 | static ulong ctrl_addr = 0x40000000; |
27 | module_param_named(sdw_ctrl_addr, ctrl_addr, ulong, 0444); |
28 | MODULE_PARM_DESC(sdw_ctrl_addr, "Intel SoundWire Controller _ADR" ); |
29 | |
30 | static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx) |
31 | { |
32 | struct fwnode_handle *link; |
33 | char name[32]; |
34 | u32 quirk_mask = 0; |
35 | |
36 | /* Find master handle */ |
37 | snprintf(buf: name, size: sizeof(name), |
38 | fmt: "mipi-sdw-link-%hhu-subproperties" , idx); |
39 | |
40 | link = fwnode_get_named_child_node(fwnode: fw_node, childname: name); |
41 | if (!link) |
42 | return false; |
43 | |
44 | fwnode_property_read_u32(fwnode: link, |
45 | propname: "intel-quirk-mask" , |
46 | val: &quirk_mask); |
47 | |
48 | if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) |
49 | return false; |
50 | |
51 | return true; |
52 | } |
53 | |
54 | static int |
55 | sdw_intel_scan_controller(struct sdw_intel_acpi_info *info) |
56 | { |
57 | struct acpi_device *adev = acpi_fetch_acpi_dev(handle: info->handle); |
58 | u8 count, i; |
59 | int ret; |
60 | |
61 | if (!adev) |
62 | return -EINVAL; |
63 | |
64 | /* Found controller, find links supported */ |
65 | count = 0; |
66 | ret = fwnode_property_read_u8_array(fwnode: acpi_fwnode_handle(adev), |
67 | propname: "mipi-sdw-master-count" , val: &count, nval: 1); |
68 | |
69 | /* |
70 | * In theory we could check the number of links supported in |
71 | * hardware, but in that step we cannot assume SoundWire IP is |
72 | * powered. |
73 | * |
74 | * In addition, if the BIOS doesn't even provide this |
75 | * 'master-count' property then all the inits based on link |
76 | * masks will fail as well. |
77 | * |
78 | * We will check the hardware capabilities in the startup() step |
79 | */ |
80 | |
81 | if (ret) { |
82 | dev_err(&adev->dev, |
83 | "Failed to read mipi-sdw-master-count: %d\n" , ret); |
84 | return -EINVAL; |
85 | } |
86 | |
87 | /* Check count is within bounds */ |
88 | if (count > SDW_MAX_LINKS) { |
89 | dev_err(&adev->dev, "Link count %d exceeds max %d\n" , |
90 | count, SDW_MAX_LINKS); |
91 | return -EINVAL; |
92 | } |
93 | |
94 | if (!count) { |
95 | dev_warn(&adev->dev, "No SoundWire links detected\n" ); |
96 | return -EINVAL; |
97 | } |
98 | dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n" , count); |
99 | |
100 | info->count = count; |
101 | info->link_mask = 0; |
102 | |
103 | for (i = 0; i < count; i++) { |
104 | if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) { |
105 | dev_dbg(&adev->dev, |
106 | "Link %d masked, will not be enabled\n" , i); |
107 | continue; |
108 | } |
109 | |
110 | if (!is_link_enabled(fw_node: acpi_fwnode_handle(adev), idx: i)) { |
111 | dev_dbg(&adev->dev, |
112 | "Link %d not selected in firmware\n" , i); |
113 | continue; |
114 | } |
115 | |
116 | info->link_mask |= BIT(i); |
117 | } |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, |
123 | void *cdata, void **return_value) |
124 | { |
125 | struct sdw_intel_acpi_info *info = cdata; |
126 | acpi_status status; |
127 | u64 adr; |
128 | |
129 | status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, data: &adr); |
130 | if (ACPI_FAILURE(status)) |
131 | return AE_OK; /* keep going */ |
132 | |
133 | if (!acpi_fetch_acpi_dev(handle)) { |
134 | pr_err("%s: Couldn't find ACPI handle\n" , __func__); |
135 | return AE_NOT_FOUND; |
136 | } |
137 | |
138 | /* |
139 | * On some Intel platforms, multiple children of the HDAS |
140 | * device can be found, but only one of them is the SoundWire |
141 | * controller. The SNDW device is always exposed with |
142 | * Name(_ADR, 0x40000000), with bits 31..28 representing the |
143 | * SoundWire link so filter accordingly |
144 | */ |
145 | if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE) |
146 | return AE_OK; /* keep going */ |
147 | |
148 | if (adr != ctrl_addr) |
149 | return AE_OK; /* keep going */ |
150 | |
151 | /* found the correct SoundWire controller */ |
152 | info->handle = handle; |
153 | |
154 | /* device found, stop namespace walk */ |
155 | return AE_CTRL_TERMINATE; |
156 | } |
157 | |
158 | /** |
159 | * sdw_intel_acpi_scan() - SoundWire Intel init routine |
160 | * @parent_handle: ACPI parent handle |
161 | * @info: description of what firmware/DSDT tables expose |
162 | * |
163 | * This scans the namespace and queries firmware to figure out which |
164 | * links to enable. A follow-up use of sdw_intel_probe() and |
165 | * sdw_intel_startup() is required for creation of devices and bus |
166 | * startup |
167 | */ |
168 | int sdw_intel_acpi_scan(acpi_handle *parent_handle, |
169 | struct sdw_intel_acpi_info *info) |
170 | { |
171 | acpi_status status; |
172 | |
173 | info->handle = NULL; |
174 | /* |
175 | * In the HDAS ACPI scope, 'SNDW' may be either the child of |
176 | * 'HDAS' or the grandchild of 'HDAS'. So let's go through |
177 | * the ACPI from 'HDAS' at max depth of 2 to find the 'SNDW' |
178 | * device. |
179 | */ |
180 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, |
181 | start_object: parent_handle, max_depth: 2, |
182 | descending_callback: sdw_intel_acpi_cb, |
183 | NULL, context: info, NULL); |
184 | if (ACPI_FAILURE(status) || info->handle == NULL) |
185 | return -ENODEV; |
186 | |
187 | return sdw_intel_scan_controller(info); |
188 | } |
189 | EXPORT_SYMBOL_NS(sdw_intel_acpi_scan, SND_INTEL_SOUNDWIRE_ACPI); |
190 | |
191 | MODULE_LICENSE("Dual BSD/GPL" ); |
192 | MODULE_DESCRIPTION("Intel Soundwire ACPI helpers" ); |
193 | |