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 */ 42 void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable) 43 { 44 45 if (!bus->ppcap) { 46 dev_err(bus->dev, "Address of PP capability is NULL"); 47 return; 48 } 49 50 if (enable) 51 snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 52 AZX_PPCTL_GPROCEN, AZX_PPCTL_GPROCEN); 53 else 54 snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 55 AZX_PPCTL_GPROCEN, 0); 56 } 57 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable); 58 59 /** 60 * snd_hdac_ext_bus_ppcap_int_enable - ppcap interrupt enable/disable 61 * @ebus: HD-audio extended core bus 62 * @enable: flag to enable/disable interrupt 63 */ 64 void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable) 65 { 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, 74 AZX_PPCTL_PIE, AZX_PPCTL_PIE); 75 else 76 snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 77 AZX_PPCTL_PIE, 0); 78 } 79 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable); 80 81 /* 82 * Multilink helpers - these helpers are useful for dealing with HDA 83 * new multilink capability 84 */ 85 86 /** 87 * snd_hdac_ext_bus_get_ml_capabilities - get multilink capability 88 * @ebus: HD-audio extended core bus 89 * 90 * This will parse all links and read the mlink capabilities and add them 91 * in hlink_list of extended hdac bus 92 * Note: this will be freed on bus exit by driver 93 */ 94 int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus) 95 { 96 int idx; 97 u32 link_count; 98 struct hdac_ext_link *hlink; 99 100 link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; 101 102 dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count); 103 104 for (idx = 0; idx < link_count; idx++) { 105 hlink = kzalloc(sizeof(*hlink), GFP_KERNEL); 106 if (!hlink) 107 return -ENOMEM; 108 hlink->index = idx; 109 hlink->bus = bus; 110 hlink->ml_addr = bus->mlcap + AZX_ML_BASE + 111 (AZX_ML_INTERVAL * idx); 112 hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP); 113 hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID); 114 115 /* since link in On, update the ref */ 116 hlink->ref_count = 1; 117 118 list_add_tail(&hlink->list, &bus->hlink_list); 119 } 120 121 return 0; 122 } 123 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities); 124 125 /** 126 * snd_hdac_link_free_all- free hdac extended link objects 127 * 128 * @ebus: HD-audio ext core bus 129 */ 130 131 void snd_hdac_link_free_all(struct hdac_bus *bus) 132 { 133 struct hdac_ext_link *l; 134 135 while (!list_empty(&bus->hlink_list)) { 136 l = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list); 137 list_del(&l->list); 138 kfree(l); 139 } 140 } 141 EXPORT_SYMBOL_GPL(snd_hdac_link_free_all); 142 143 /** 144 * snd_hdac_ext_bus_get_link_index - get link based on codec name 145 * @ebus: HD-audio extended core bus 146 * @codec_name: codec name 147 */ 148 struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus, 149 const char *codec_name) 150 { 151 int i; 152 struct hdac_ext_link *hlink = NULL; 153 int bus_idx, addr; 154 155 if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2) 156 return NULL; 157 if (bus->idx != bus_idx) 158 return NULL; 159 160 list_for_each_entry(hlink, &bus->hlink_list, list) { 161 for (i = 0; i < HDA_MAX_CODECS; i++) { 162 if (hlink->lsdiid & (0x1 << addr)) 163 return hlink; 164 } 165 } 166 167 return NULL; 168 } 169 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_link); 170 171 static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable) 172 { 173 int timeout; 174 u32 val; 175 int mask = (1 << AZX_MLCTL_CPA_SHIFT); 176 177 udelay(3); 178 timeout = 150; 179 180 do { 181 val = readl(link->ml_addr + AZX_REG_ML_LCTL); 182 if (enable) { 183 if (((val & mask) >> AZX_MLCTL_CPA_SHIFT)) 184 return 0; 185 } else { 186 if (!((val & mask) >> AZX_MLCTL_CPA_SHIFT)) 187 return 0; 188 } 189 udelay(3); 190 } while (--timeout); 191 192 return -EIO; 193 } 194 195 /** 196 * snd_hdac_ext_bus_link_power_up -power up hda link 197 * @link: HD-audio extended link 198 */ 199 int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link) 200 { 201 snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, 202 AZX_MLCTL_SPA, AZX_MLCTL_SPA); 203 204 return check_hdac_link_power_active(link, true); 205 } 206 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up); 207 208 /** 209 * snd_hdac_ext_bus_link_power_down -power down hda link 210 * @link: HD-audio extended link 211 */ 212 int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link) 213 { 214 snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0); 215 216 return check_hdac_link_power_active(link, false); 217 } 218 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down); 219 220 /** 221 * snd_hdac_ext_bus_link_power_up_all -power up all hda link 222 * @ebus: HD-audio extended bus 223 */ 224 int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus) 225 { 226 struct hdac_ext_link *hlink = NULL; 227 int ret; 228 229 list_for_each_entry(hlink, &bus->hlink_list, list) { 230 snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, 231 AZX_MLCTL_SPA, AZX_MLCTL_SPA); 232 ret = check_hdac_link_power_active(hlink, true); 233 if (ret < 0) 234 return ret; 235 } 236 237 return 0; 238 } 239 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all); 240 241 /** 242 * snd_hdac_ext_bus_link_power_down_all -power down all hda link 243 * @ebus: HD-audio extended bus 244 */ 245 int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus) 246 { 247 struct hdac_ext_link *hlink = NULL; 248 int ret; 249 250 list_for_each_entry(hlink, &bus->hlink_list, list) { 251 snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, 252 AZX_MLCTL_SPA, 0); 253 ret = check_hdac_link_power_active(hlink, false); 254 if (ret < 0) 255 return ret; 256 } 257 258 return 0; 259 } 260 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all); 261 262 int snd_hdac_ext_bus_link_get(struct hdac_bus *bus, 263 struct hdac_ext_link *link) 264 { 265 int ret = 0; 266 267 mutex_lock(&bus->lock); 268 269 /* 270 * if we move from 0 to 1, count will be 1 so power up this link 271 * as well, also check the dma status and trigger that 272 */ 273 if (++link->ref_count == 1) { 274 if (!bus->cmd_dma_state) { 275 snd_hdac_bus_init_cmd_io(bus); 276 bus->cmd_dma_state = true; 277 } 278 279 ret = snd_hdac_ext_bus_link_power_up(link); 280 281 /* 282 * wait for 521usec for codec to report status 283 * HDA spec section 4.3 - Codec Discovery 284 */ 285 udelay(521); 286 bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); 287 dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); 288 snd_hdac_chip_writew(bus, STATESTS, bus->codec_mask); 289 } 290 291 mutex_unlock(&bus->lock); 292 return ret; 293 } 294 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get); 295 296 int snd_hdac_ext_bus_link_put(struct hdac_bus *bus, 297 struct hdac_ext_link *link) 298 { 299 int ret = 0; 300 struct hdac_ext_link *hlink; 301 bool link_up = false; 302 303 mutex_lock(&bus->lock); 304 305 /* 306 * if we move from 1 to 0, count will be 0 307 * so power down this link as well 308 */ 309 if (--link->ref_count == 0) { 310 ret = snd_hdac_ext_bus_link_power_down(link); 311 312 /* 313 * now check if all links are off, if so turn off 314 * cmd dma as well 315 */ 316 list_for_each_entry(hlink, &bus->hlink_list, list) { 317 if (hlink->ref_count) { 318 link_up = true; 319 break; 320 } 321 } 322 323 if (!link_up) { 324 snd_hdac_bus_stop_cmd_io(bus); 325 bus->cmd_dma_state = false; 326 } 327 } 328 329 mutex_unlock(&bus->lock); 330 return ret; 331 } 332 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put); 333