| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 | 
 | 2 | // hdac_component.c - routines for sync between HD-A core and DRM driver | 
 | 3 |  | 
 | 4 | #include <linux/init.h> | 
 | 5 | #include <linux/module.h> | 
 | 6 | #include <linux/pci.h> | 
 | 7 | #include <linux/component.h> | 
 | 8 | #include <sound/core.h> | 
 | 9 | #include <sound/hdaudio.h> | 
 | 10 | #include <sound/hda_component.h> | 
 | 11 | #include <sound/hda_register.h> | 
 | 12 |  | 
 | 13 | static void hdac_acomp_release(struct device *dev, void *res) | 
 | 14 | { | 
 | 15 | } | 
 | 16 |  | 
 | 17 | static struct drm_audio_component *hdac_get_acomp(struct device *dev) | 
 | 18 | { | 
 | 19 | 	return devres_find(dev, hdac_acomp_release, NULL, NULL); | 
 | 20 | } | 
 | 21 |  | 
 | 22 | /** | 
 | 23 |  * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup | 
 | 24 |  * @bus: HDA core bus | 
 | 25 |  * @enable: enable or disable the wakeup | 
 | 26 |  * | 
 | 27 |  * This function is supposed to be used only by a HD-audio controller | 
 | 28 |  * driver that needs the interaction with graphics driver. | 
 | 29 |  * | 
 | 30 |  * This function should be called during the chip reset, also called at | 
 | 31 |  * resume for updating STATESTS register read. | 
 | 32 |  * | 
 | 33 |  * Returns zero for success or a negative error code. | 
 | 34 |  */ | 
 | 35 | int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable) | 
 | 36 | { | 
 | 37 | 	struct drm_audio_component *acomp = bus->audio_component; | 
 | 38 |  | 
 | 39 | 	if (!acomp || !acomp->ops) | 
 | 40 | 		return -ENODEV; | 
 | 41 |  | 
 | 42 | 	if (!acomp->ops->codec_wake_override) | 
 | 43 | 		return 0; | 
 | 44 |  | 
 | 45 | 	dev_dbg(bus->dev, "%s codec wakeup\n", | 
 | 46 | 		enable ? "enable" : "disable"); | 
 | 47 |  | 
 | 48 | 	acomp->ops->codec_wake_override(acomp->dev, enable); | 
 | 49 |  | 
 | 50 | 	return 0; | 
 | 51 | } | 
 | 52 | EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup); | 
 | 53 |  | 
 | 54 | /** | 
 | 55 |  * snd_hdac_display_power - Power up / down the power refcount | 
 | 56 |  * @bus: HDA core bus | 
 | 57 |  * @enable: power up or down | 
 | 58 |  * | 
 | 59 |  * This function is supposed to be used only by a HD-audio controller | 
 | 60 |  * driver that needs the interaction with graphics driver. | 
 | 61 |  * | 
 | 62 |  * This function manages a refcount and calls the get_power() and | 
 | 63 |  * put_power() ops accordingly, toggling the codec wakeup, too. | 
 | 64 |  * | 
 | 65 |  * Returns zero for success or a negative error code. | 
 | 66 |  */ | 
 | 67 | int snd_hdac_display_power(struct hdac_bus *bus, bool enable) | 
 | 68 | { | 
 | 69 | 	struct drm_audio_component *acomp = bus->audio_component; | 
 | 70 |  | 
 | 71 | 	if (!acomp || !acomp->ops) | 
 | 72 | 		return -ENODEV; | 
 | 73 |  | 
 | 74 | 	dev_dbg(bus->dev, "display power %s\n", | 
 | 75 | 		enable ? "enable" : "disable"); | 
 | 76 |  | 
 | 77 | 	if (enable) { | 
 | 78 | 		if (!bus->drm_power_refcount++) { | 
 | 79 | 			if (acomp->ops->get_power) | 
 | 80 | 				acomp->ops->get_power(acomp->dev); | 
 | 81 | 			snd_hdac_set_codec_wakeup(bus, true); | 
 | 82 | 			snd_hdac_set_codec_wakeup(bus, false); | 
 | 83 | 		} | 
 | 84 | 	} else { | 
 | 85 | 		WARN_ON(!bus->drm_power_refcount); | 
 | 86 | 		if (!--bus->drm_power_refcount) | 
 | 87 | 			if (acomp->ops->put_power) | 
 | 88 | 				acomp->ops->put_power(acomp->dev); | 
 | 89 | 	} | 
 | 90 |  | 
 | 91 | 	return 0; | 
 | 92 | } | 
 | 93 | EXPORT_SYMBOL_GPL(snd_hdac_display_power); | 
 | 94 |  | 
 | 95 | /** | 
 | 96 |  * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate | 
 | 97 |  * @codec: HDA codec | 
 | 98 |  * @nid: the pin widget NID | 
 | 99 |  * @dev_id: device identifier | 
 | 100 |  * @rate: the sample rate to set | 
 | 101 |  * | 
 | 102 |  * This function is supposed to be used only by a HD-audio controller | 
 | 103 |  * driver that needs the interaction with graphics driver. | 
 | 104 |  * | 
 | 105 |  * This function sets N/CTS value based on the given sample rate. | 
 | 106 |  * Returns zero for success, or a negative error code. | 
 | 107 |  */ | 
 | 108 | int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, | 
 | 109 | 			     int dev_id, int rate) | 
 | 110 | { | 
 | 111 | 	struct hdac_bus *bus = codec->bus; | 
 | 112 | 	struct drm_audio_component *acomp = bus->audio_component; | 
 | 113 | 	int port, pipe; | 
 | 114 |  | 
 | 115 | 	if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate) | 
 | 116 | 		return -ENODEV; | 
 | 117 | 	port = nid; | 
 | 118 | 	if (acomp->audio_ops && acomp->audio_ops->pin2port) { | 
 | 119 | 		port = acomp->audio_ops->pin2port(codec, nid); | 
 | 120 | 		if (port < 0) | 
 | 121 | 			return -EINVAL; | 
 | 122 | 	} | 
 | 123 | 	pipe = dev_id; | 
 | 124 | 	return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate); | 
 | 125 | } | 
 | 126 | EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); | 
 | 127 |  | 
 | 128 | /** | 
 | 129 |  * snd_hdac_acomp_get_eld - Get the audio state and ELD via component | 
 | 130 |  * @codec: HDA codec | 
 | 131 |  * @nid: the pin widget NID | 
 | 132 |  * @dev_id: device identifier | 
 | 133 |  * @audio_enabled: the pointer to store the current audio state | 
 | 134 |  * @buffer: the buffer pointer to store ELD bytes | 
 | 135 |  * @max_bytes: the max bytes to be stored on @buffer | 
 | 136 |  * | 
 | 137 |  * This function is supposed to be used only by a HD-audio controller | 
 | 138 |  * driver that needs the interaction with graphics driver. | 
 | 139 |  * | 
 | 140 |  * This function queries the current state of the audio on the given | 
 | 141 |  * digital port and fetches the ELD bytes onto the given buffer. | 
 | 142 |  * It returns the number of bytes for the total ELD data, zero for | 
 | 143 |  * invalid ELD, or a negative error code. | 
 | 144 |  * | 
 | 145 |  * The return size is the total bytes required for the whole ELD bytes, | 
 | 146 |  * thus it may be over @max_bytes.  If it's over @max_bytes, it implies | 
 | 147 |  * that only a part of ELD bytes have been fetched. | 
 | 148 |  */ | 
 | 149 | int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id, | 
 | 150 | 			   bool *audio_enabled, char *buffer, int max_bytes) | 
 | 151 | { | 
 | 152 | 	struct hdac_bus *bus = codec->bus; | 
 | 153 | 	struct drm_audio_component *acomp = bus->audio_component; | 
 | 154 | 	int port, pipe; | 
 | 155 |  | 
 | 156 | 	if (!acomp || !acomp->ops || !acomp->ops->get_eld) | 
 | 157 | 		return -ENODEV; | 
 | 158 |  | 
 | 159 | 	port = nid; | 
 | 160 | 	if (acomp->audio_ops && acomp->audio_ops->pin2port) { | 
 | 161 | 		port = acomp->audio_ops->pin2port(codec, nid); | 
 | 162 | 		if (port < 0) | 
 | 163 | 			return -EINVAL; | 
 | 164 | 	} | 
 | 165 | 	pipe = dev_id; | 
 | 166 | 	return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled, | 
 | 167 | 				   buffer, max_bytes); | 
 | 168 | } | 
 | 169 | EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld); | 
 | 170 |  | 
 | 171 | static int hdac_component_master_bind(struct device *dev) | 
 | 172 | { | 
 | 173 | 	struct drm_audio_component *acomp = hdac_get_acomp(dev); | 
 | 174 | 	int ret; | 
 | 175 |  | 
 | 176 | 	if (WARN_ON(!acomp)) | 
 | 177 | 		return -EINVAL; | 
 | 178 |  | 
 | 179 | 	ret = component_bind_all(dev, acomp); | 
 | 180 | 	if (ret < 0) | 
 | 181 | 		return ret; | 
 | 182 |  | 
 | 183 | 	if (WARN_ON(!(acomp->dev && acomp->ops))) { | 
 | 184 | 		ret = -EINVAL; | 
 | 185 | 		goto out_unbind; | 
 | 186 | 	} | 
 | 187 |  | 
 | 188 | 	/* pin the module to avoid dynamic unbinding, but only if given */ | 
 | 189 | 	if (!try_module_get(acomp->ops->owner)) { | 
 | 190 | 		ret = -ENODEV; | 
 | 191 | 		goto out_unbind; | 
 | 192 | 	} | 
 | 193 |  | 
 | 194 | 	if (acomp->audio_ops && acomp->audio_ops->master_bind) { | 
 | 195 | 		ret = acomp->audio_ops->master_bind(dev, acomp); | 
 | 196 | 		if (ret < 0) | 
 | 197 | 			goto module_put; | 
 | 198 | 	} | 
 | 199 |  | 
 | 200 | 	return 0; | 
 | 201 |  | 
 | 202 |  module_put: | 
 | 203 | 	module_put(acomp->ops->owner); | 
 | 204 | out_unbind: | 
 | 205 | 	component_unbind_all(dev, acomp); | 
 | 206 |  | 
 | 207 | 	return ret; | 
 | 208 | } | 
 | 209 |  | 
 | 210 | static void hdac_component_master_unbind(struct device *dev) | 
 | 211 | { | 
 | 212 | 	struct drm_audio_component *acomp = hdac_get_acomp(dev); | 
 | 213 |  | 
 | 214 | 	if (acomp->audio_ops && acomp->audio_ops->master_unbind) | 
 | 215 | 		acomp->audio_ops->master_unbind(dev, acomp); | 
 | 216 | 	module_put(acomp->ops->owner); | 
 | 217 | 	component_unbind_all(dev, acomp); | 
 | 218 | 	WARN_ON(acomp->ops || acomp->dev); | 
 | 219 | } | 
 | 220 |  | 
 | 221 | static const struct component_master_ops hdac_component_master_ops = { | 
 | 222 | 	.bind = hdac_component_master_bind, | 
 | 223 | 	.unbind = hdac_component_master_unbind, | 
 | 224 | }; | 
 | 225 |  | 
 | 226 | /** | 
 | 227 |  * snd_hdac_acomp_register_notifier - Register audio component ops | 
 | 228 |  * @bus: HDA core bus | 
 | 229 |  * @aops: audio component ops | 
 | 230 |  * | 
 | 231 |  * This function is supposed to be used only by a HD-audio controller | 
 | 232 |  * driver that needs the interaction with graphics driver. | 
 | 233 |  * | 
 | 234 |  * This function sets the given ops to be called by the graphics driver. | 
 | 235 |  * | 
 | 236 |  * Returns zero for success or a negative error code. | 
 | 237 |  */ | 
 | 238 | int snd_hdac_acomp_register_notifier(struct hdac_bus *bus, | 
 | 239 | 				    const struct drm_audio_component_audio_ops *aops) | 
 | 240 | { | 
 | 241 | 	if (!bus->audio_component) | 
 | 242 | 		return -ENODEV; | 
 | 243 |  | 
 | 244 | 	bus->audio_component->audio_ops = aops; | 
 | 245 | 	return 0; | 
 | 246 | } | 
 | 247 | EXPORT_SYMBOL_GPL(snd_hdac_acomp_register_notifier); | 
 | 248 |  | 
 | 249 | /** | 
 | 250 |  * snd_hdac_acomp_init - Initialize audio component | 
 | 251 |  * @bus: HDA core bus | 
 | 252 |  * @match_master: match function for finding components | 
 | 253 |  * @extra_size: Extra bytes to allocate | 
 | 254 |  * | 
 | 255 |  * This function is supposed to be used only by a HD-audio controller | 
 | 256 |  * driver that needs the interaction with graphics driver. | 
 | 257 |  * | 
 | 258 |  * This function initializes and sets up the audio component to communicate | 
 | 259 |  * with graphics driver. | 
 | 260 |  * | 
 | 261 |  * Unlike snd_hdac_i915_init(), this function doesn't synchronize with the | 
 | 262 |  * binding with the DRM component.  Each caller needs to sync via master_bind | 
 | 263 |  * audio_ops. | 
 | 264 |  * | 
 | 265 |  * Returns zero for success or a negative error code. | 
 | 266 |  */ | 
 | 267 | int snd_hdac_acomp_init(struct hdac_bus *bus, | 
 | 268 | 			const struct drm_audio_component_audio_ops *aops, | 
 | 269 | 			int (*match_master)(struct device *, void *), | 
 | 270 | 			size_t extra_size) | 
 | 271 | { | 
 | 272 | 	struct component_match *match = NULL; | 
 | 273 | 	struct device *dev = bus->dev; | 
 | 274 | 	struct drm_audio_component *acomp; | 
 | 275 | 	int ret; | 
 | 276 |  | 
 | 277 | 	if (WARN_ON(hdac_get_acomp(dev))) | 
 | 278 | 		return -EBUSY; | 
 | 279 |  | 
 | 280 | 	acomp = devres_alloc(hdac_acomp_release, sizeof(*acomp) + extra_size, | 
 | 281 | 			     GFP_KERNEL); | 
 | 282 | 	if (!acomp) | 
 | 283 | 		return -ENOMEM; | 
 | 284 | 	acomp->audio_ops = aops; | 
 | 285 | 	bus->audio_component = acomp; | 
 | 286 | 	devres_add(dev, acomp); | 
 | 287 |  | 
 | 288 | 	component_match_add(dev, &match, match_master, bus); | 
 | 289 | 	ret = component_master_add_with_match(dev, &hdac_component_master_ops, | 
 | 290 | 					      match); | 
 | 291 | 	if (ret < 0) | 
 | 292 | 		goto out_err; | 
 | 293 |  | 
 | 294 | 	return 0; | 
 | 295 |  | 
 | 296 | out_err: | 
 | 297 | 	bus->audio_component = NULL; | 
 | 298 | 	devres_destroy(dev, hdac_acomp_release, NULL, NULL); | 
 | 299 | 	dev_info(dev, "failed to add audio component master (%d)\n", ret); | 
 | 300 |  | 
 | 301 | 	return ret; | 
 | 302 | } | 
 | 303 | EXPORT_SYMBOL_GPL(snd_hdac_acomp_init); | 
 | 304 |  | 
 | 305 | /** | 
 | 306 |  * snd_hdac_acomp_exit - Finalize audio component | 
 | 307 |  * @bus: HDA core bus | 
 | 308 |  * | 
 | 309 |  * This function is supposed to be used only by a HD-audio controller | 
 | 310 |  * driver that needs the interaction with graphics driver. | 
 | 311 |  * | 
 | 312 |  * This function releases the audio component that has been used. | 
 | 313 |  * | 
 | 314 |  * Returns zero for success or a negative error code. | 
 | 315 |  */ | 
 | 316 | int snd_hdac_acomp_exit(struct hdac_bus *bus) | 
 | 317 | { | 
 | 318 | 	struct device *dev = bus->dev; | 
 | 319 | 	struct drm_audio_component *acomp = bus->audio_component; | 
 | 320 |  | 
 | 321 | 	if (!acomp) | 
 | 322 | 		return 0; | 
 | 323 |  | 
 | 324 | 	WARN_ON(bus->drm_power_refcount); | 
 | 325 | 	if (bus->drm_power_refcount > 0 && acomp->ops) | 
 | 326 | 		acomp->ops->put_power(acomp->dev); | 
 | 327 |  | 
 | 328 | 	component_master_del(dev, &hdac_component_master_ops); | 
 | 329 |  | 
 | 330 | 	bus->audio_component = NULL; | 
 | 331 | 	devres_destroy(dev, hdac_acomp_release, NULL, NULL); | 
 | 332 |  | 
 | 333 | 	return 0; | 
 | 334 | } | 
 | 335 | EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit); |