xref: /linux/sound/soc/sdca/sdca_functions.c (revision 2c1ed907520c50326b8f604907a8478b27881a2e)
1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2 // Copyright(c) 2024 Intel Corporation
3 
4 /*
5  * The MIPI SDCA specification is available for public downloads at
6  * https://www.mipi.org/mipi-sdca-v1-0-download
7  */
8 
9 #define dev_fmt(fmt) "%s: " fmt, __func__
10 
11 #include <linux/acpi.h>
12 #include <linux/device.h>
13 #include <linux/module.h>
14 #include <linux/property.h>
15 #include <linux/soundwire/sdw.h>
16 #include <linux/types.h>
17 #include <sound/sdca.h>
18 #include <sound/sdca_function.h>
19 
patch_sdca_function_type(u32 interface_revision,u32 * function_type)20 static int patch_sdca_function_type(u32 interface_revision, u32 *function_type)
21 {
22 	/*
23 	 * Unfortunately early SDCA specifications used different indices for Functions,
24 	 * for backwards compatibility we have to reorder the values found
25 	 */
26 	if (interface_revision < 0x0801) {
27 		switch (*function_type) {
28 		case 1:
29 			*function_type = SDCA_FUNCTION_TYPE_SMART_AMP;
30 			break;
31 		case 2:
32 			*function_type = SDCA_FUNCTION_TYPE_SMART_MIC;
33 			break;
34 		case 3:
35 			*function_type = SDCA_FUNCTION_TYPE_SPEAKER_MIC;
36 			break;
37 		case 4:
38 			*function_type = SDCA_FUNCTION_TYPE_UAJ;
39 			break;
40 		case 5:
41 			*function_type = SDCA_FUNCTION_TYPE_RJ;
42 			break;
43 		case 6:
44 			*function_type = SDCA_FUNCTION_TYPE_HID;
45 			break;
46 		default:
47 			return -EINVAL;
48 		}
49 	}
50 
51 	return 0;
52 }
53 
get_sdca_function_name(u32 function_type)54 static const char *get_sdca_function_name(u32 function_type)
55 {
56 	switch (function_type) {
57 	case SDCA_FUNCTION_TYPE_SMART_AMP:
58 		return SDCA_FUNCTION_TYPE_SMART_AMP_NAME;
59 	case SDCA_FUNCTION_TYPE_SMART_MIC:
60 		return SDCA_FUNCTION_TYPE_SMART_MIC_NAME;
61 	case SDCA_FUNCTION_TYPE_UAJ:
62 		return SDCA_FUNCTION_TYPE_UAJ_NAME;
63 	case SDCA_FUNCTION_TYPE_HID:
64 		return SDCA_FUNCTION_TYPE_HID_NAME;
65 	case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
66 		return SDCA_FUNCTION_TYPE_SIMPLE_AMP_NAME;
67 	case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
68 		return SDCA_FUNCTION_TYPE_SIMPLE_MIC_NAME;
69 	case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
70 		return SDCA_FUNCTION_TYPE_SPEAKER_MIC_NAME;
71 	case SDCA_FUNCTION_TYPE_RJ:
72 		return SDCA_FUNCTION_TYPE_RJ_NAME;
73 	case SDCA_FUNCTION_TYPE_IMP_DEF:
74 		return SDCA_FUNCTION_TYPE_IMP_DEF_NAME;
75 	default:
76 		return NULL;
77 	}
78 }
79 
find_sdca_function(struct acpi_device * adev,void * data)80 static int find_sdca_function(struct acpi_device *adev, void *data)
81 {
82 	struct fwnode_handle *function_node = acpi_fwnode_handle(adev);
83 	struct sdca_device_data *sdca_data = data;
84 	struct device *dev = &adev->dev;
85 	struct fwnode_handle *control5; /* used to identify function type */
86 	const char *function_name;
87 	u32 function_type;
88 	int func_index;
89 	u64 addr;
90 	int ret;
91 
92 	if (sdca_data->num_functions >= SDCA_MAX_FUNCTION_COUNT) {
93 		dev_err(dev, "maximum number of functions exceeded\n");
94 		return -EINVAL;
95 	}
96 
97 	ret = acpi_get_local_u64_address(adev->handle, &addr);
98 	if (ret < 0)
99 		return ret;
100 
101 	if (!addr || addr > 0x7) {
102 		dev_err(dev, "invalid addr: 0x%llx\n", addr);
103 		return -ENODEV;
104 	}
105 
106 	/*
107 	 * Extracting the topology type for an SDCA function is a
108 	 * convoluted process.
109 	 * The Function type is only visible as a result of a read
110 	 * from a control. In theory this would mean reading from the hardware,
111 	 * but the SDCA/DisCo specs defined the notion of "DC value" - a constant
112 	 * represented with a DSD subproperty.
113 	 * Drivers have to query the properties for the control
114 	 * SDCA_CONTROL_ENTITY_0_FUNCTION_TOPOLOGY (0x05)
115 	 */
116 	control5 = fwnode_get_named_child_node(function_node,
117 					       "mipi-sdca-control-0x5-subproperties");
118 	if (!control5)
119 		return -ENODEV;
120 
121 	ret = fwnode_property_read_u32(control5, "mipi-sdca-control-dc-value",
122 				       &function_type);
123 
124 	fwnode_handle_put(control5);
125 
126 	if (ret < 0) {
127 		dev_err(dev, "function type only supported as DisCo constant\n");
128 		return ret;
129 	}
130 
131 	ret = patch_sdca_function_type(sdca_data->interface_revision, &function_type);
132 	if (ret < 0) {
133 		dev_err(dev, "SDCA version %#x invalid function type %d\n",
134 			sdca_data->interface_revision, function_type);
135 		return ret;
136 	}
137 
138 	function_name = get_sdca_function_name(function_type);
139 	if (!function_name) {
140 		dev_err(dev, "invalid SDCA function type %d\n", function_type);
141 		return -EINVAL;
142 	}
143 
144 	dev_info(dev, "SDCA function %s (type %d) at 0x%llx\n",
145 		 function_name, function_type, addr);
146 
147 	/* store results */
148 	func_index = sdca_data->num_functions;
149 	sdca_data->sdca_func[func_index].adr = addr;
150 	sdca_data->sdca_func[func_index].type = function_type;
151 	sdca_data->sdca_func[func_index].name = function_name;
152 	sdca_data->num_functions++;
153 
154 	return 0;
155 }
156 
sdca_lookup_functions(struct sdw_slave * slave)157 void sdca_lookup_functions(struct sdw_slave *slave)
158 {
159 	struct device *dev = &slave->dev;
160 	struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
161 
162 	if (!adev) {
163 		dev_info(dev, "No matching ACPI device found, ignoring peripheral\n");
164 		return;
165 	}
166 	acpi_dev_for_each_child(adev, find_sdca_function, &slave->sdca_data);
167 }
168 EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA");
169 
170 MODULE_LICENSE("Dual BSD/GPL");
171 MODULE_DESCRIPTION("SDCA library");
172