1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // soc-apci.c - support for ACPI enumeration. 4 // 5 // Copyright (c) 2013-15, Intel Corporation. 6 7 #include <linux/export.h> 8 #include <linux/module.h> 9 #include <sound/soc-acpi.h> 10 11 static bool snd_soc_acpi_id_present(struct snd_soc_acpi_mach *machine) 12 { 13 const struct snd_soc_acpi_codecs *comp_ids = machine->comp_ids; 14 int i; 15 16 if (machine->id[0]) { 17 if (acpi_dev_present(machine->id, NULL, -1)) 18 return true; 19 } 20 21 if (comp_ids) { 22 for (i = 0; i < comp_ids->num_codecs; i++) { 23 if (acpi_dev_present(comp_ids->codecs[i], NULL, -1)) { 24 strscpy(machine->id, comp_ids->codecs[i], ACPI_ID_LEN); 25 return true; 26 } 27 } 28 } 29 30 return false; 31 } 32 33 struct snd_soc_acpi_mach * 34 snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines) 35 { 36 struct snd_soc_acpi_mach *mach; 37 struct snd_soc_acpi_mach *mach_alt; 38 39 for (mach = machines; mach->id[0] || mach->comp_ids; mach++) { 40 if (snd_soc_acpi_id_present(mach)) { 41 if (mach->machine_quirk) { 42 mach_alt = mach->machine_quirk(mach); 43 if (!mach_alt) 44 continue; /* not full match, ignore */ 45 mach = mach_alt; 46 } 47 48 return mach; 49 } 50 } 51 return NULL; 52 } 53 EXPORT_SYMBOL_GPL(snd_soc_acpi_find_machine); 54 55 static acpi_status snd_soc_acpi_find_package(acpi_handle handle, u32 level, 56 void *context, void **ret) 57 { 58 struct acpi_device *adev = acpi_fetch_acpi_dev(handle); 59 acpi_status status; 60 struct snd_soc_acpi_package_context *pkg_ctx = context; 61 62 pkg_ctx->data_valid = false; 63 64 if (adev && adev->status.present && adev->status.functional) { 65 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; 66 union acpi_object *myobj = NULL; 67 68 status = acpi_evaluate_object_typed(handle, pkg_ctx->name, 69 NULL, &buffer, 70 ACPI_TYPE_PACKAGE); 71 if (ACPI_FAILURE(status)) 72 return AE_OK; 73 74 myobj = buffer.pointer; 75 if (!myobj || myobj->package.count != pkg_ctx->length) { 76 kfree(buffer.pointer); 77 return AE_OK; 78 } 79 80 status = acpi_extract_package(myobj, 81 pkg_ctx->format, pkg_ctx->state); 82 if (ACPI_FAILURE(status)) { 83 kfree(buffer.pointer); 84 return AE_OK; 85 } 86 87 kfree(buffer.pointer); 88 pkg_ctx->data_valid = true; 89 return AE_CTRL_TERMINATE; 90 } 91 92 return AE_OK; 93 } 94 95 bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], 96 struct snd_soc_acpi_package_context *ctx) 97 { 98 acpi_status status; 99 100 status = acpi_get_devices(hid, snd_soc_acpi_find_package, ctx, NULL); 101 102 if (ACPI_FAILURE(status) || !ctx->data_valid) 103 return false; 104 105 return true; 106 } 107 EXPORT_SYMBOL_GPL(snd_soc_acpi_find_package_from_hid); 108 109 struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) 110 { 111 struct snd_soc_acpi_mach *mach = arg; 112 struct snd_soc_acpi_codecs *codec_list = 113 (struct snd_soc_acpi_codecs *) mach->quirk_data; 114 int i; 115 116 if (mach->quirk_data == NULL) 117 return mach; 118 119 for (i = 0; i < codec_list->num_codecs; i++) { 120 if (!acpi_dev_present(codec_list->codecs[i], NULL, -1)) 121 return NULL; 122 } 123 124 return mach; 125 } 126 EXPORT_SYMBOL_GPL(snd_soc_acpi_codec_list); 127 128 #define SDW_CODEC_ADR_MASK(_adr) ((_adr) & (SDW_DISCO_LINK_ID_MASK | SDW_VERSION_MASK | \ 129 SDW_MFG_ID_MASK | SDW_PART_ID_MASK)) 130 131 /* Check if all Slaves defined on the link can be found */ 132 bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev, 133 const struct snd_soc_acpi_link_adr *link, 134 struct sdw_extended_slave_id *ids, 135 int num_slaves) 136 { 137 unsigned int part_id, link_id, unique_id, mfg_id, version; 138 int i, j, k; 139 140 for (i = 0; i < link->num_adr; i++) { 141 u64 adr = link->adr_d[i].adr; 142 int reported_part_count = 0; 143 144 mfg_id = SDW_MFG_ID(adr); 145 part_id = SDW_PART_ID(adr); 146 link_id = SDW_DISCO_LINK_ID(adr); 147 version = SDW_VERSION(adr); 148 149 for (j = 0; j < num_slaves; j++) { 150 /* find out how many identical parts were reported on that link */ 151 if (ids[j].link_id == link_id && 152 ids[j].id.part_id == part_id && 153 ids[j].id.mfg_id == mfg_id && 154 ids[j].id.sdw_version == version) 155 reported_part_count++; 156 } 157 158 for (j = 0; j < num_slaves; j++) { 159 int expected_part_count = 0; 160 161 if (ids[j].link_id != link_id || 162 ids[j].id.part_id != part_id || 163 ids[j].id.mfg_id != mfg_id || 164 ids[j].id.sdw_version != version) 165 continue; 166 167 /* find out how many identical parts are expected */ 168 for (k = 0; k < link->num_adr; k++) { 169 u64 adr2 = link->adr_d[k].adr; 170 171 if (SDW_CODEC_ADR_MASK(adr2) == SDW_CODEC_ADR_MASK(adr)) 172 expected_part_count++; 173 } 174 175 if (reported_part_count == expected_part_count) { 176 /* 177 * we have to check unique id 178 * if there is more than one 179 * Slave on the link 180 */ 181 unique_id = SDW_UNIQUE_ID(adr); 182 if (reported_part_count == 1 || 183 ids[j].id.unique_id == unique_id) { 184 dev_dbg(dev, "found part_id %#x at link %d\n", part_id, link_id); 185 break; 186 } 187 } else { 188 dev_dbg(dev, "part_id %#x reported %d expected %d on link %d, skipping\n", 189 part_id, reported_part_count, expected_part_count, link_id); 190 } 191 } 192 if (j == num_slaves) { 193 dev_dbg(dev, "Slave part_id %#x not found\n", part_id); 194 return false; 195 } 196 } 197 return true; 198 } 199 EXPORT_SYMBOL_GPL(snd_soc_acpi_sdw_link_slaves_found); 200 201 MODULE_LICENSE("GPL v2"); 202 MODULE_DESCRIPTION("ALSA SoC ACPI module"); 203
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.