The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/sound/fdt/audio_soc.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2019 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   23  *
   24  */
   25 
   26 #include <sys/cdefs.h>
   27 __FBSDID("$FreeBSD$");
   28 
   29 #include "opt_platform.h"
   30 
   31 #include <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/bus.h>
   34 #include <sys/clock.h>
   35 #include <sys/kernel.h>
   36 #include <sys/lock.h>
   37 #include <sys/module.h>
   38 #include <sys/endian.h>
   39 
   40 #include <dev/ofw/ofw_bus.h>
   41 #include <dev/ofw/ofw_bus_subr.h>
   42 
   43 #include <dev/sound/fdt/audio_dai.h>
   44 #include <dev/sound/pcm/sound.h>
   45 #include "audio_dai_if.h"
   46 
   47 #define AUDIO_BUFFER_SIZE       48000 * 4
   48 
   49 struct audio_soc_aux_node {
   50         SLIST_ENTRY(audio_soc_aux_node) link;
   51         device_t                        dev;
   52 };
   53 
   54 struct audio_soc_channel {
   55         struct audio_soc_softc  *sc;    /* parent device's softc */
   56         struct pcm_channel      *pcm;   /* PCM channel */
   57         struct snd_dbuf         *buf;   /* PCM buffer */
   58         int                     dir;    /* direction */
   59 };
   60 
   61 struct audio_soc_softc {
   62         /*
   63          * pcm_register assumes that sc is snddev_info,
   64          * so this has to be first structure member for "compatibility"
   65          */
   66         struct snddev_info      info;
   67         device_t                dev;
   68         char                    *name;
   69         struct intr_config_hook init_hook;
   70         device_t                cpu_dev;
   71         device_t                codec_dev;
   72         SLIST_HEAD(, audio_soc_aux_node)        aux_devs;
   73         unsigned int            mclk_fs;
   74         struct audio_soc_channel        play_channel;
   75         struct audio_soc_channel        rec_channel;
   76         /*
   77          * The format is from the CPU node, for CODEC node clock roles
   78          * need to be reversed.
   79          */
   80         uint32_t                format;
   81         uint32_t                link_mclk_fs;
   82 };
   83 
   84 static struct ofw_compat_data compat_data[] = {
   85         {"simple-audio-card",   1},
   86         {NULL,                  0},
   87 };
   88 
   89 static struct {
   90         const char *name;
   91         unsigned int fmt;
   92 } ausoc_dai_formats[] = {
   93         { "i2s",        AUDIO_DAI_FORMAT_I2S },
   94         { "right_j",    AUDIO_DAI_FORMAT_RJ },
   95         { "left_j",     AUDIO_DAI_FORMAT_LJ },
   96         { "dsp_a",      AUDIO_DAI_FORMAT_DSPA },
   97         { "dsp_b",      AUDIO_DAI_FORMAT_DSPB },
   98         { "ac97",       AUDIO_DAI_FORMAT_AC97 },
   99         { "pdm",        AUDIO_DAI_FORMAT_PDM },
  100 };
  101 
  102 static int      audio_soc_probe(device_t dev);
  103 static int      audio_soc_attach(device_t dev);
  104 static int      audio_soc_detach(device_t dev);
  105 
  106 /*
  107  * Invert master/slave roles for CODEC side of the node
  108  */
  109 static uint32_t
  110 audio_soc_reverse_clocks(uint32_t format)
  111 {
  112         int fmt, pol, clk;
  113 
  114         fmt = AUDIO_DAI_FORMAT_FORMAT(format);
  115         pol = AUDIO_DAI_FORMAT_POLARITY(format);
  116         clk = AUDIO_DAI_FORMAT_CLOCK(format);
  117 
  118         switch (clk) {
  119         case AUDIO_DAI_CLOCK_CBM_CFM:
  120                 clk = AUDIO_DAI_CLOCK_CBS_CFS;
  121                 break;
  122         case AUDIO_DAI_CLOCK_CBS_CFM:
  123                 clk = AUDIO_DAI_CLOCK_CBM_CFS;
  124                 break;
  125         case AUDIO_DAI_CLOCK_CBM_CFS:
  126                 clk = AUDIO_DAI_CLOCK_CBS_CFM;
  127                 break;
  128         case AUDIO_DAI_CLOCK_CBS_CFS:
  129                 clk = AUDIO_DAI_CLOCK_CBM_CFM;
  130                 break;
  131         }
  132 
  133         return AUDIO_DAI_FORMAT(fmt, pol, clk);
  134 }
  135 
  136 static uint32_t
  137 audio_soc_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksz)
  138 {
  139 
  140         return (blocksz);
  141 }
  142 
  143 static int
  144 audio_soc_chan_setformat(kobj_t obj, void *data, uint32_t format)
  145 {
  146 
  147         struct audio_soc_softc *sc;
  148         struct audio_soc_channel *ausoc_chan;
  149 
  150         ausoc_chan = data;
  151         sc = ausoc_chan->sc;
  152 
  153         return AUDIO_DAI_SET_CHANFORMAT(sc->cpu_dev, format);
  154 }
  155 
  156 static uint32_t
  157 audio_soc_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
  158 {
  159 
  160         struct audio_soc_softc *sc;
  161         struct audio_soc_channel *ausoc_chan;
  162         uint32_t rate;
  163         struct audio_soc_aux_node *aux_node;
  164 
  165         ausoc_chan = data;
  166         sc = ausoc_chan->sc;
  167 
  168         if (sc->link_mclk_fs) {
  169                 rate = speed * sc->link_mclk_fs;
  170                 if (AUDIO_DAI_SET_SYSCLK(sc->cpu_dev, rate, AUDIO_DAI_CLOCK_IN))
  171                         device_printf(sc->dev, "failed to set sysclk for CPU node\n");
  172 
  173                 if (AUDIO_DAI_SET_SYSCLK(sc->codec_dev, rate, AUDIO_DAI_CLOCK_OUT))
  174                         device_printf(sc->dev, "failed to set sysclk for codec node\n");
  175 
  176                 SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
  177                         if (AUDIO_DAI_SET_SYSCLK(aux_node->dev, rate, AUDIO_DAI_CLOCK_OUT))
  178                                 device_printf(sc->dev, "failed to set sysclk for aux node\n");
  179                 }
  180         }
  181 
  182         /*
  183          * Let CPU node determine speed
  184          */
  185         speed = AUDIO_DAI_SET_CHANSPEED(sc->cpu_dev, speed);
  186         AUDIO_DAI_SET_CHANSPEED(sc->codec_dev, speed);
  187         SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
  188                 AUDIO_DAI_SET_CHANSPEED(aux_node->dev, speed);
  189         }
  190 
  191         return (speed);
  192 }
  193 
  194 static uint32_t
  195 audio_soc_chan_getptr(kobj_t obj, void *data)
  196 {
  197         struct audio_soc_softc *sc;
  198         struct audio_soc_channel *ausoc_chan;
  199 
  200         ausoc_chan = data;
  201         sc = ausoc_chan->sc;
  202 
  203         return AUDIO_DAI_GET_PTR(sc->cpu_dev, ausoc_chan->dir);
  204 }
  205 
  206 static void *
  207 audio_soc_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
  208         struct pcm_channel *c, int dir)
  209 {
  210         struct audio_soc_channel *ausoc_chan;
  211         void *buffer;
  212 
  213         ausoc_chan = devinfo;
  214         buffer = malloc(AUDIO_BUFFER_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
  215 
  216         if (sndbuf_setup(b, buffer, AUDIO_BUFFER_SIZE) != 0) {
  217                 free(buffer, M_DEVBUF);
  218                 return NULL;
  219         }
  220 
  221         ausoc_chan->dir = dir;
  222         ausoc_chan->buf = b;
  223         ausoc_chan->pcm = c;
  224 
  225         return (devinfo);
  226 }
  227 
  228 static int
  229 audio_soc_chan_trigger(kobj_t obj, void *data, int go)
  230 {
  231         struct audio_soc_softc *sc;
  232         struct audio_soc_channel *ausoc_chan;
  233         struct audio_soc_aux_node *aux_node;
  234 
  235         ausoc_chan = (struct audio_soc_channel *)data;
  236         sc = ausoc_chan->sc;
  237         AUDIO_DAI_TRIGGER(sc->codec_dev, go, ausoc_chan->dir);
  238         SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
  239                 AUDIO_DAI_TRIGGER(aux_node->dev, go, ausoc_chan->dir);
  240         }
  241 
  242         return AUDIO_DAI_TRIGGER(sc->cpu_dev, go, ausoc_chan->dir);
  243 }
  244 
  245 static int
  246 audio_soc_chan_free(kobj_t obj, void *data)
  247 {
  248 
  249         struct audio_soc_channel *ausoc_chan;
  250         void *buffer;
  251 
  252         ausoc_chan = (struct audio_soc_channel *)data;
  253 
  254         buffer = sndbuf_getbuf(ausoc_chan->buf);
  255         if (buffer)
  256                 free(buffer, M_DEVBUF);
  257 
  258         return (0);
  259 }
  260 
  261 static struct pcmchan_caps *
  262 audio_soc_chan_getcaps(kobj_t obj, void *data)
  263 {
  264         struct audio_soc_softc *sc;
  265         struct audio_soc_channel *ausoc_chan;
  266 
  267         ausoc_chan = data;
  268         sc = ausoc_chan->sc;
  269 
  270         return AUDIO_DAI_GET_CAPS(sc->cpu_dev);
  271 }
  272 
  273 static kobj_method_t audio_soc_chan_methods[] = {
  274         KOBJMETHOD(channel_init,        audio_soc_chan_init),
  275         KOBJMETHOD(channel_free,        audio_soc_chan_free),
  276         KOBJMETHOD(channel_setformat,   audio_soc_chan_setformat),
  277         KOBJMETHOD(channel_setspeed,    audio_soc_chan_setspeed),
  278         KOBJMETHOD(channel_setblocksize,audio_soc_chan_setblocksize),
  279         KOBJMETHOD(channel_trigger,     audio_soc_chan_trigger),
  280         KOBJMETHOD(channel_getptr,      audio_soc_chan_getptr),
  281         KOBJMETHOD(channel_getcaps,     audio_soc_chan_getcaps),
  282         KOBJMETHOD_END
  283 };
  284 CHANNEL_DECLARE(audio_soc_chan);
  285 
  286 static void
  287 audio_soc_intr(void *arg)
  288 {
  289         struct audio_soc_softc *sc;
  290         int channel_intr_required;
  291 
  292         sc = (struct audio_soc_softc *)arg;
  293         channel_intr_required = AUDIO_DAI_INTR(sc->cpu_dev, sc->play_channel.buf, sc->rec_channel.buf);
  294         if (channel_intr_required & AUDIO_DAI_PLAY_INTR)
  295                 chn_intr(sc->play_channel.pcm);
  296         if (channel_intr_required & AUDIO_DAI_REC_INTR)
  297                 chn_intr(sc->rec_channel.pcm);
  298 }
  299 
  300 static int
  301 audio_soc_probe(device_t dev)
  302 {
  303 
  304         if (!ofw_bus_status_okay(dev))
  305                 return (ENXIO);
  306 
  307         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
  308                 device_set_desc(dev, "simple-audio-card");
  309                 return (BUS_PROBE_DEFAULT);
  310         }
  311 
  312         return (ENXIO);
  313 }
  314 
  315 static void
  316 audio_soc_init(void *arg)
  317 {
  318         struct audio_soc_softc *sc;
  319         phandle_t node, child;
  320         device_t daidev, auxdev;
  321         uint32_t xref;
  322         uint32_t *aux_devs;
  323         int ncells, i;
  324         struct audio_soc_aux_node *aux_node;
  325 
  326         sc = (struct audio_soc_softc *)arg;
  327         config_intrhook_disestablish(&sc->init_hook);
  328 
  329         node = ofw_bus_get_node(sc->dev);
  330         /* TODO: handle multi-link nodes */
  331         child = ofw_bus_find_child(node, "simple-audio-card,cpu");
  332         if (child == 0) {
  333                 device_printf(sc->dev, "cpu node is missing\n");
  334                 return;
  335         }
  336         if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) {
  337                 device_printf(sc->dev, "missing sound-dai property in cpu node\n");
  338                 return;
  339         }
  340         daidev = OF_device_from_xref(xref);
  341         if (daidev == NULL) {
  342                 device_printf(sc->dev, "no driver attached to cpu node\n");
  343                 return;
  344         }
  345         sc->cpu_dev = daidev;
  346 
  347         child = ofw_bus_find_child(node, "simple-audio-card,codec");
  348         if (child == 0) {
  349                 device_printf(sc->dev, "codec node is missing\n");
  350                 return;
  351         }
  352         if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) {
  353                 device_printf(sc->dev, "missing sound-dai property in codec node\n");
  354                 return;
  355         }
  356         daidev = OF_device_from_xref(xref);
  357         if (daidev == NULL) {
  358                 device_printf(sc->dev, "no driver attached to codec node\n");
  359                 return;
  360         }
  361         sc->codec_dev = daidev;
  362 
  363         /* Add AUX devices */
  364         aux_devs = NULL;
  365         ncells = OF_getencprop_alloc_multi(node, "simple-audio-card,aux-devs", sizeof(*aux_devs),
  366             (void **)&aux_devs);
  367 
  368         for (i = 0; i < ncells; i++) {
  369                 auxdev = OF_device_from_xref(aux_devs[i]);
  370                 if (auxdev == NULL)
  371                         device_printf(sc->dev, "warning: no driver attached to aux node\n");
  372                 aux_node = (struct audio_soc_aux_node *)malloc(sizeof(*aux_node), M_DEVBUF, M_NOWAIT);
  373                 if (aux_node == NULL) {
  374                         device_printf(sc->dev, "failed to allocate aux node struct\n");
  375                         return;
  376                 }
  377                 aux_node->dev = auxdev;
  378                 SLIST_INSERT_HEAD(&sc->aux_devs, aux_node, link);
  379         }
  380 
  381         if (aux_devs)
  382                 OF_prop_free(aux_devs);
  383 
  384         if (AUDIO_DAI_INIT(sc->cpu_dev, sc->format)) {
  385                 device_printf(sc->dev, "failed to initialize cpu node\n");
  386                 return;
  387         }
  388 
  389         /* Reverse clock roles for CODEC */
  390         if (AUDIO_DAI_INIT(sc->codec_dev, audio_soc_reverse_clocks(sc->format))) {
  391                 device_printf(sc->dev, "failed to initialize codec node\n");
  392                 return;
  393         }
  394 
  395         SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
  396                 if (AUDIO_DAI_INIT(aux_node->dev, audio_soc_reverse_clocks(sc->format))) {
  397                         device_printf(sc->dev, "failed to initialize aux node\n");
  398                         return;
  399                 }
  400         }
  401 
  402         if (pcm_register(sc->dev, sc, 1, 1)) {
  403                 device_printf(sc->dev, "failed to register PCM\n");
  404                 return;
  405         }
  406 
  407         sc->play_channel.sc = sc;
  408         sc->rec_channel.sc = sc;
  409 
  410         pcm_addchan(sc->dev, PCMDIR_PLAY, &audio_soc_chan_class, &sc->play_channel);
  411         pcm_addchan(sc->dev, PCMDIR_REC, &audio_soc_chan_class, &sc->rec_channel);
  412 
  413         pcm_setstatus(sc->dev, "at EXPERIMENT");
  414 
  415         AUDIO_DAI_SETUP_INTR(sc->cpu_dev, audio_soc_intr, sc);
  416         AUDIO_DAI_SETUP_MIXER(sc->codec_dev, sc->dev);
  417         SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
  418                 AUDIO_DAI_SETUP_MIXER(aux_node->dev, sc->dev);
  419         }
  420 }
  421 
  422 static int
  423 audio_soc_attach(device_t dev)
  424 {
  425         struct audio_soc_softc *sc;
  426         char *name;
  427         phandle_t node, cpu_child;
  428         uint32_t xref;
  429         int i, ret;
  430         char tmp[32];
  431         unsigned int fmt, pol, clk;
  432         bool frame_master, bitclock_master;
  433 
  434         sc = device_get_softc(dev);
  435         sc->dev = dev;
  436         node = ofw_bus_get_node(dev);
  437 
  438         ret = OF_getprop_alloc(node, "name", (void **)&name);
  439         if (ret == -1)
  440                 name = "SoC audio";
  441 
  442         sc->name = strdup(name, M_DEVBUF);
  443         device_set_desc(dev, sc->name);
  444 
  445         if (ret != -1)
  446                 OF_prop_free(name);
  447 
  448         SLIST_INIT(&sc->aux_devs);
  449 
  450         ret = OF_getprop(node, "simple-audio-card,format", tmp, sizeof(tmp));
  451         if (ret == 0) {
  452                 for (i = 0; i < nitems(ausoc_dai_formats); i++) {
  453                         if (strcmp(tmp, ausoc_dai_formats[i].name) == 0) {
  454                                 fmt = ausoc_dai_formats[i].fmt;
  455                                 break;
  456                         }
  457                 }
  458                 if (i == nitems(ausoc_dai_formats))
  459                         return (EINVAL);
  460         } else
  461                 fmt = AUDIO_DAI_FORMAT_I2S;
  462 
  463         if (OF_getencprop(node, "simple-audio-card,mclk-fs",
  464             &sc->link_mclk_fs, sizeof(sc->link_mclk_fs)) <= 0)
  465                 sc->link_mclk_fs = 0;
  466 
  467         /* Unless specified otherwise, CPU node is the master */
  468         frame_master = bitclock_master = true;
  469 
  470         cpu_child = ofw_bus_find_child(node, "simple-audio-card,cpu");
  471 
  472         if ((OF_getencprop(node, "simple-audio-card,frame-master", &xref, sizeof(xref))) > 0)
  473                 frame_master = cpu_child == OF_node_from_xref(xref);
  474 
  475         if ((OF_getencprop(node, "simple-audio-card,bitclock-master", &xref, sizeof(xref))) > 0)
  476                 bitclock_master = cpu_child == OF_node_from_xref(xref);
  477 
  478         if (frame_master) {
  479                 clk = bitclock_master ?
  480                     AUDIO_DAI_CLOCK_CBM_CFM : AUDIO_DAI_CLOCK_CBS_CFM;
  481         } else {
  482                 clk = bitclock_master ?
  483                     AUDIO_DAI_CLOCK_CBM_CFS : AUDIO_DAI_CLOCK_CBS_CFS;
  484         }
  485 
  486         bool bitclock_inversion = OF_hasprop(node, "simple-audio-card,bitclock-inversion");
  487         bool frame_inversion = OF_hasprop(node, "simple-audio-card,frame-inversion");
  488         if (bitclock_inversion) {
  489                 pol = frame_inversion ?
  490                     AUDIO_DAI_POLARITY_IB_IF : AUDIO_DAI_POLARITY_IB_NF;
  491         } else {
  492                 pol = frame_inversion ?
  493                     AUDIO_DAI_POLARITY_NB_IF : AUDIO_DAI_POLARITY_NB_NF;
  494         }
  495 
  496         sc->format = AUDIO_DAI_FORMAT(fmt, pol, clk);
  497 
  498         sc->init_hook.ich_func = audio_soc_init;
  499         sc->init_hook.ich_arg = sc;
  500         if (config_intrhook_establish(&sc->init_hook) != 0)
  501                 return (ENOMEM);
  502 
  503         return (0);
  504 }
  505 
  506 static int
  507 audio_soc_detach(device_t dev)
  508 {
  509         struct audio_soc_softc *sc;
  510         struct audio_soc_aux_node *aux;
  511 
  512         sc = device_get_softc(dev);
  513         if (sc->name)
  514                 free(sc->name, M_DEVBUF);
  515 
  516         while ((aux = SLIST_FIRST(&sc->aux_devs)) != NULL) {
  517                 SLIST_REMOVE_HEAD(&sc->aux_devs, link);
  518                 free(aux, M_DEVBUF);
  519         }
  520 
  521         return (0);
  522 }
  523 
  524 static device_method_t audio_soc_methods[] = {
  525         /* device_if methods */
  526         DEVMETHOD(device_probe,         audio_soc_probe),
  527         DEVMETHOD(device_attach,        audio_soc_attach),
  528         DEVMETHOD(device_detach,        audio_soc_detach),
  529 
  530         DEVMETHOD_END,
  531 };
  532 
  533 static driver_t audio_soc_driver = {
  534         "pcm",
  535         audio_soc_methods,
  536         sizeof(struct audio_soc_softc),
  537 };
  538 
  539 DRIVER_MODULE(audio_soc, simplebus, audio_soc_driver, NULL, NULL);
  540 MODULE_VERSION(audio_soc, 1);

Cache object: d401d4fe61bfeef4efc10f48bb11a076


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.