0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/delay.h>
0013 #include <linux/slab.h>
0014 #include <sound/pcm.h>
0015 #include <sound/hda_register.h>
0016 #include <sound/hdaudio_ext.h>
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029 void snd_hdac_ext_stream_init(struct hdac_bus *bus,
0030 struct hdac_ext_stream *hext_stream,
0031 int idx, int direction, int tag)
0032 {
0033 if (bus->ppcap) {
0034 hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
0035 AZX_PPHC_INTERVAL * idx;
0036
0037 hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
0038 AZX_PPLC_MULTI * bus->num_streams +
0039 AZX_PPLC_INTERVAL * idx;
0040 }
0041
0042 if (bus->spbcap) {
0043 hext_stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
0044 AZX_SPB_INTERVAL * idx +
0045 AZX_SPB_SPIB;
0046
0047 hext_stream->fifo_addr = bus->spbcap + AZX_SPB_BASE +
0048 AZX_SPB_INTERVAL * idx +
0049 AZX_SPB_MAXFIFO;
0050 }
0051
0052 if (bus->drsmcap)
0053 hext_stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
0054 AZX_DRSM_INTERVAL * idx;
0055
0056 hext_stream->decoupled = false;
0057 snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag);
0058 }
0059 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init);
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069 int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
0070 int num_stream, int dir)
0071 {
0072 int stream_tag = 0;
0073 int i, tag, idx = start_idx;
0074
0075 for (i = 0; i < num_stream; i++) {
0076 struct hdac_ext_stream *hext_stream =
0077 kzalloc(sizeof(*hext_stream), GFP_KERNEL);
0078 if (!hext_stream)
0079 return -ENOMEM;
0080 tag = ++stream_tag;
0081 snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag);
0082 idx++;
0083 }
0084
0085 return 0;
0086
0087 }
0088 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
0089
0090
0091
0092
0093
0094
0095 void snd_hdac_stream_free_all(struct hdac_bus *bus)
0096 {
0097 struct hdac_stream *s, *_s;
0098 struct hdac_ext_stream *hext_stream;
0099
0100 list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
0101 hext_stream = stream_to_hdac_ext_stream(s);
0102 snd_hdac_ext_stream_decouple(bus, hext_stream, false);
0103 list_del(&s->list);
0104 kfree(hext_stream);
0105 }
0106 }
0107 EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
0108
0109 void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
0110 struct hdac_ext_stream *hext_stream,
0111 bool decouple)
0112 {
0113 struct hdac_stream *hstream = &hext_stream->hstream;
0114 u32 val;
0115 int mask = AZX_PPCTL_PROCEN(hstream->index);
0116
0117 val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
0118
0119 if (decouple && !val)
0120 snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask);
0121 else if (!decouple && val)
0122 snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
0123
0124 hext_stream->decoupled = decouple;
0125 }
0126 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked);
0127
0128
0129
0130
0131
0132
0133
0134 void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
0135 struct hdac_ext_stream *hext_stream, bool decouple)
0136 {
0137 spin_lock_irq(&bus->reg_lock);
0138 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple);
0139 spin_unlock_irq(&bus->reg_lock);
0140 }
0141 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
0142
0143
0144
0145
0146
0147 void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream)
0148 {
0149 snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
0150 AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN);
0151 }
0152 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start);
0153
0154
0155
0156
0157
0158 void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream)
0159 {
0160 snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
0161 }
0162 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear);
0163
0164
0165
0166
0167
0168 void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream)
0169 {
0170 unsigned char val;
0171 int timeout;
0172
0173 snd_hdac_ext_link_stream_clear(hext_stream);
0174
0175 snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
0176 AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST);
0177 udelay(3);
0178 timeout = 50;
0179 do {
0180 val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) &
0181 AZX_PPLCCTL_STRST;
0182 if (val)
0183 break;
0184 udelay(3);
0185 } while (--timeout);
0186 val &= ~AZX_PPLCCTL_STRST;
0187 writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
0188 udelay(3);
0189
0190 timeout = 50;
0191
0192 do {
0193 val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
0194 if (!val)
0195 break;
0196 udelay(3);
0197 } while (--timeout);
0198
0199 }
0200 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset);
0201
0202
0203
0204
0205
0206
0207 int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *hext_stream, int fmt)
0208 {
0209 struct hdac_stream *hstream = &hext_stream->hstream;
0210 unsigned int val;
0211
0212
0213 snd_hdac_ext_link_stream_clear(hext_stream);
0214
0215 val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL);
0216 val = (val & ~AZX_PPLCCTL_STRM_MASK) |
0217 (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
0218 writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
0219
0220
0221 writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT);
0222
0223 return 0;
0224 }
0225 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
0226
0227
0228
0229
0230
0231
0232 void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
0233 int stream)
0234 {
0235 snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
0236 }
0237 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id);
0238
0239
0240
0241
0242
0243
0244 void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link,
0245 int stream)
0246 {
0247 snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
0248 }
0249 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id);
0250
0251 static struct hdac_ext_stream *
0252 hdac_ext_link_stream_assign(struct hdac_bus *bus,
0253 struct snd_pcm_substream *substream)
0254 {
0255 struct hdac_ext_stream *res = NULL;
0256 struct hdac_stream *hstream = NULL;
0257
0258 if (!bus->ppcap) {
0259 dev_err(bus->dev, "stream type not supported\n");
0260 return NULL;
0261 }
0262
0263 spin_lock_irq(&bus->reg_lock);
0264 list_for_each_entry(hstream, &bus->stream_list, list) {
0265 struct hdac_ext_stream *hext_stream = container_of(hstream,
0266 struct hdac_ext_stream,
0267 hstream);
0268 if (hstream->direction != substream->stream)
0269 continue;
0270
0271
0272 if (hext_stream->decoupled && !hext_stream->link_locked) {
0273 res = hext_stream;
0274 break;
0275 }
0276
0277 if (!hext_stream->link_locked) {
0278 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true);
0279 res = hext_stream;
0280 break;
0281 }
0282 }
0283 if (res) {
0284 res->link_locked = 1;
0285 res->link_substream = substream;
0286 }
0287 spin_unlock_irq(&bus->reg_lock);
0288 return res;
0289 }
0290
0291 static struct hdac_ext_stream *
0292 hdac_ext_host_stream_assign(struct hdac_bus *bus,
0293 struct snd_pcm_substream *substream)
0294 {
0295 struct hdac_ext_stream *res = NULL;
0296 struct hdac_stream *hstream = NULL;
0297
0298 if (!bus->ppcap) {
0299 dev_err(bus->dev, "stream type not supported\n");
0300 return NULL;
0301 }
0302
0303 spin_lock_irq(&bus->reg_lock);
0304 list_for_each_entry(hstream, &bus->stream_list, list) {
0305 struct hdac_ext_stream *hext_stream = container_of(hstream,
0306 struct hdac_ext_stream,
0307 hstream);
0308 if (hstream->direction != substream->stream)
0309 continue;
0310
0311 if (!hstream->opened) {
0312 if (!hext_stream->decoupled)
0313 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true);
0314 res = hext_stream;
0315 break;
0316 }
0317 }
0318 if (res) {
0319 res->hstream.opened = 1;
0320 res->hstream.running = 0;
0321 res->hstream.substream = substream;
0322 }
0323 spin_unlock_irq(&bus->reg_lock);
0324
0325 return res;
0326 }
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345 struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
0346 struct snd_pcm_substream *substream,
0347 int type)
0348 {
0349 struct hdac_ext_stream *hext_stream = NULL;
0350 struct hdac_stream *hstream = NULL;
0351
0352 switch (type) {
0353 case HDAC_EXT_STREAM_TYPE_COUPLED:
0354 hstream = snd_hdac_stream_assign(bus, substream);
0355 if (hstream)
0356 hext_stream = container_of(hstream,
0357 struct hdac_ext_stream,
0358 hstream);
0359 return hext_stream;
0360
0361 case HDAC_EXT_STREAM_TYPE_HOST:
0362 return hdac_ext_host_stream_assign(bus, substream);
0363
0364 case HDAC_EXT_STREAM_TYPE_LINK:
0365 return hdac_ext_link_stream_assign(bus, substream);
0366
0367 default:
0368 return NULL;
0369 }
0370 }
0371 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
0372
0373
0374
0375
0376
0377
0378
0379
0380 void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type)
0381 {
0382 struct hdac_bus *bus = hext_stream->hstream.bus;
0383
0384 switch (type) {
0385 case HDAC_EXT_STREAM_TYPE_COUPLED:
0386 snd_hdac_stream_release(&hext_stream->hstream);
0387 break;
0388
0389 case HDAC_EXT_STREAM_TYPE_HOST:
0390 spin_lock_irq(&bus->reg_lock);
0391 if (hext_stream->decoupled && !hext_stream->link_locked)
0392 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
0393 spin_unlock_irq(&bus->reg_lock);
0394 snd_hdac_stream_release(&hext_stream->hstream);
0395 break;
0396
0397 case HDAC_EXT_STREAM_TYPE_LINK:
0398 spin_lock_irq(&bus->reg_lock);
0399 if (hext_stream->decoupled && !hext_stream->hstream.opened)
0400 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
0401 hext_stream->link_locked = 0;
0402 hext_stream->link_substream = NULL;
0403 spin_unlock_irq(&bus->reg_lock);
0404 break;
0405
0406 default:
0407 dev_dbg(bus->dev, "Invalid type %d\n", type);
0408 }
0409
0410 }
0411 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
0412
0413
0414
0415
0416
0417
0418
0419 void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *bus,
0420 bool enable, int index)
0421 {
0422 u32 mask = 0;
0423
0424 if (!bus->spbcap) {
0425 dev_err(bus->dev, "Address of SPB capability is NULL\n");
0426 return;
0427 }
0428
0429 mask |= (1 << index);
0430
0431 if (enable)
0432 snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, mask);
0433 else
0434 snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
0435 }
0436 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable);
0437
0438
0439
0440
0441
0442
0443
0444 int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus,
0445 struct hdac_ext_stream *hext_stream, u32 value)
0446 {
0447
0448 if (!bus->spbcap) {
0449 dev_err(bus->dev, "Address of SPB capability is NULL\n");
0450 return -EINVAL;
0451 }
0452
0453 writel(value, hext_stream->spib_addr);
0454
0455 return 0;
0456 }
0457 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib);
0458
0459
0460
0461
0462
0463
0464
0465
0466 int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus,
0467 struct hdac_ext_stream *hext_stream)
0468 {
0469
0470 if (!bus->spbcap) {
0471 dev_err(bus->dev, "Address of SPB capability is NULL\n");
0472 return -EINVAL;
0473 }
0474
0475 return readl(hext_stream->fifo_addr);
0476 }
0477 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo);
0478
0479
0480
0481
0482
0483
0484
0485 void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus,
0486 bool enable, int index)
0487 {
0488 u32 mask = 0;
0489
0490 if (!bus->drsmcap) {
0491 dev_err(bus->dev, "Address of DRSM capability is NULL\n");
0492 return;
0493 }
0494
0495 mask |= (1 << index);
0496
0497 if (enable)
0498 snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask);
0499 else
0500 snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
0501 }
0502 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable);
0503
0504
0505
0506
0507
0508
0509
0510 int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus,
0511 struct hdac_ext_stream *hext_stream, u32 value)
0512 {
0513
0514 if (!bus->drsmcap) {
0515 dev_err(bus->dev, "Address of DRSM capability is NULL\n");
0516 return -EINVAL;
0517 }
0518
0519 writel(value, hext_stream->dpibr_addr);
0520
0521 return 0;
0522 }
0523 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr);
0524
0525
0526
0527
0528
0529
0530 int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value)
0531 {
0532 snd_hdac_stream_writel(&hext_stream->hstream, SD_LPIB, value);
0533
0534 return 0;
0535 }
0536 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib);