linux/sound/hda/ext/hdac_ext_controller.c
<<
>>
Prefs
   1/*
   2 *  hdac-ext-controller.c - HD-audio extended controller functions.
   3 *
   4 *  Copyright (C) 2014-2015 Intel Corp
   5 *  Author: Jeeja KP <jeeja.kp@intel.com>
   6 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   7 *
   8 *  This program is free software; you can redistribute it and/or modify
   9 *  it under the terms of the GNU General Public License as published by
  10 *  the Free Software Foundation; version 2 of the License.
  11 *
  12 *  This program is distributed in the hope that it will be useful, but
  13 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 *  General Public License for more details.
  16 *
  17 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  18 */
  19
  20#include <linux/delay.h>
  21#include <linux/slab.h>
  22#include <sound/hda_register.h>
  23#include <sound/hdaudio_ext.h>
  24
  25/*
  26 * maximum HDAC capablities we should parse to avoid endless looping:
  27 * currently we have 4 extended caps, so this is future proof for now.
  28 * extend when this limit is seen meeting in real HW
  29 */
  30#define HDAC_MAX_CAPS 10
  31
  32/*
  33 * processing pipe helpers - these helpers are useful for dealing with HDA
  34 * new capability of processing pipelines
  35 */
  36
  37/**
  38 * snd_hdac_ext_bus_ppcap_enable - enable/disable processing pipe capability
  39 * @ebus: HD-audio extended core bus
  40 * @enable: flag to turn on/off the capability
  41 */
  42void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable)
  43{
  44        struct hdac_bus *bus = &ebus->bus;
  45
  46        if (!bus->ppcap) {
  47                dev_err(bus->dev, "Address of PP capability is NULL");
  48                return;
  49        }
  50
  51        if (enable)
  52                snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN);
  53        else
  54                snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0);
  55}
  56EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
  57
  58/**
  59 * snd_hdac_ext_bus_ppcap_int_enable - ppcap interrupt enable/disable
  60 * @ebus: HD-audio extended core bus
  61 * @enable: flag to enable/disable interrupt
  62 */
  63void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable)
  64{
  65        struct hdac_bus *bus = &ebus->bus;
  66
  67        if (!bus->ppcap) {
  68                dev_err(bus->dev, "Address of PP capability is NULL\n");
  69                return;
  70        }
  71
  72        if (enable)
  73                snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE);
  74        else
  75                snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0);
  76}
  77EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
  78
  79/*
  80 * Multilink helpers - these helpers are useful for dealing with HDA
  81 * new multilink capability
  82 */
  83
  84/**
  85 * snd_hdac_ext_bus_get_ml_capabilities - get multilink capability
  86 * @ebus: HD-audio extended core bus
  87 *
  88 * This will parse all links and read the mlink capabilities and add them
  89 * in hlink_list of extended hdac bus
  90 * Note: this will be freed on bus exit by driver
  91 */
  92int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
  93{
  94        int idx;
  95        u32 link_count;
  96        struct hdac_ext_link *hlink;
  97        struct hdac_bus *bus = &ebus->bus;
  98
  99        link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
 100
 101        dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count);
 102
 103        for (idx = 0; idx < link_count; idx++) {
 104                hlink  = kzalloc(sizeof(*hlink), GFP_KERNEL);
 105                if (!hlink)
 106                        return -ENOMEM;
 107                hlink->index = idx;
 108                hlink->bus = bus;
 109                hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
 110                                        (AZX_ML_INTERVAL * idx);
 111                hlink->lcaps  = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
 112                hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
 113
 114                /* since link in On, update the ref */
 115                hlink->ref_count = 1;
 116
 117                list_add_tail(&hlink->list, &ebus->hlink_list);
 118        }
 119
 120        return 0;
 121}
 122EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities);
 123
 124/**
 125 * snd_hdac_link_free_all- free hdac extended link objects
 126 *
 127 * @ebus: HD-audio ext core bus
 128 */
 129
 130void snd_hdac_link_free_all(struct hdac_ext_bus *ebus)
 131{
 132        struct hdac_ext_link *l;
 133
 134        while (!list_empty(&ebus->hlink_list)) {
 135                l = list_first_entry(&ebus->hlink_list, struct hdac_ext_link, list);
 136                list_del(&l->list);
 137                kfree(l);
 138        }
 139}
 140EXPORT_SYMBOL_GPL(snd_hdac_link_free_all);
 141
 142/**
 143 * snd_hdac_ext_bus_get_link_index - get link based on codec name
 144 * @ebus: HD-audio extended core bus
 145 * @codec_name: codec name
 146 */
 147struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *ebus,
 148                                                 const char *codec_name)
 149{
 150        int i;
 151        struct hdac_ext_link *hlink = NULL;
 152        int bus_idx, addr;
 153
 154        if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2)
 155                return NULL;
 156        if (ebus->idx != bus_idx)
 157                return NULL;
 158
 159        list_for_each_entry(hlink, &ebus->hlink_list, list) {
 160                for (i = 0; i < HDA_MAX_CODECS; i++) {
 161                        if (hlink->lsdiid & (0x1 << addr))
 162                                return hlink;
 163                }
 164        }
 165
 166        return NULL;
 167}
 168EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_link);
 169
 170static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
 171{
 172        int timeout;
 173        u32 val;
 174        int mask = (1 << AZX_MLCTL_CPA_SHIFT);
 175
 176        udelay(3);
 177        timeout = 150;
 178
 179        do {
 180                val = readl(link->ml_addr + AZX_REG_ML_LCTL);
 181                if (enable) {
 182                        if (((val & mask) >> AZX_MLCTL_CPA_SHIFT))
 183                                return 0;
 184                } else {
 185                        if (!((val & mask) >> AZX_MLCTL_CPA_SHIFT))
 186                                return 0;
 187                }
 188                udelay(3);
 189        } while (--timeout);
 190
 191        return -EIO;
 192}
 193
 194/**
 195 * snd_hdac_ext_bus_link_power_up -power up hda link
 196 * @link: HD-audio extended link
 197 */
 198int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link)
 199{
 200        snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA);
 201
 202        return check_hdac_link_power_active(link, true);
 203}
 204EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up);
 205
 206/**
 207 * snd_hdac_ext_bus_link_power_down -power down hda link
 208 * @link: HD-audio extended link
 209 */
 210int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link)
 211{
 212        snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0);
 213
 214        return check_hdac_link_power_active(link, false);
 215}
 216EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
 217
 218/**
 219 * snd_hdac_ext_bus_link_power_up_all -power up all hda link
 220 * @ebus: HD-audio extended bus
 221 */
 222int snd_hdac_ext_bus_link_power_up_all(struct hdac_ext_bus *ebus)
 223{
 224        struct hdac_ext_link *hlink = NULL;
 225        int ret;
 226
 227        list_for_each_entry(hlink, &ebus->hlink_list, list) {
 228                snd_hdac_updatel(hlink->ml_addr,
 229                                AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA);
 230                ret = check_hdac_link_power_active(hlink, true);
 231                if (ret < 0)
 232                        return ret;
 233        }
 234
 235        return 0;
 236}
 237EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all);
 238
 239/**
 240 * snd_hdac_ext_bus_link_power_down_all -power down all hda link
 241 * @ebus: HD-audio extended bus
 242 */
 243int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus)
 244{
 245        struct hdac_ext_link *hlink = NULL;
 246        int ret;
 247
 248        list_for_each_entry(hlink, &ebus->hlink_list, list) {
 249                snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0);
 250                ret = check_hdac_link_power_active(hlink, false);
 251                if (ret < 0)
 252                        return ret;
 253        }
 254
 255        return 0;
 256}
 257EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
 258
 259int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
 260                                struct hdac_ext_link *link)
 261{
 262        int ret = 0;
 263
 264        mutex_lock(&ebus->lock);
 265
 266        /*
 267         * if we move from 0 to 1, count will be 1 so power up this link
 268         * as well, also check the dma status and trigger that
 269         */
 270        if (++link->ref_count == 1) {
 271                if (!ebus->cmd_dma_state) {
 272                        snd_hdac_bus_init_cmd_io(&ebus->bus);
 273                        ebus->cmd_dma_state = true;
 274                }
 275
 276                ret = snd_hdac_ext_bus_link_power_up(link);
 277        }
 278
 279        mutex_unlock(&ebus->lock);
 280        return ret;
 281}
 282EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
 283
 284int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
 285                                struct hdac_ext_link *link)
 286{
 287        int ret = 0;
 288        struct hdac_ext_link *hlink;
 289        bool link_up = false;
 290
 291        mutex_lock(&ebus->lock);
 292
 293        /*
 294         * if we move from 1 to 0, count will be 0
 295         * so power down this link as well
 296         */
 297        if (--link->ref_count == 0) {
 298                ret = snd_hdac_ext_bus_link_power_down(link);
 299
 300                /*
 301                 * now check if all links are off, if so turn off
 302                 * cmd dma as well
 303                 */
 304                list_for_each_entry(hlink, &ebus->hlink_list, list) {
 305                        if (hlink->ref_count) {
 306                                link_up = true;
 307                                break;
 308                        }
 309                }
 310
 311                if (!link_up) {
 312                        snd_hdac_bus_stop_cmd_io(&ebus->bus);
 313                        ebus->cmd_dma_state = false;
 314                }
 315        }
 316
 317        mutex_unlock(&ebus->lock);
 318        return ret;
 319}
 320EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);
 321