Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  hdac-ext-stream.c - HD-audio extended stream operations.
0004  *
0005  *  Copyright (C) 2015 Intel Corp
0006  *  Author: Jeeja KP <jeeja.kp@intel.com>
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  * snd_hdac_ext_stream_init - initialize each stream (aka device)
0020  * @bus: HD-audio core bus
0021  * @hext_stream: HD-audio ext core stream object to initialize
0022  * @idx: stream index number
0023  * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
0024  * @tag: the tag id to assign
0025  *
0026  * initialize the stream, if ppcap is enabled then init those and then
0027  * invoke hdac stream initialization routine
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  * snd_hdac_ext_stream_init_all - create and initialize the stream objects
0063  *   for an extended hda bus
0064  * @bus: HD-audio core bus
0065  * @start_idx: start index for streams
0066  * @num_stream: number of streams to initialize
0067  * @dir: direction of streams
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  * snd_hdac_stream_free_all - free hdac extended stream objects
0092  *
0093  * @bus: HD-audio core bus
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  * snd_hdac_ext_stream_decouple - decouple the hdac stream
0130  * @bus: HD-audio core bus
0131  * @hext_stream: HD-audio ext core stream object to initialize
0132  * @decouple: flag to decouple
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  * snd_hdac_ext_link_stream_start - start a stream
0145  * @hext_stream: HD-audio ext core stream to start
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  * snd_hdac_ext_link_stream_clear - stop a stream DMA
0156  * @hext_stream: HD-audio ext core stream to stop
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  * snd_hdac_ext_link_stream_reset - reset a stream
0166  * @hext_stream: HD-audio ext core stream to reset
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     /* waiting for hardware to report that the stream is out of reset */
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  * snd_hdac_ext_link_stream_setup -  set up the SD for streaming
0204  * @hext_stream: HD-audio ext core stream to set up
0205  * @fmt: stream format
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     /* make sure the run bit is zero for SD */
0213     snd_hdac_ext_link_stream_clear(hext_stream);
0214     /* program the stream_tag */
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     /* program the stream format */
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  * snd_hdac_ext_link_set_stream_id - maps stream id to link output
0229  * @link: HD-audio ext link to set up
0230  * @stream: stream id
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  * snd_hdac_ext_link_clear_stream_id - maps stream id to link output
0241  * @link: HD-audio ext link to set up
0242  * @stream: stream id
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         /* check if decoupled stream and not in use is available */
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  * snd_hdac_ext_stream_assign - assign a stream for the PCM
0330  * @bus: HD-audio core bus
0331  * @substream: PCM substream to assign
0332  * @type: type of stream (coupled, host or link stream)
0333  *
0334  * This assigns the stream based on the type (coupled/host/link), for the
0335  * given PCM substream, assigns it and returns the stream object
0336  *
0337  * coupled: Looks for an unused stream
0338  * host: Looks for an unused decoupled host stream
0339  * link: Looks for an unused decoupled link stream
0340  *
0341  * If no stream is free, returns NULL. The function tries to keep using
0342  * the same stream object when it's used beforehand.  when a stream is
0343  * decoupled, it becomes a host stream and link stream.
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  * snd_hdac_ext_stream_release - release the assigned stream
0375  * @hext_stream: HD-audio ext core stream to release
0376  * @type: type of stream (coupled, host or link stream)
0377  *
0378  * Release the stream that has been assigned by snd_hdac_ext_stream_assign().
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  * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream
0415  * @bus: HD-audio core bus
0416  * @enable: flag to enable/disable SPIB
0417  * @index: stream index for which SPIB need to be enabled
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  * snd_hdac_ext_stream_set_spib - sets the spib value of a stream
0440  * @bus: HD-audio core bus
0441  * @hext_stream: hdac_ext_stream
0442  * @value: spib value to set
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  * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream
0461  * @bus: HD-audio core bus
0462  * @hext_stream: hdac_ext_stream
0463  *
0464  * Return maxfifo for the stream
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  * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream
0481  * @bus: HD-audio core bus
0482  * @enable: flag to enable/disable DRSM
0483  * @index: stream index for which DRSM need to be enabled
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  * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream
0506  * @bus: HD-audio core bus
0507  * @hext_stream: hdac_ext_stream
0508  * @value: dpib value to set
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  * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream
0527  * @hext_stream: hdac_ext_stream
0528  * @value: lpib value to set
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);