linux/sound/hda/hdac_component.c
<<
>>
Prefs
   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
  13static void hdac_acomp_release(struct device *dev, void *res)
  14{
  15}
  16
  17static 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 */
  35int 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}
  52EXPORT_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 */
  67int 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}
  93EXPORT_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 */
 108int 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}
 126EXPORT_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 */
 149int 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}
 169EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
 170
 171static 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);
 204out_unbind:
 205        component_unbind_all(dev, acomp);
 206
 207        return ret;
 208}
 209
 210static 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
 221static 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 */
 238int 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}
 247EXPORT_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 */
 267int 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
 296out_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}
 303EXPORT_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 */
 316int 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}
 335EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit);
 336